Scribble at 2024-10-10 21:17:52 Last modified: unmodified
ついでに、二枚の画像を比較するプログラムを作った。かなり単純で制約条件が多いけれど、ひとまず Stable Diffusion で比較のために同じサイズで生成した画像どうしを比較するには、これでいいだろう。
考え方としては、画像のピクセルから読み取った色のインデックス情報を連結して、シリアライズした一個の文字列として整形し、それら二つの文字列どうしのレーヴェンシュタイン距離を測っている。レーヴェンシュタイン関数なんて、「さよなら0系新幹線」のサイトでバックエンドを開発したとき以来だな。
ここで比較する対象を制約した事情があって、もちろん 169 x 231 ピクセルという小さな画像でも、全てのポイントについて色情報を取り出して比較するとなると、40万弱の情報を比較することになる。こんな小さな画像でも、比較するのに何十分もかかってしまうのでは実用性がない。したがって、類似画像検索のシステムを開発するときでも利用される手法だが、まず比較する画像を圧縮してしまうのが妥当だろう。確かに、これだと比較の精度は落ちるが解析の速度は上がる。ということで、よくあるのが 8 x 8 ピクセルへの圧縮ということのようなので、たぶん比較するのに使うときは正方形の同じサイズで出力した画像どうしを扱うだろうから、こういう制約は特に問題ない。ただ、8 x 8 ピクセルではあまりにも精度が低いと思うので、自分用にローカルで動かすだけだから、もう少し丁寧に比較しようということで、64 x 64 ピクセルへ縮小した画像を比較している。
なお、他にもハミング距離などを扱う関数はあるが、これは使えなかった。ハミング距離を測る関数は、整数しか受け付けないからだ。PHP で整数として扱える桁数の限界が19桁(約900京)であるから、これでは 8 x 8 ピクセルの色情報をシリアル化した数字を整数に変換した値すら扱えない。そして更にまずいのは、色情報の先頭が 34 とか 8 とかになりえるので、全く同じサイズの画像どうしを比較する場合でも整数にしてしまうと比較すべき桁数がズレてしまい、そのズレが大きくなると異なる位置どうしの色情報を比較してしまう問題が起きる。これを防ぐには、imagecolorat() で取得したインデックス情報から R, G, B という色ごとの情報を抜き出して、sprintf() で0のパディングを使った三桁の文字列に整形してから、文字列として連結してシリアライズしなくてはならない。こうして文字列どうしを比較できる関数としては、レーヴェンシュタイン関数が残るというわけである。
なお、64 x 64 ピクセルだとポイントの総数は4,096だが、異なる1色で塗りつぶした画像を比較すると、レーヴェンシュタイン距離は28,672という数値が出る。これだと理論上の最大値が分からないので、幾つかの画像を比較してみて数値の増減という相対的な比較しかできないのだが、それはまた今後の課題ということにしたい。