Ruby Rmagick で同じ画像を見つける

やってることは、ImageMagick の compare で2つの画像を比較し RSME を出して、小さいものを表示することです。RSME はピクセルの何かの値の二乗和のルートです。

最初に黒の画像との比較で RSME を出して、小さい順に並べて、順番に 10 個ずつ比較する。似た画像なら、RSME も近い値になっているという仮定でそうしてる。もし、全組み合わせをすると大変な量になる、例えば 1000 個の画像があると、1000*(1000-1)/2 = 499,500 通りとなるが、このやりかただと、990x10 + 10*(10-1)/2 = 9945通りですむので。

RMagick と Parallel が必要。あとはコメントを参照
http://rmagick.rubyforge.org/
https://github.com/grosser/parallel

ソースは http://ge.tt/7Hn3Uzh/v/0?c からダウンロードもできます。

#!/usr/bin/ruby
require 'tmpdir'
require 'parallel'
require 'readline'
require 'RMagick'
require 'tempfile'
include Magick

if ARGV[0] != nil then
	srcdir=ARGV[0]
	if FileTest.exist?(srcdir) then
		if File::ftype(srcdir) != "directory" then
			srcdir=Dir.pwd
		end	
	else
		srcdir=Dir.pwd
	end
else
	srcdir=Dir.pwd
end

picsuffix="jpg,png,gif"

piclist=[]
black=Magick::Image.new(300,300) {self.background_color = "black"}

Dir.mktmpdir("tmp",ENV["HOME"]){|td|

	# Parallel だと piclist が空になってしまったので、普通の foreach
	# Global 変数とか使えばうまく行くのかな?
	Dir.glob(srcdir+"/*{"+picsuffix+"}").each {|f| 

		tp=Tempfile.open(["",".png"],td)
		
		# 最初 .difference まで一気にやったら、メモリ消費がすごいことに
		# 一旦ファイルに吐くとだいぶ良くなった
		ImageList.new(f).resize(300,300).write(tp.path)
		a=ImageList.new(tp.path).difference(black)[0]
		tp.close
		piclist=piclist+[[a,f]]
	}
	piclist=piclist.sort{|p,q| p[0]<=>q[0]}

	while piclist.length>0
		tp1=Tempfile.open(["",".png"],td)
		pic1=ImageList.new(piclist[0][1]).resize(300,300).write(tp1.path)
		pic1=ImageList.new(tp1.path)
		Parallel.each(piclist[1,10], in_threads:4){|i|
			tp2=Tempfile.open(["",".png"],td)
			pic2=	ImageList.new(i[1]).resize(300,300).write(tp2.path)
			pic2=ImageList.new(tp2.path)
			difference=pic1.difference(pic2)[1]
			if difference < 0.05 then
				puts piclist[0][1]+","+i[1]+","+difference.to_s+"\n"
			end
			tp2.close
		}
		tp1.close
		piclist.shift
	end
}