Is there a simple way to get image dimensions in Ruby? - ruby

I'm looking for an easy way to get width and height dimensions for image files in Ruby without having to use ImageMagick or ImageScience (running Snow Leapard).

As of June 2012, FastImage which "finds the size or type of an image given its uri by fetching as little as needed" is a good option. It works with local images and those on remote servers.
An IRB example from the readme:
require 'fastimage'
FastImage.size("http://stephensykes.com/images/ss.com_x.gif")
=> [266, 56] # width, height
Standard array assignment in a script:
require 'fastimage'
size_array = FastImage.size("http://stephensykes.com/images/ss.com_x.gif")
puts "Width: #{size_array[0]}"
puts "Height: #{size_array[1]}"
Or, using multiple assignment in a script:
require 'fastimage'
width, height = FastImage.size("http://stephensykes.com/images/ss.com_x.gif")
puts "Width: #{width}"
puts "Height: #{height}"

You could try these (untested):
http://snippets.dzone.com/posts/show/805
PNG:
IO.read('image.png')[0x10..0x18].unpack('NN')
=> [713, 54]
GIF:
IO.read('image.gif')[6..10].unpack('SS')
=> [130, 50]
BMP:
d = IO.read('image.bmp')[14..28]
d[0] == 40 ? d[4..-1].unpack('LL') : d[4..8].unpack('SS')
JPG:
class JPEG
attr_reader :width, :height, :bits
def initialize(file)
if file.kind_of? IO
examine(file)
else
File.open(file, 'rb') { |io| examine(io) }
end
end
private
def examine(io)
raise 'malformed JPEG' unless io.getc == 0xFF && io.getc == 0xD8 # SOI
class << io
def readint; (readchar << 8) + readchar; end
def readframe; read(readint - 2); end
def readsof; [readint, readchar, readint, readint, readchar]; end
def next
c = readchar while c != 0xFF
c = readchar while c == 0xFF
c
end
end
while marker = io.next
case marker
when 0xC0..0xC3, 0xC5..0xC7, 0xC9..0xCB, 0xCD..0xCF # SOF markers
length, #bits, #height, #width, components = io.readsof
raise 'malformed JPEG' unless length == 8 + components * 3
when 0xD9, 0xDA: break # EOI, SOS
when 0xFE: #comment = io.readframe # COM
when 0xE1: io.readframe # APP1, contains EXIF tag
else io.readframe # ignore frame
end
end
end
end

There's also a new (July 2011) library that wasn't around at the time the question was originally asked: the Dimensions rubygem (which seems to be authored by the same Sam Stephenson responsible for the byte-manipulation techniques also suggested here.)
Below code sample from project's README
require 'dimensions'
Dimensions.dimensions("upload_bird.jpg") # => [300, 225]
Dimensions.width("upload_bird.jpg") # => 300
Dimensions.height("upload_bird.jpg") # => 225

There's a handy method in the paperclip gem:
>> Paperclip::Geometry.from_file("/path/to/image.jpg")
=> 180x180
This only works if identify is installed. If it isn't, if PHP is installed, you could do something like this:
system(%{php -r '$w = getimagesize("#{path}"); echo("${w[0]}x${w[1]}");'})
# eg returns "200x100" (width x height)

I have finally found a nice quick way to get dimensions of an image. You should use MiniMagick.
require 'mini_magick'
image = MiniMagick::Image.open('http://www.thetvdb.com/banners/fanart/original/81189-43.jpg')
assert_equal 1920, image[:width]
assert_equal 1080, image[:height]

libimage-size is a Ruby library for calculating image sizes for a wide variety of graphical formats. A gem is available, or you can download the source tarball and extract the image_size.rb file.

