Question

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).

Was it helpful?

Solution

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.

OTHER TIPS

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]

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')
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top