Skip to content

Instantly share code, notes, and snippets.

@stanislaw
Created July 29, 2012 14:35
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save stanislaw/3199258 to your computer and use it in GitHub Desktop.
Save stanislaw/3199258 to your computer and use it in GitHub Desktop.
Ruby-vips vs Oil
#!/usr/bin/env ruby
require 'rubygems'
gem 'ruby-vips'
gem 'oil'
require 'vips'
require 'oil'
require 'benchmark'
require 'stringio'
module Procedure
NUMBER = 1
IM_SIZE = 100
class << self
def run processor, img, best_of
result = nil
result = Benchmark.bmbm do |b|
(1 .. best_of).each do |number|
b.report number.to_s do
NUMBER.times do
send processor, img
end
end
end
end
output result
end
def output result
result = (result.map(&:to_a).map{|el| el[5]}.min * 1000).to_i
"#{result}ms"
end
class VipsProcessor
HIGH_QUALITY = false
SHARPEN_MASK = begin
conv_mask = [
[ -1, -1, -1 ],
[ -1, 24, -1 ],
[ -1, -1, -1 ]
]
::VIPS::Mask.new conv_mask, 16
end
attr_reader :src, :image
def initialize src
@src = src
resize_to_limit IM_SIZE, IM_SIZE
end
def resize_to_limit(new_width, new_height)
manipulate! do |image|
image = resize_image(image,new_width,new_height) if new_width < image.x_size || new_height < image.y_size
image
end
end
def manipulate!
@image ||= VIPS::Image.new(src)
@image = yield @image
rescue => e
raise("Failed to manipulate file, maybe it is not an image? Original Error: #{e}")
end
def resize_image(image, width, height, min_or_max = :min)
ratio = get_ratio image, width, height, min_or_max
if jpeg? # find the shrink ratio for loading
shrink_factor = [8, 4, 2, 1].find {|sf| 1.0 / ratio >= sf }
shrink_factor = 1 if shrink_factor == nil
image = VIPS::Image.jpeg src,
:shrink_factor => shrink_factor, :sequential => true
ratio = get_ratio image, width, height, min_or_max
elsif png?
image = VIPS::Image.png src, :sequential => true
end
if ratio > 1
image = image.affinei_resize :nearest, ratio
else
if HIGH_QUALITY
if ratio <= 0.5
factor = (1.0 / ratio).floor
puts "int shrink by factor = #{factor}"
image = image.shrink(factor)
image = image.tile_cache(image.x_size, 1, 10)
ratio = get_ratio image, width, height, min_or_max
end
image = image.affinei_resize :bilinear, ratio
image = image.conv SHARPEN_MASK
else
image = image.affinei_resize :nearest, ratio
end
end
image
end
def get_ratio(image, width,height, min_or_max = :min)
width_ratio = width.to_f / image.x_size
height_ratio = height.to_f / image.y_size
[width_ratio, height_ratio].send(min_or_max)
end
def jpeg?(path = src)
path =~ /.*jpg$/i or path =~ /.*jpeg$/i
end
def png?(path = src)
path =~ /.*png$/i
end
end
def vips_jpg src
processor = VipsProcessor.new(src)
output = processor.resize_to_limit IM_SIZE, IM_SIZE
output.jpeg("vips.#{src}")
end
def vips_png src
processor = VipsProcessor.new(src)
output = processor.resize_to_limit IM_SIZE, IM_SIZE
output.png("vips.#{src}")
end
def oil_jpg src
jpeg = Oil::JPEG.new(im(src), IM_SIZE, IM_SIZE)
File.open("oil.#{src}", 'wb') do |f|
jpeg.each { |data| f << data }
end
end
def oil_png src
png = Oil::PNG.new(im(src), IM_SIZE, IM_SIZE)
File.open("oil.#{src}", 'wb') do |f|
png.each { |data| f << data }
end
end
def im src
File.open src, 'rb'
end
end
end
best_of = 1
#result_vips_jpg = Procedure.run :vips_jpg, "peacock.jpg", best_of
result_vips_jpg = Procedure.run :vips_jpg, "wtc.jpg", best_of
result_vips_png = Procedure.run :vips_png, "peacock.png", best_of
#result_oil_jpg = Procedure.run :oil_jpg, "peacock.jpg", best_of
result_oil_jpg = Procedure.run :oil_jpg, "wtc.jpg", best_of
result_oil_png = Procedure.run :oil_png, "peacock.png", best_of
puts "Ruby-vips #{VIPS::VERSION} built against libvips #{VIPS::LIB_VERSION}"
print "ruby-vips, jpeg image: "
puts result_vips_jpg
print "ruby-vips, png image: "
puts result_vips_png
print "oil, jpeg image: "
puts result_oil_jpg
print "oil, png image: "
puts result_oil_png
@stanislaw
Copy link
Author