Here's a version of the JPEG class from ChristopheD's answer that works in both Ruby 1.8.7 and Ruby 1.9. This allows you to get the width and height of a JPEG (.jpg) image file by looking directly at the bits. (Alternatively, just use the Dimensions gem, as suggested in another answer.)
class JPEG
attr_reader :width, :height, :bits
def initialize(file)
if file.kind_of? IO
examine(file)
else
File.open(file, 'rb') { |io| examine(io) }
end
end
private
def examine(io)
if RUBY_VERSION >= "1.9"
class << io
def getc; super.bytes.first; end
def readchar; super.bytes.first; end
end
end
class << io
def readint; (readchar << 8) + readchar; end
def readframe; read(readint - 2); end
def readsof; [readint, readchar, readint, readint, readchar]; end
def next
c = readchar while c != 0xFF
c = readchar while c == 0xFF
c
end
end
raise 'malformed JPEG' unless io.getc == 0xFF && io.getc == 0xD8 # SOI
while marker = io.next
case marker
when 0xC0..0xC3, 0xC5..0xC7, 0xC9..0xCB, 0xCD..0xCF # SOF markers
length, #bits, #height, #width, components = io.readsof
raise 'malformed JPEG' unless length == 8 + components * 3
# colons not allowed in 1.9, change to "then"
when 0xD9, 0xDA then break # EOI, SOS
when 0xFE then #comment = io.readframe # COM
when 0xE1 then io.readframe # APP1, contains EXIF tag
else io.readframe # ignore frame
end
end
end
end

For PNGs I got this modified version of ChristopeD's method to work.
File.binread(path, 64)[0x10..0x18].unpack('NN')

Related

Bounding box across multiple pages with Prawn PDF

I'm trying to create a document with Prawn Ruby PDF generator, but I'm facing the following problem:
The image below shows what structure I'm trying to do.
And this is the example code that tries to mimics my real scenario with the way that I'm trying to achieve this. The 2.times and (50.times.map { |i| i.to_s }.join("\n")) mimics dynamic data.
require 'prawn'
class MyPdf
def self.to_pdf(*args)
new(*args).to_pdf
end
def to_pdf
pdf.move_down 200
2.times do
pdf.bounding_box(
[0, pdf.cursor],
width: pdf.bounds.width
) do
pdf.text (50.times.map { |i| i.to_s }.join("\n"))
pdf.stroke_bounds
end
end
pdf
end
def pdf
#pdf ||= Prawn::Document.new(page_size: 'A4')
end
end
But I'm having a lot of trouble with the dynamic bounding box placing.
Do you people know a way to achieve this with or without bounding boxes?
You may be looking for span:
def to_pdf
pdf.move_down 200
2.times do
pdf.span(pdf.bounds.width) do
pdf.text (50.times.map { |i| i.to_s }.join("\n"))
pdf.stroke_bounds
end
end
end
I failed to do that with boundig boxes. May be this is not the best solution, but you can do this with tables :
data = []
500.times do |i|
data.push [i.to_s]
end
table(data, width: bounds.width) do |t|
t.cells.border_width = 0 # We don't want to see a table
t.before_rendering_page do |page|
page.row(0).border_top_width = 1
page.row(-1).border_bottom_width = 1
page.column(0).border_left_width = 1
page.column(-1).border_right_width = 1
end
end
source : http://prawnpdf.org/prawn-table-manual.pdf (page 17)
Check your margins to have the continuation on the top of the next page

Ruby GTK Pixbuf timed image change

