※ https://qiita.com/best_not_best/items/669aaa9e1b8de647d29d にこの記事の修正版を投稿しました!
皆さんこんにちは。@best_not_bestです。
現在のお仕事は、求人サイトの案件レコメンド機能の実装や、Google AnalyticsやAdobe Analyticsを使って、そのサイトの分析をしています。(あとは動物画像を社内SlackにひたすらPostしています。)
前回のAdvent Calendarでは、Deep Learningを使った画像類似度判定を行いました。精度は良かったのですが、やはり学習データを収集することがネックとなります・・・。今回は、単純に2枚の画像を比較して画像の類似度を求めてみたいと思います。
やること
以下のおいぬ様の画像の類似度を比較します。画像はGoogle先生から拾ってきています。
精度を高めるため、正面を向いている画像を選びました。
比較画像
ファイル名 | 画像 | 説明 |
---|---|---|
05.png | 柴犬(かわいい) |
比較対象
ファイル名 | 画像 | 説明 |
---|---|---|
01.png | ダックスフント(かわいい) | |
02.png | コーギー(かわいい) | |
03.png | ゴールデン・レトリバー(かわいい) | |
04.png | 柴犬(かわいい) | |
06.png | ラブラドール・レトリーバー(かわいい) |
同じ犬種である、05.pngと04.pngの類似度が高くなれば(いちおう)成功です。
環境
- マシン/OS
- MacBook Pro (Retina, 15-inch, Mid 2014)
- OS X Yosemite 10.10.5
- Python
- Python 3.5.2 :: Anaconda 4.1.1 (x86_64)
- Pythonパッケージ
- opencv3 3.1.0(condaでインストールします。)
ディレクトリ構成
hist_matching.py
feature_detection.py
images
├─ 01.png
├─ 02.png
├─ 03.png
├─ 04.png
├─ 05.png
└─ 06.png
検証1: ヒストグラム比較
ざっくり言うと、色合いで比較する手法です。詳細は以下を参照ください。
色合いで比較するのでグレースケール変換は行いません。また、画像サイズは一律200px × 200pxに変換して比較しています。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""hist matching."""
import cv2
import os
TARGET_FILE = '05.png'
IMG_DIR = os.path.abspath(os.path.dirname(__file__)) + '/images/'
IMG_SIZE = (200, 200)
target_img_path = IMG_DIR + TARGET_FILE
target_img = cv2.imread(target_img_path)
target_img = cv2.resize(target_img, IMG_SIZE)
target_hist = cv2.calcHist([target_img], [0], None, [256], [0, 256])
print('TARGET_FILE: %s' % (TARGET_FILE))
files = os.listdir(IMG_DIR)
for file in files:
if file == '.DS_Store' or file == TARGET_FILE:
continue
comparing_img_path = IMG_DIR + file
comparing_img = cv2.imread(comparing_img_path)
comparing_img = cv2.resize(comparing_img, IMG_SIZE)
comparing_hist = cv2.calcHist([comparing_img], [0], None, [256], [0, 256])
ret = cv2.compareHist(target_hist, comparing_hist, 0)
print(file, ret)
実行すると、メモリを消費するのかSegmentation fault: 11
やpython(18114,0x7fff7a45b000) malloc: *** error for object 0x102000e00: incorrect checksum for freed object - object was probably modified after being freed.
といったエラーがまれに表示されます。解決策は見つかりませんでした(すいません)が、大量の画像を扱う場合は、比較画像の数を絞って複数回処理を行ったほうが良さそうです。
TARGET_FILE: 05.png
01.png 0.3064316801821619
02.png -0.09702013809004943
03.png 0.5273343981076624
04.png 0.5453261576844468
06.png 0.1256772923432995
全く同じ画像の場合、類似度は1となります。05.pngと04.pngの類似度が高くなっているのが分かります。01.pngとの類似度が高いのは意外でした。
検証2: 特徴点のマッチング
2画像の特徴点を抽出し、それらの距離を比較します。以下を参考にさせていただきました。
- 3日で作る高速特定物体認識システム (4) 特徴点のマッチング - 人工知能に関する断創録
- [OpenCV] いまさら局所特徴量で物体検出!? - Qiita
- 特徴点のマッチング - OpenCV-Python Tutorials 1 documentation
抽出精度を高めるため、グレースケール変換を行っています。前項と同様に、画像サイズは一律200px × 200pxに変換して比較しています。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""feature detection."""
import cv2
import os
TARGET_FILE = '05.png'
IMG_DIR = os.path.abspath(os.path.dirname(__file__)) + '/images/'
IMG_SIZE = (200, 200)
target_img_path = IMG_DIR + TARGET_FILE
target_img = cv2.imread(target_img_path, cv2.IMREAD_GRAYSCALE)
target_img = cv2.resize(target_img, IMG_SIZE)
bf = cv2.BFMatcher(cv2.NORM_HAMMING)
# detector = cv2.ORB_create()
detector = cv2.AKAZE_create()
(target_kp, target_des) = detector.detectAndCompute(target_img, None)
print('TARGET_FILE: %s' % (TARGET_FILE))
files = os.listdir(IMG_DIR)
for file in files:
if file == '.DS_Store' or file == TARGET_FILE:
continue
comparing_img_path = IMG_DIR + file
try:
comparing_img = cv2.imread(comparing_img_path, cv2.IMREAD_GRAYSCALE)
comparing_img = cv2.resize(comparing_img, IMG_SIZE)
(comparing_kp, comparing_des) = detector.detectAndCompute(comparing_img, None)
matches = bf.match(target_des, comparing_des)
dist = [m.distance for m in matches]
ret = sum(dist) / len(dist)
except cv2.error:
ret = 100000
print(file, ret)
まれにcv2.error
を吐くため、try except
を使っています。抽出手法はAKAZEとORBで試しました。
TARGET_FILE: 05.png
01.png 143.925
02.png 134.05
03.png 140.775
04.png 127.8
06.png 148.725
TARGET_FILE: 05.png
01.png 67.59139784946237
02.png 58.60931899641577
03.png 59.354838709677416
04.png 53.59498207885304
06.png 63.55913978494624
距離を算出しているので、全く同じ画像の場合の値は0となり、値が小さいほど類似度が高くなります。実施したどちらの手法でも、前項と同様に05.pngと04.pngの類似度が高くなっています。
まとめ
- Deep Learningを使わなくてもそれなりの精度は出せる
- 手法に応じてグレースケール変換、サイズ変換を行う
- 特徴点抽出の場合、向きが異なる画像の比較は難しいので、同じ向きの画像を比較させる
- 例えば、必ず正面を向いている社員証の画像どうしは比較し易い気がする
- おいぬ様 is GOD.