update to use minimagick instead, and modernize the gem a bit. didn't touch the rails parts, just the zoomifier code

This commit is contained in:
John Bintz 2013-04-11 18:09:30 -04:00
parent 0b40e0019d
commit 66f550139b
5 changed files with 39 additions and 25 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
.DS_Store .DS_Store
*.swp *.swp
Gemfile.lock

3
Gemfile Normal file
View File

@ -0,0 +1,3 @@
source 'https://rubygems.org'
gemspec

View File

@ -1,7 +1,7 @@
require 'fileutils' require 'fileutils'
require 'open-uri' require 'open-uri'
require 'rubygems' require 'rubygems'
require 'rmagick' require 'mini_magick'
# Breaks up images into tiles suitable for viewing with Zoomify. # Breaks up images into tiles suitable for viewing with Zoomify.
# See http://zoomify.com/ for more details. # See http://zoomify.com/ for more details.
@ -42,17 +42,23 @@ module Zoomifier
tmpdir = "#{outputdir}/tmp" tmpdir = "#{outputdir}/tmp"
Dir.mkdir(tmpdir) Dir.mkdir(tmpdir)
tilesdir = nil tilesdir = nil
image = Magick::Image.read(filename).first.strip! original_image = MiniMagick::Image.open(filename)
# Each level of zoom is a factor of 2. Here we obtain the number of zooms # Each level of zoom is a factor of 2. Here we obtain the number of zooms
# allowed by the original file dimensions and the constant tile size. # allowed by the original file dimensions and the constant tile size.
levels = (Math.log([image.rows, image.columns].max.to_f / TILESIZE) / Math.log(2)).ceil levels = (Math.log([original_image['height'], original_image['width']].max.to_f / TILESIZE) / Math.log(2)).ceil
tiles = 0 tiles = 0
(0..levels).each do |level| (0..levels).each do |level|
n = levels - level n = levels - level
# Obtain the image to tile for this level. The 0th level should consist # Obtain the image to tile for this level. The 0th level should consist
# of one tile, while the highest level should be the original image. # of one tile, while the highest level should be the original image.
level_image = image.resize(image.columns >> n, image.rows >> n)
tiles(tmpdir, level, level_image) do |filename| create_image = proc {
image = MiniMagick::Image.open(filename)
image.resize("#{image['width'] >> n}x#{image['height'] >> n}")
image
}
tiles(tmpdir, level, create_image) do |filename|
# The tile images are chunked into directories named TileGroupN, N # The tile images are chunked into directories named TileGroupN, N
# starting at 0 and increasing monotonically. Each directory contains # starting at 0 and increasing monotonically. Each directory contains
# at most 256 images. The images are sorted by level, tile row, and # at most 256 images. The images are sorted by level, tile row, and
@ -66,34 +72,36 @@ module Zoomifier
tiles += 1 tiles += 1
end end
# Rmagick needs a bit of help freeing image memory. # Rmagick needs a bit of help freeing image memory.
level_image = nil
GC.start GC.start
end end
File.open("#{outputdir}/ImageProperties.xml", 'w') do |f| File.open("#{outputdir}/ImageProperties.xml", 'w') do |f|
f.write("<IMAGE_PROPERTIES WIDTH=\"#{image.columns}\" HEIGHT=\"#{image.rows}\" NUMTILES=\"#{tiles}\" NUMIMAGES=\"1\" VERSION=\"1.8\" TILESIZE=\"#{TILESIZE}\" />") f.write("<IMAGE_PROPERTIES WIDTH=\"#{original_image['width']}\" HEIGHT=\"#{original_image['height']}\" NUMTILES=\"#{tiles}\" NUMIMAGES=\"1\" VERSION=\"1.8\" TILESIZE=\"#{TILESIZE}\" />")
end end
Dir.rmdir(tmpdir) FileUtils.rm_rf(tmpdir)
outputdir outputdir
end end
# Splits the given image up into images of TILESIZE, writes them to the # Splits the given image up into images of TILESIZE, writes them to the
# given directory, and yields their names # given directory, and yields their names
def self.tiles(dir, level, image) def self.tiles(dir, level, image)
slice(image.rows).each_with_index do |y_slice, j| my_image = image.()
slice(image.columns).each_with_index do |x_slice, i|
original_width = my_image['width']
original_height = my_image['height']
my_image.write "#{dir}/_tmp.jpg"
slice(original_height).each_with_index do |y_slice, j|
slice(original_width).each_with_index do |x_slice, i|
# The images are named "level-column-row.jpg" # The images are named "level-column-row.jpg"
filename = "#{level}-#{i}-#{j}.jpg" filename = "#{level}-#{i}-#{j}.jpg"
tile_image = image.crop(x_slice[0], y_slice[0], x_slice[1], y_slice[1])
tile_image.write("#{dir}/#{filename}") do new_image = MiniMagick::Image.open("#{dir}/_tmp.jpg")
# FIXME - the images end up being 4-5x larger than those produced
# by Zoomifier EZ and friends... no idea why just yet, except to note new_image.crop("#{x_slice[1]}x#{y_slice[1]}+#{x_slice[0]}+#{y_slice[0]}")
# that the density of these tiles ends up being 400x400, while new_image.strip
# everybody else produces tiles at 72x72. Can't see why that would new_image.write("#{dir}/#{filename}")
# matter though...
self.quality = 80
end
# Rmagick needs a bit of help freeing image memory. # Rmagick needs a bit of help freeing image memory.
tile_image = nil
GC.start GC.start
yield filename yield filename
end end

View File

@ -6,7 +6,7 @@ describe Zoomifier do
Zoomifier.should respond_to(:zoomify) Zoomifier.should respond_to(:zoomify)
end end
describe "all images", :shared => true do shared_examples "all images" do
before(:all) do before(:all) do
FileUtils.rm_rf(@output) FileUtils.rm_rf(@output)
Zoomifier::zoomify(@input) Zoomifier::zoomify(@input)
@ -31,9 +31,9 @@ describe Zoomifier do
it "should create tiles of 256x256 or less" do it "should create tiles of 256x256 or less" do
@tiles.each do |file| @tiles.each do |file|
image = Magick::Image.read(@output + file).first image = MiniMagick::Image.open(@output + file)
image.rows.should <= 256 image['width'].should <= 256
image.columns.should <= 256 image['height'].should <= 256
end end
end end

View File

@ -13,5 +13,7 @@ Gem::Specification.new do |s|
s.default_executable = 'zoomify' s.default_executable = 'zoomify'
s.has_rdoc = true s.has_rdoc = true
s.extra_rdoc_files = ['README.markdown'] s.extra_rdoc_files = ['README.markdown']
s.add_dependency('rmagick')
s.add_dependency('mini_magick')
s.add_development_dependency 'rspec'
end end