でかいjpeg画像をmini_magickで高速にサムネイル化(リサイズ&Crop)する方法
画像をサムネイル化する必要があったのでどうしたら速くなるのかしらべてたら本当は速いImageMagick: サムネイル画像生成を10倍速くする方法によると 普通にImageMagickでも十分速いみたい。
なのでrubyのImageMagickラッパーのmini_magickを使ってどれぐらい速くなるのか調査
準備
image_magickのインストール
brew install imagemagick
mini_magickのインストール
gem install mini_magick
-defineオプションをつける
リサイズの対象がjpegでリサイズ後のサイズがだいたいわかっているときは-define jpeg:size=100x100
ってオプション(100x100はリサイズ後のサイズ)をつけてやると省メモリで速くなるらしいのでどれぐらい変わるか調査。 ついでにmini_magick経由と直接コマンドを実行するのでどれくらいかわるかも調べる。
使ったコードは以下のとおり。
require 'benchmark'
require 'mini_magick'
n = 10
blob = File.open('cat2.jpg').read
Benchmark.bm(22) do |x|
x.report("mini_magic define on:") {
n.times do
image = MiniMagick::Image.read(blob)
image.define "jpeg:size=100x100"
image.resize "100x100"
image.write 'resized_cat.jpg'
end
}
x.report("mini_magic define off:") {
n.times do
image = MiniMagick::Image.read(blob)
image.resize "100x100"
image.write 'resized_cat.jpg'
end
}
x.report("sh define on:"){
n.times do
`convert -define jpeg:size=100x100 -resize 100x100 cat.jpg resized_cat3.jpg`
end
}
x.report("sh no define off:"){
n.times do
`convert -resize 100x100 cat.jpg resized_cat3.jpg`
end
}
ちなみにcat2.jpgはだいたい2400*3000で6.4MBぐらいのでっかい画像を使って同じ処理を10回繰り返した。 結果は以下のとおり。
user system total real
mini_magic define on: 0.140000 0.260000 3.650000 ( 4.029392)
mini_magic define off: 0.140000 0.240000 11.170000 ( 12.378788)
sh define on: 0.000000 0.010000 1.810000 ( 2.036538)
sh define off: 0.000000 0.000000 11.440000 ( 12.624999
defineオプションすげー!って感じです。あとやっぱり直にImageMagickを呼んだほうが速くなるみたい。
切り取り方法を変えてみる
リサイズして中心を四角くCropしてサムネを作りたかったのでCrop方法もちょっと試してみた。
1つめはshapeを使って画像の要らないとこをそぎ取る。 2つめは単純にCropする。 3つめはcarrierwaveでのCrop方法にdefineオプションつけたもの。処理的には切り取りたいサイズの透明な画像を中心に置いて重なった部分だけを残す感じ。
require 'benchmark'
require 'mini_magick'
n = 10
blob = File.open('cat2.jpg').read
Benchmark.bm(22) do |x|
x.report("mini_magic crop1:"){
n.times do
image = MiniMagick::Image.read(blob)
image.define "jpeg:size=100x100"
w = image[:width]
h = image[:height]
if w < h
remove = ((h - w)/2).round
image.shave("0x#{remove}")
elsif w > h
remove = ((w - h)/2).round
image.shave("#{remove}x0")
end
image.resize "100x100"
image.write 'resized_crop_cat.jpg'
end
}
x.report("mini_magic crop2:"){
n.times do
image = MiniMagick::Image.read(blob)
width = height = 100
cols, rows = image[:dimensions]
if width != cols || height != rows
scale = [width/cols.to_f, height/rows.to_f].max
cols = (scale * (cols + 0.5)).round
rows = (scale * (rows + 0.5)).round
end
crop_x = crop_y = 0
crop_val = ((cols - rows).abs / 2).round
if cols < rows
crop_y = crop_val
else
crop_x = crop_val
end
image.define "jpeg:size=#{cols}x#{rows}"
image.resize "#{cols}x#{rows}"
image.crop "100x100+#{crop_x}+#{crop_y}"
image.write 'resized_crop_cat2.jpg'
end
}
x.report("mini_magic crop3:"){
n.times do
img = MiniMagick::Image.read(blob)
width = 100
height = 100
cols, rows = img[:dimensions]
img.define "jpeg:size=#{width}x#{height}"
img.combine_options do |cmd|
if width != cols || height != rows
scale = [width/cols.to_f, height/rows.to_f].max
cols = (scale * (cols + 0.5)).round
rows = (scale * (rows + 0.5)).round
cmd.resize "#{cols}x#{rows}"
end
cmd.gravity 'Center'
cmd.background "rgba(255,255,255,0.0)"
cmd.extent "#{width}x#{height}" if cols != width || rows != height
end
img.write 'resized_crop_cat3.jpg'
end
}
end
結果は以下の通り。
user system total real
mini_magic crop1: 0.160000 0.260000 4.630000 ( 5.327175)
mini_magic crop2: 0.160000 0.260000 4.230000 ( 4.850669)
mini_magic crop3: 0.150000 0.240000 3.900000 ( 4.906052)
3つめのやり方が結構速い。