大量の画像ファイルからOpenCV使って特定の顔が映ってる写真だけを抜き出すやつを作った
タイトルの通りです。必要性に迫られて、ある顔が映っている画像ファイルを選別して抜き出すやつを作ってみました。
長男の卒業式写真をもらったんですが・・・約740枚もあって、とんでもない量が送られてきました。容量も全部で11.2GB。
いやあ、いくらなんでも740枚は多過ぎだろう。欲しいのは、自分の子供が映っているやつだけなんだけど。
ちょっと、学校側は張り切り過ぎです。
てことで、
1.テンプレート画像となる顔画像を用意
2.740枚の画像が入ったフォルダ「data」の1枚1枚を読み込み、顔検出する
3.その検出した画像とテンプレート画像の顔をと比較し、類似度を計算
4.類似度がある一定以上の顔画像を含む画像ファイルを「result」というフォルダにコピーする
というプログラムを作りました。
いわゆるディープラーニングの画像認識を使わず、OpenCVのみとしました。なんせ、740枚も処理するので、畳み込みニューラルネットワーク(CNN)では時間がかかり過ぎです。このため、簡易な方法を選びました。
具体的には、顔検出にはhaar-like特徴分類器を、類似度計算に使う画像特徴量にはSIFT特徴を、2つの画像の特徴量のマッチングにはBFMatcher、つまり総当たりのアルゴリズムを使ってます。
で、作ったのが以下のプログラムです。
(「face_pickup.py」という名前にしました)
import numpy as np
import os
import shutil
# 顔を検出する関数
# Haar-like特徴分類器を使用して顔を検出
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=10, minSize=(200, 200))
return faces
# SIFT特徴抽出器を作成する関数
sift = cv2.SIFT_create()
return sift
# 画像の特徴量を計算する関数
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
keypoints, descriptors = sift.detectAndCompute(gray, None)
return keypoints, descriptors
# マッチングを行い、類似度を計算する関数
bf = cv2.BFMatcher()
matches = bf.knnMatch(des1, des2, k=2)
good_matches = []
try:
for m, n in matches:
if m.distance < 0.75 * n.distance:
good_matches.append(m)
except ValueError:
pass
similarity = len(good_matches) / max(len(des1), len(des2))
return similarity
# メインの処理
# 顔検出用の画像を読み込む
face_image = cv2.imread('face.jpg')
# dataフォルダ内の画像を読み込む
images = [f for f in os.listdir(images_folder) if os.path.isfile(os.path.join(images_folder, f))]
# SIFT特徴抽出器を作成する
# 類似度のしきい値
# 類似度の高い顔が含まれている画像をresultフォルダにコピーする
image_path = os.path.join(images_folder, image_file)
image = cv2.imread(image_path)
# 顔を検出する
if len(faces) > 0:
face_keypoints, face_descriptors = compute_features(face_image, sift)
max_sim = 0.0
similarity = 0.0
for (x, y, w, h) in faces:
detectface_image = image[y:y+h,x:x+w]
image_keypoints, image_descriptors = compute_features(detectface_image, sift)
# 類似度を計算する
similarity = match_features(face_descriptors, image_descriptors)
if similarity>max_sim:
max_sim = similarity
print(image_file, ',', len(faces), ',', max_sim)
result_folder = 'result'
if not os.path.exists(result_folder):
os.makedirs(result_folder)
shutil.copy(image_path, os.path.join(result_folder, image_file))
print("類似度の高い顔が含まれている画像のコピーが完了しました。")
if __name__ == '__main__':
main()
実は、Bing Chatで作らせたんですが、エラーだらけでして、これを修正してまともに動かせるように変えてます。
ChatGPTもかなり進化してますが、なかなかそのまま使えるコードをいきなりは作ってくれませんね。
これと、顔画像を検索させたい画像ファイルを「data」というフォルダにぶち込んでおきます。
使うライブラリは、OpenCV、numpy辺りがあれば使えます。
さらに、face.jpgという名前で、検索したい顔画像を用意しておきます。
ぼかしのおかげでちょっとヤバい感じの写真ですが、これがテンプレート画像です(もちろん、実際にはぼかし無しです)。
フォルダ構成は以上のようになります。
プログラム本体「face_pickup.py」以外には、検索させたい画像をおさめた「data」フォルダ、抜き出した画像をおさめた「result」フォルダ、テンプレート画像「face.jpg」を用意します。
(「result」フォルダはなくても自動で作られます)
これで、準備完了。
実行は、コマンドプロンプトなどでこのプログラムの入ったフォルダに移動して、
python face_pickup.py
です。
こんな感じに実行されます。ファイル名、検出された顔画像の数、そしてその中で一番高い類似度の値、の順に表示されます。
なお類似度の閾値は、プログラム中ほどにある「similarity_threshold = 0.065」の0.065の値を調整します。
これを決めるために、最初は2、30枚くらいの画像でトライした方がいいでしょう。
で、閾値を決めたら、740枚を一気に走らせます。
で、出てきた結果がこれ。数分ほどで処理は完了。結果、だいたい200枚くらいが拾われました。
ですが、結構余計な画像が多いですね。顔ですらない画像も中には含まれてました。
それらを削ると、残ったのは結局100枚くらい。
さほど精度がいいとは言い難いですが、740枚から1枚1枚探ることを考えると、これでもかなりマシです。
おそらくですが、何枚かは欠落しているかと思いますが、これだけ取れれば十分かと。
最初、妻が全部見て選ぶ言ってたんですが、途中で音を上げました。
そんな妻に、Pythonの威力とやらを見せつけてやりましたよ。ほんと、OpenCVの画像処理サマサマです。
高精度なCNNを使わずとも、OpenCVだけでもここまでのことはできるんです。ご参考まで。
Pythonと深層学習による革新的な画像認識と処理の裏技~CNNとOpenCVで実現する顔認識、物体検出、画像分類のテクニック~ |
« 動画生成AIサービス「Luma AI」を試してみた | トップページ | 最近買った小物類 »
「数値解析系」カテゴリの記事
- Googleの生成AI「Gemini Advanced」に入ってみた(2024.12.01)
- Tanuki-8Bの4ビット量子化ggufモデルでRAGをやってみました(2024.09.14)
- 純日本産なローカルLLM「Tanuki-8B」を試してみた(2024.09.02)
- 東京のセールスフォースに行ってきました(2024.07.24)
- ローカルLLM「Llama-3-ELYZA-JP-8B」にいろいろ聞いてみた(2024.07.01)
コメント