John, here is my first result:

Ruby-vips 0.3.0 built against libvips 7.30.0-Sun Jul 22 22:44:12 EEST 2012
ruby-vips, jpeg image: 62ms
ruby-vips, png image: 911ms
oil, jpeg image: 34ms
oil, png image: 361ms

Png sequential mode support is commented because of a bug - see next comment below.

Subscribing you to this gist's updates: @jcupitt.

@stanislaw
Copy link
Author

If I uncomment :sequential => true, I get the following on Mac OS X Lion:

Stanislaws-MacBook-Air:ruby-vips-vs-oil Stanislaw$ ./ruby-vips-vs-oil.rb 
Rehearsal -------------------------------------
1   0.060000   0.010000   0.070000 (  0.076529)
2   0.070000   0.010000   0.080000 (  0.066369)
3   0.050000   0.010000   0.060000 (  0.063638)
---------------------------- total: 0.210000sec

        user     system      total        real
1   0.060000   0.000000   0.060000 (  0.062379)
2   0.060000   0.010000   0.070000 (  0.062331)
3   0.060000   0.010000   0.070000 (  0.061656)
Rehearsal -------------------------------------
1 /Users/Stanislaw/.rvm/gems/ruby-1.9.3-p194/gems/ruby-vips-0.3.0/lib/vips/writer.rb:66:in `write_internal': VIPS error: vips_image_get: field "icc-profile-data" not found (VIPS::Error)
VipsSequential: non-sequential read --- at position 3808 in file, but position 0 requested
vips2png: unable to write "vips.peacock.png"
    from /Users/Stanislaw/.rvm/gems/ruby-1.9.3-p194/gems/ruby-vips-0.3.0/lib/vips/writer.rb:66:in `write'
    from /Users/Stanislaw/.rvm/gems/ruby-1.9.3-p194/gems/ruby-vips-0.3.0/lib/vips/writer.rb:269:in `invoke_writer'
    from /Users/Stanislaw/.rvm/gems/ruby-1.9.3-p194/gems/ruby-vips-0.3.0/lib/vips/writer.rb:245:in `png'
    from ./ruby-vips-vs-oil.rb:129:in `vips_png'
    from ./ruby-vips-vs-oil.rb:30:in `block (4 levels) in run'
    from ./ruby-vips-vs-oil.rb:29:in `times'
    from ./ruby-vips-vs-oil.rb:29:in `block (3 levels) in run'
    from /Users/Stanislaw/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/benchmark.rb:280:in `measure'
    from /Users/Stanislaw/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/benchmark.rb:257:in `block in bmbm'
    from /Users/Stanislaw/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/benchmark.rb:255:in `each'
    from /Users/Stanislaw/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/benchmark.rb:255:in `inject'
    from /Users/Stanislaw/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/benchmark.rb:255:in `bmbm'
    from ./ruby-vips-vs-oil.rb:26:in `run'
    from ./ruby-vips-vs-oil.rb:157:in `<main>'

@stanislaw
Copy link
Author

Gists act like a common git repositories.

You need to fork this gist and run (replace with your numbers)

git clone git://gist.github.com/3199258.git ruby-vips-vs-oil
cd ruby-vips-vs-oil # It is just a common git repo.

@stanislaw
Copy link
Author

I was running fast, so It could be a mistake somewhere, please check the overall code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment