kerasによる類似画像検索の実装。
まず、類似画像検索とは。
画像から画像を探すことであり、これはCNN等にて作成したモデルを応用して可能である。
類似画像検索のおもしろいのは、学習済モデルの全結合層を用いて実装する形となっている。
VGG19のモデルを以下に記載する。
上記の場合、使用するのは、fc2(Dense)層の値を使用する。
さて、kerasを使用した簡単な実装を記載します。
###①VGG19の重みダウンロード
wget https://github.com/fchollet/deep-learning-models/releases/download/v0.1/vgg19_weights_tf_dim_ordering_tf_kernels.h5
※以降ではvgg19.h5にリネームしています.
###②検索対象の作成
類似画像の検索となるため、検索対象を作成します。
自身で画像を持っていれば特定のフォルダにまとめてください。
スレイピング等でも可能ですが、最近は制限が増えているため対象サイトがスクレイピング可能か確認下さい.
###③プログラム
今回はQiitaで公開されていたプログラムを一部変更して使用させてもらっています。
誰でも使用できるように、検索対象とターゲット画像をGUIにて操作可能となっていました。
import glob
from pathlib import Path
import tkinter
import tkinter.filedialog
#License
#The MIT License
import keras
from keras.models import Model
from keras.layers import Input, Dense
from keras.preprocessing import image
from keras.applications.vgg19 import preprocess_input
from keras.models import load_model
#License
#These weights are ported from the ones released by VGG at Oxford under the Creative Commons Attribution License.
#https://keras.io/applications/
from keras.applications.vgg19 import VGG19, preprocess_input
#Apache License Version 2.0
#https://github.com/nmslib/nmslib/blob/master/README.md
import nmslib
#https://numpy.org/license.html
import numpy as np
current_path = Path.cwd()
# refer https://qiita.com/wasnot/items/20c4f30a529ae3ed5f52
# refer https://qiita.com/K-jun/items/cab923d49a939a8486fc
def main():
print("データベースを選択してください")
print("サブディレクトリ内の画像もすべて検索対象となります")
data_folder_path = tkinter.filedialog.askdirectory(initialdir = current_path,
title = 'choose data folder')
print("データベースと比較したい画像を選択してください")
test_img_path = tkinter.filedialog.askopenfilename(initialdir = current_path,
#title = 'choose test image', filetypes = [('image file', '*.jpeg;*.jpg;*.png')])
title = 'choose test image', filetypes = [('image file', '*')])
#base_model = VGG19(weights="imagenet")
base_model = VGG19(weights="vgg19.h5")
#base_model.summary()
#outputsを"fc2"と指定し、2番目の全結合層を出力します
model = Model(inputs=base_model.input, outputs=base_model.get_layer("fc2").output)
test_img = image.load_img(test_img_path, target_size=(224, 224))
x = image.img_to_array(test_img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)
test_fc2_features = model.predict(x)
#選択したフォルダに存在するpng,jpeg,jpgをサブディレクトリも含めて抽出
png_list = glob.glob(data_folder_path + "/**/*.png", recursive=True)
jpeg_list = glob.glob(data_folder_path + "/**/*.jpeg", recursive=True)
jpg_list = glob.glob(data_folder_path + "/**/*.jpg", recursive=True)
image_list = png_list + jpeg_list + jpg_list
fc2_list = []
for image_path in image_list:
img = image.load_img(image_path, target_size=(224, 224))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)
fc2_features = model.predict(x)
fc2_list.append(fc2_features[0])
index = nmslib.init(method='hnsw', space='cosinesimil')
index.addDataPointBatch(fc2_list)
index.createIndex({'post': 2}, print_progress=True)
ids, distances = index.knnQuery(test_fc2_features, k=len(image_list))
result = [image_list[i] for i in ids]
#print(ids)
#print(distances)
#print(result)
count = 0
print("選択した画像は " , test_img_path, " です")
print("選択した画像に似ている順に表示します")
for i, id in enumerate(ids):
print(image_list[id], " : 距離: ", distances[i])
count += 1
if count == 5:
break
main()
###④実行結果
結果は、検索対象の画像から類似している上位5つを表示するようにしています。
****************************************************選択した画像は /home/taroimo/2022/similary/Target/sea.jpg です
選択した画像に似ている順に表示します
/home/konosuke/2022/similary/DB/DSC00493.jpg : 距離: 0.49756563
/home/konosuke/2022/similary/DB/PXL_20211015_043202997.jpg : 距離: 0.54518986
/home/konosuke/2022/similary/DB/DSC02634.jpg : 距離: 0.5474366
/home/konosuke/2022/similary/DB/DSC00424.jpg : 距離: 0.5517075
/home/konosuke/2022/similary/DB/PXL_20211014_003302316.jpg : 距離: 0.5669149
以上で、実装終了です。
あまり頭を使わずに類似画像検索を検証できてよかったです。
実際に特定の目的で使用する場合の作成方法を今後は模索しようと思います。
###参考サイト
https://qiita.com/holyeightmonk/items/489a2ef2e8c6c22d97f6