Pythonでフォルダ内に同じ画像がないか検索してみた

同じフォルダの中に同じ画像がないかどうか、Pythonで調べてみました。

目次

  1. 一致する画像を検索したい
  2. 画そのもので同じ画像かどうかを判断したい
  3. ImageHashライブラリで比較する
  4. ImageHashでの画像比較例
  5. フォルダ内の類似の画像を検索する方法

一致する画像を検索したい

私は整理が苦手です。

ということで、画像ファイルがいろいろなフォルダに散乱しているのですが、なかには同じ画像であったり、同じ画像を拡大縮小したものだったりするファイルが存在します。困ったことに、ファイル名すらばらばらだったりします。私が悪いのですが。

というわけで画像ファイルを整理したいのです。面倒なことはPythonにやらせようということで、Pythonさんに同じ画像の判定をしてもらいたいと思います。

画そのもので同じ画像かどうかを判断したい

ファイルが重複するかどうか調べる方法としては、例えば、ファイル名・作成者・タイムスタンプ・ファイルサイズなどのファイルのメタ情報で比較するのが手っ取り早い方法です。ただ、クラウド上のオブジェクトをダウンロードした場合など、こういうメタ情報が変化しますのでこの方法は使えません。

ファイルのハッシュを計算して比較する方法もあります。フリーウェアのダウンロードサイトなどで使われる方法ですね。今回は画像の一致を調べたいのですが、拡大縮小されているとファイルのハッシュも変わると思いますので、この方法も使えません。

というわけで、画そのもので比較したいのです。

画像そのものの比較としてぱっと思いつくのは、各画素の輝度の比較やヒストグラムの比較、特徴点の比較です。

今回は、Perceptual hashという手法で画像そのものの比較をしてみます。 詳しく解説されているサイト がありますので、こちらを参考にしてください。

ImageHashライブラリで比較する

Pythonは便利なもので、画像のPerceptual hashを計算する ImageHash というライブラリが公開されています。

このライブラリでは、4種類のハッシュの計算ができます。

  • average hashing (aHash) 平均輝度からの差分を使う方法
  • perception hashing (pHash) DCTしてから差分を使う方法
  • difference hashing (dHash) 隣接領域との差分を使う方法
  • wavelet hashing (wHash) ウェーブレット変換してから差分を使う方法

手順としては、各画像のハッシュ値を計算して、画像間のハッシュ値の差が小さい(最小は0)ほど類似度が高いという判定をするということになります。

pipでインストールできます。PillowとSciPyが必要なようです。

> pip install ImageHash

ImageHashでの画像比較例

モデルは プロ生ちゃん のサイトからダウンロードさせていただいたIXYさんによるイラストです。元データはPSDファイルなので、PillowなどであらかじめJPEGに変換してから使用します。

比較した画像はこの5種類です。

test-1

test-1

test-2

test-2

test-3

test-3

test-4

test-4

test-5

test-5

では、test-1とtest-2の各ハッシュ値の差分を計算してみます。

from PIL import Image
import imagehash

hash_1a = imagehash.average_hash(Image.open('test-1.jpg'))
hash_1p = imagehash.phash(Image.open('test-1.jpg'))
hash_1d = imagehash.dhash(Image.open('test-1.jpg'))
hash_1w = imagehash.whash(Image.open('test-1.jpg'))

hash_2a = imagehash.average_hash(Image.open('test-2.jpg'))
hash_2p = imagehash.phash(Image.open('test-2.jpg'))
hash_2d = imagehash.dhash(Image.open('test-2.jpg'))
hash_2w = imagehash.whash(Image.open('test-2.jpg'))

print('ahash : ', hash_2a-hash_1a)
print('phash : ', hash_2p-hash_1p)
print('dhash : ', hash_2d-hash_1d)
print('whash : ', hash_2w-hash_1w)
> python test.py
ahash :  5
phash :  12
dhash :  6
whash :  6

こんな感じで数値になります。計算方法によって結果が結構異なりますね。

各画像のtest-1からの差を計算してみます。

  test-1 test-2 test-3 test-4 test-5
ahash 0 5 4 0 13
phash 0 12 16 0 30
dhash 0 6 7 0 26
whash 0 6 0 0 32

test-1とtest-1が一致するのは当然ですが、縮尺の違うtest-1とtest-4もきちんと一致しました。

whashでtest-1とtest-3が一致したのが謎ですね。得手不得手がありそうなので、試してから使用するのが良さそうです。

フォルダ内の類似の画像を検索する方法

では、フォルダ内の類似画像の検索をしてみます。

今回は、ahashでハッシュ値の差が0になるものを検出させます。

import os
from PIL import Image
import imagehash

userpath = '.'  # 検索するパス

image_files = []
f = [os.path.join(userpath, path) for path in os.listdir(userpath)]
for i in f:
    if i.endswith('.jpg') or i.endswith('.png'):
        image_files.append(i)

imgs = {}
for img in sorted(image_files):
    hash = imagehash.average_hash(Image.open(img))
    if hash in imgs:
        print('Similar image :', img, imgs[hash])
    else:
        imgs[hash] = img

出力はこうなります。

>python p.py
Similar image : .\test-4.jpg .\test-1.jpg

検出できました。

広告

Pythonで画像処理カテゴリの投稿