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. 日記保存時にも同様のことしつつ、類似画像を検索する

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

実際に値をセットしてみて、そのキーの容量を求めることができるコマンドがある。

https://github.com/sripathikrishnan/redis-rdb-tools

  • redis-memory-for-key [key]

このコマンドは DUMP key コマンドを発行した結果を再度 Python でパースしながら消費容量を計算している。割と面倒くさいことをして正確に出そうとしてる。

参考:ダメな方法

DUMP してサイズを見る

DUMP key して出てくる文字列のサイズを単純に見ると、これはファイルに書き出すときの形式になっており、文字列が LZF で圧縮されていたりする。

ついでにいうとキーや期限などのオーバーヘッドの容量が含まれない。

DEBUG OBJECT key の serializedlength

DUMP された結果のサイズを表示しているようで、DUMP と同様に圧縮されたサイズがでるっぽい。

  1. トップ
  2. tech
  3. Redis のメモリ消費量を見積る