libpuzzle の Perl binding である Image::Libpuzzle を使って関連画像を実装してみた。pHash や avgHash も試してみたけど、どれが良いかというとなんともいえない。

現像パラメータ違いみたいな写真同士のものはちゃんとスコアが高く出ている気がするが、それ以外の場合はいまいち似ているとは言えないような感じで、いまいちよくわからない。基本的に同一画像判定用な気がするので、あまり大きな期待はできなそう。

SQLite スキーマ

CREATE TABLE images (
	`id` INTEGER PRIMARY KEY,
	`uri` TEXT NOT NULL,
	`entry_id` INTEGER NOT NULL,
	`sig` BLOB NOT NULL
);
CREATE UNIQUE INDEX index_images_uri ON images (`uri`, `entry_id`);

CREATE TABLE ngram (
	image_id INTEGER NOT NULL,
	word BLOB NOT NULL,
	PRIMARY KEY (`image_id`, `word`)
) WITHOUT ROWID;
CREATE INDEX index_word_image_id ON ngram (`word`, `image_id`);

こういう感じのスキーマ。複数のエントリに同じURLを貼ると無駄になるけど滅多にないので気にしない。

実装

Service::SimilarImage にだいたいまとめて実装した。SQL 自体は php - Libpuzzle Indexing millions of pictures? - Stack Overflow に書いてあるのとほぼ同じ。

  1. トップ
  2. tech
  3. 関連画像を表示

redeveloped タグをつけてる写真は過去の写真を演出… | Thu, Jul 13. 2017 - 氾濫原 をやろうと思って、画像ハッシュ化方法である pHash (64bit) avgHash (64bit) を試してみたけど、ウーンって感じだった。まぁまぁうまくいっている気もするけど、全然似てない写真の距離が近いこともある。

理由として想像すると

  • pHash も avgHash も色情報を捨ててる
  • 64bit (8x8) だと足りない

なので、色を考慮したハッシュ化をしたい。チャンネルごとに pHash にするとかなのかなあ。8x8 のままで RGB 3chとると単純に192bitになる。

もうちょっとコントロール可能にするなら、YCbCr にして、それぞれハミング距離を求めつつ、チャンネルごとに係数をかける (どの程度色情報を考慮するか決定する) とか?

redeveloped タグをつけてる写真は過去の写真を演出を変えて再現像したものになってる。

なので、過去のエントリがあるならリンクを貼りたいなと思ったけど、日記のどこに貼ったかは全くわからないので難しい。しかも全て画像は Google Photos にある。

やるとしたら

  1. 一度日記内の画像を全てダウンロードして特徴量検出とインデックス化をする
  2. 日記保存時にも同様のことしつつ、類似画像を検索する

みたいになるのかなあ。「類似」といっても、現像パラメータの違いだけなので、ほとんど全く同じなんだよなあ。