概要
大量の画像の中から類似画像を検索するソリューションを開発するとします。
類似画像を検索したいが、その都度ファイルを読み出したり、メモリに乗せて処理するのは速度やHWコスト面で大変です。
検索を容易にするためにはRDBMSを活用するのが楽です。
そのため今回は画像の形状パターンをハッシュ化して予めDBに登録しておき、検索したい画像のハッシュ値とSQLだけで類似画像を検索できるようにします。
画像の形状パターンをハッシュ化するには Avarage Hash(aHash) や Perceptual Hash(pHash) などの手法があります。
Avarage Hash
画像を8x8に縮小してグレースケール化した各点の輝度値の平均値を取り、その平均値と比べ各点大きいか小さいかで2値化して一列にすることで64bit値を取得します。
Perceptual Hash
画像を8x8より大きい適当なサイズに縮小して、離散コサイン変換し、低周波成分の8x8を利用して64bit値を取得します。
画像のハッシュ化
aHash や pHash は ImageHash というライブラリを使うと大変楽にできます。
Pythonならpipで入手できます。
pip install imagehash
画像をハッシュ化するには以下のような感じです。
from PIL import Image
import imagehash
# Average hash
ahash = imagehash.avarage_hash(Image.open("画像ファイル"))
# Perceptual hash
phash = imagehash.phash(Image.open("画像ファイル"))
これらのハッシュ化は画像を結果的に8x8=64bitのハッシュ値にします。
DBにハッシュ登録用のカラム作成
画像ファイルを管理するテーブルにハッシュ値用のカラムを追加します。
今回は組み込み関数の都合上XAMPPのMariaDBで説明します。
ハッシュ値は64bitの正の整数にできるので、分かりやすくUnsignedのbigintで定義します。
テーブルにデータを挿入する方法は色々あるので今回は割愛します。
SQLで検索
ハッシュ値同士の差であるハミング距離はXORで取得できます。
例えば
0xbfdd87c1cddfc0d0 = 0b1011111111011101100001111100000111001101110111111100000011010000
0xbfdf81f1cfdfc1d0 = 0b1011111111011111100000011111000111001111110111111100000111010000
という2つのハッシュ値の差は、
0b0000000000000010000001100011000000000010000000000000000100000000
となります。
この場合1が7つあるので、ハミング距離は7です。
ハミング距離が小さいほど類似しているものになります。
なので類似度の高いものから上位5件取りたい場合は、
SELECT
id,
CHAR_LENGTH(BIN(hash ^ 検索するハッシュ)) - CHAR_LENGTH(REPLACE(BIN(hash ^ 検索するハッシュ), '1', '')) as diff
FROM
images
ORDER BY diff ASC
LIMIT 5;
のようにすることで、取得できます。
感想
RDBMSで画像の管理しているならついでにハッシュ値も登録しておくと便利。