Rubyで画像生成メモ
数百枚のVGA程度の解像度の画像にそれぞれ数十のアルファチャンネルあり画像をcompositeして書き出す処理なら、RMagickよりjavax.imageioをJRubyから使うのがいいんじゃないの
- javax.imageio、JAIともにJavaで書いてもJRubyから使っても速度差は無かった(とりあえず画像の読み込み、フォーマット変換、書き出し、width heightなどのパラメータ取得は確認)
- Java1.5→1.6にするとjavax.imageioの画像読み込み/書き出し速度1.3〜1.5倍になる
- Java1.6じゃないとJAIで画像を書き出せない
- Java1.6じゃないとimageioでpng形式で画像を書き出せない(getTypeがエラーになる)
- javax.imageioとJAIでは縦横1000ピクセルなどの大きい画像の時は読み書き速度はJAIの方が速いが、320x240程度x数百枚なら変わらない
- でもImageMagick(RMagick)の方がさらに2〜3倍速い
アルファ値ありの画像のcompositeはimageioめちゃくちゃ速い。ImageMagick遅い。
- 320x240、250枚の画像に50個の40x40の画像をランダムにcompositeした
- ImageMagick(Ruby1.8.7+RMagick2.13.1+ImageMagick6.5.2)→25秒前後
- imageio(JRuby1.5.1+Java1.6+javax.imageio)→6〜7秒
- 250枚に10個ずつに減らした
- ImageMagick→7〜8秒
- imageio→4秒
- 大量にcompositeするならimageioが速い
- JAIでcompositeの仕方がよくわからない
JRuby+JAIは使いにくい
- ParameterBlockにjava.lang.Float値を入れる必要があるが、RubyでNumber.to_fするとDoubleになる。明示的にjava.lang.Float.parseFloat("0.3");とかして直に値を渡してもJRubyがjava.lang.Doubleとして渡してしまう。
JRubyからJAI.create("filestore" すると
java.lang.IllegalArgumentException: operation "FileStore" requires 1 source object(s).
というエラーがでる。
これも、JAI.creaet "filestore"をするJavaプログラムを先に作ってclassにしてimportして使えば回避できる
こうやる
ImageCopyJai.java
import javax.media.jai.Interpolation; import javax.media.jai.JAI; import javax.media.jai.RenderedOp; import com.sun.media.jai.codec.FileSeekableStream; public class ImageCopyJai{ public static void main(String args[]){ if(args.length < 2){ System.out.println("ImageCopyJai input.jpg output.bmp"); System.exit(1); } if(ImageCopyJai.copy(args[0], args[1])){ System.out.println(args[1]); } else System.out.println("error"); } public static boolean copy(String name_in, String name_out) { try{ RenderedOp img = JAI.create("fileload", name_in); System.out.println(name_in+" => "+img.getWidth()+", "+img.getHeight()); JAI.create("filestore", img, name_out, "BMP", null); } catch(Exception e){ e.printStackTrace(); return false; } return true; } }
JRubyから使う
image_copy.rb
#!/usr/bin/env jruby # -*- coding: utf-8 -*- require 'java' import 'ImageCopyJai' import 'javax.media.jai.Interpolation' import 'javax.media.jai.JAI' import 'javax.media.jai.RenderedOp' import 'com.sun.media.jai.codec.FileSeekableStream' import 'java.awt.image.renderable.ParameterBlock' if ARGV.size < 2 puts 'jruby iamge_copy.rb /dir/to/in/ /dir/to/out/' exit 1 end indir = ARGV.shift outdir = ARGV.shift indir += '/' if indir =~ /.+[^\/]$/ outdir += '/' if outdir =~ /.+[^\/]$/ Dir.mkdir(outdir) if !File.exists?(outdir) files = Dir.glob("#{indir}*") puts files.size start = Time.now files.each{|name| begin out_name = name.scan(/\/([^\/]+)\..+$/).first ImageCopyJai.copy(name, "#{outdir}#{out_name}.bmp") rescue => e puts "#{name} => error" puts e end } puts Time.now-start