I am creating an image slideshow in ruby, using gtk pixbuf to load images. I am very new to ruby & GTK, this may be an stupid question.
Currently image changes are linked to the GUI button_press_event, I would like them to change /refresh automatically based on a set time, like a slideshow or animation. I saw the gtk animation using a gif method, but I would like to use individual jpeg files inline sequence, so that I can set the time to show a slide. Once the loop has gone through all the images, the GUI should display buttons for replay or quit. ( I haven't used #time yet, it is just there for possibilities ) Thanks for any suggestions;
require 'gtk2'
class Pics
attr_accessor :pile, :picindex, :imgLoaded, :image, :box, :window, :time
def initialize
#window = Gtk::Window.new()
#window.signal_connect("destroy"){Gtk.main_quit}
pic1 = "1.jpg"
pic2 = "2.jpg"
pic3 = "3.jpg"
pic4 = "4.jpg"
#pile = [pic1, pic2, pic3, pic4]
#picindex = 0
self.getImage
#box = Gtk::EventBox.new.add(#image)
#time = true
end
def nuImage
#box.remove(#image)
#picindex = #picindex + 1
#picindex = 0 if #picindex == #pile.length
self.getImage
#box.add(#image)
#box.show
end
def getImage
#imgLoaded = #pile[#picindex]
img = Gdk::Pixbuf.new(#imgLoaded, 556, 900)
#image = Gtk::Image.new(img)
#image.show
end
end # class Pics
pics = Pics.new
pics.box.signal_connect("button_press_event"){pics.nuImage}
pics.window.set_default_size(556, 900)
pics.window.add(pics.box)
pics.window.show_all
Gtk.main
use GLib.timeout_add () or GLib.timeout_add_seconds (). Return False if you don't want to use it anymore.read GLib documentation, Section: Main Event Loop
This is a solution:
def start_button__clicked(but)
#thread = Thread.new {
loop do
next_button__clicked
sleep(2)
end
end
def stop_button__clicked(but)
#thread.kill
end
This is how I would do it in visual ruby. Its basically the same.
You'd just have a form with a button named "start_button" and "stop_button" etc.
the following code is an implementation:
GLib::Timeout.add(1000) do
pics.nuImage if pics.time
true
end
pics.window.signal_connect("key_press_event") do |_window, event|
case event.keyval
when Gdk::Keyval::GDK_KEY_space
pics.time = !pics.time
end
end
more details:
http://ruby-gnome2.sourceforge.jp/hiki.cgi?GLib%3A%3ATimeout

How do I read a CSV file?

I have problems reading a CSV file with two columns separated by "\tab".
My code is:
require 'csv'
require 'rubygems'
# Globals
INFINITY = 1.0/0
if __FILE__ == $0
# Locals
data = []
fn = ''
# Argument check
if ARGV.length == 1
fn = ARGV[0]
else
puts 'Usage: kmeans.rb INPUT-FILE'
exit
end
# Get all data
CSV.foreach(fn) do |row|
x = row[0].to_f
y = row[1].to_f
p = Point.new(x,y)
data.push p
end
# Determine the number of clusters to find
puts 'Number of clusters to find:'
k = STDIN.gets.chomp!.to_i
# Run algorithm on data
clusters = kmeans(data, k)
# Graph output by running gnuplot pipe
Gnuplot.open do |gp|
# Start a new plot
Gnuplot::Plot.new(gp) do |plot|
plot.title fn
# Plot each cluster's points
clusters.each do |cluster|
# Collect all x and y coords for this cluster
x = cluster.points.collect {|p| p.x }
y = cluster.points.collect {|p| p.y }
# Plot w/o a title (clutters things up)
plot.data << Gnuplot::DataSet.new([x,y]) do |ds|
ds.notitle
end
end
end
end
end
The file is:
48.2641334571 86.4516903905
0.1140042627 35.8368597414
97.4319168245 92.8009240744
24.4614031388 18.3292584382
36.2367675367 32.8294024271
75.5836860736 68.30729977
38.6577034445 25.7701728584
28.2607136287 64.4493377817
61.5358486771 61.2195232194
I'm getting this error:
test.csv:1: syntax error, unexpected ',', expecting $end
48.2641334571,86.4516903905
^
You are just missing an end at the bottom. Your very first if is not closed.
CSV are "Comma-Separated Values". Yours are using tabs. This is not a big problem, because the CSV class can handle it, you just need to specify that your separator is a tab:
CSV.foreach(fn, { :col_sep => "\t" })
Be sure to double-check your file that it is using tabs, not spaces which are not the same.
I'm still confused about the error message, is this everything you received?

JRuby require error on .so file

I am working on a feature for an application that has to run on JRuby. Out of habit I develop on 'Native' Ruby. When I try to run any methods from this class in JRuby I get error messages like:
MissingSourceFile: no such file to load -- serialport
I think the problem lies in in the require 'serialport.so'. Is there a way to make this work in JRuby? Is there another way to require a .so file? Or a gem that can work with JRuby to provide the SerialPort class? I tried installing the ruby-serialport gem but that doesn't seem to install properly (Windows nmake issues).
Here is what my class file looks like:
require 'serialport.so'
class AlphaDisplay
#STATES
SOLID = "b"
ROTATE = "a"
BLINK = "c"
#COLORS
RED = "1"
GREEN = "2"
YELLOW = "3"
ORANGE = "7"
def self.message(address = 00, text = "ICS", color = AlphaDisplay::GREEN, state = AlphaDisplay::SOLID)
address = address.to_s
if address.length == 1
address = "0#{address}"
end
string = 1.chr + 90.chr + address + 2.chr + 65.chr + 65.chr + 27.chr + 26.chr + state + 28.chr + color + text + 4.chr
return string
end
def self.test(address = 00, text = "ICS", color = AlphaDisplay::GREEN, state = AlphaDisplay::SOLID)
sp = SerialPort.new(0, 9600, 8, 1, SerialPort::NONE)
sp.write(message(address,text,color,state))
sp.close
end
end
def SerialPort::new(port, *params)
sp = create(port)
begin
sp.set_modem_params(*params)
rescue
sp.close
raise
end
return sp
end
def SerialPort::open(port, *params)
sp = create(port)
begin
sp.set_modem_params(*params)
if (block_given?)
yield sp
sp.close
return nil
end
rescue
sp.close
raise
end
return sp
end
As far as I know, JRuby doesn't provide any native emulation layer, just the Ruby interpreter in Java. The problem is that ".so" files are UNIX specific and cannot run on Windows without some sort of emulation/translation.
You're better off avoiding Ruby libraries which require native extensions if you're going to use JRuby.

streaming html from webrick?

Has anyone tried streaming html/text/content from webrick? I've tried assigning an IO to the response body, but webrick is waiting for the stream to be closed first.
found this link by accident (http://redmine.ruby-lang.org/attachments/download/161) which contains webrick patch
# Copyright (C) 2008 Brian Candler, released under Ruby Licence.
#
# A collection of small monkey-patches to webrick.
require 'webrick'
module WEBrick
class HTTPRequest
# Generate HTTP/1.1 100 continue response. See
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/18459
def continue
if self['expect'] == '100-continue' && #config[:HTTPVersion] >= "1.1"
#socket.write "HTTP/#{#config[:HTTPVersion]} 100 continue\r\n\r\n"
#header.delete('expect')
end
end
end
class HTTPResponse
alias :orig_setup_header :setup_header
# Correct termination of streamed HTTP/1.1 responses. See
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/18454 and
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/18565
def setup_header
orig_setup_header
unless chunked? || #header['content-length']
#header['connection'] = "close"
#keep_alive = false
end
end
# Allow streaming of zipfile entry. See
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/18460
def send_body(socket)
if #body.respond_to?(:read) then send_body_io(socket)
elsif #body.respond_to?(:call) then send_body_proc(socket)
else send_body_string(socket)
end
end
# If the response body is a proc, then we invoke it and pass in
# an object which supports "write" and "<<" methods. This allows
# arbitary output streaming.
def send_body_proc(socket)
if #request_method == "HEAD"
# do nothing
elsif chunked?
#body.call(ChunkedWrapper.new(socket, self))
_write_data(socket, "0#{CRLF}#{CRLF}")
else
size = #header['content-length'].to_i
#body.call(socket) # TODO: StreamWrapper which supports offset, size
#sent_size = size
end
end
class ChunkedWrapper
def initialize(socket, resp)
#socket = socket
#resp = resp
end
def write(buf)
return if buf.empty?
data = ""
data << format("%x", buf.size) << CRLF
data << buf << CRLF
socket = #socket
#resp.instance_eval {
_write_data(socket, data)
#sent_size += buf.size
}
end
alias :<< :write
end
end
end
if RUBY_VERSION < "1.9"
old_verbose, $VERBOSE = $VERBOSE, nil
# Increase from default of 4K for efficiency, similar to
# http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8/lib/net/protocol.rb?r1=11708&r2=12092
# In trunk the default is 64K and can be adjusted using :InputBufferSize,
# :OutputBufferSize
WEBrick::HTTPRequest::BUFSIZE = 16384
WEBrick::HTTPResponse::BUFSIZE = 16384
$VERBOSE = old_verbose
end
to use simply pass a proc to as the response body, like so
res.body = proc { |w|
10.times do
w << Time.now.to_s
sleep(1)
end
}
woot!
I would suggest against using WEBrick for anything really, it's junk. I would say try Mongrel.
I know that wasn't your question, it's just some friendly advice.

Resources