快イタン

オタク趣味用ブログ兼雑記帳です

証明写真用の画像をopenCV+Pythonで顔認識してトリミング

目的

顔認識を使って証明写真用の顔写真を全身の写真or集合写真から任意の大きさ(ピクセル単位)にトリミングする.

背景

証明写真用の写真を指定された大きさ(ピクセル単位)に切り出す必要があったので,「手作業での切り出しはめんどくさい」「画像処理で扱いやすい」という状況で自動的にやるほうが楽かなーと思ってやりました.あとはブログを始めるための適当なネタとして….

環境

OpenCV 4.4.0+Python 3.8.5

やったこと

概要

顔認識にはopenCV備え付けのカスケード分類機を使用.学習済みの顔認識用のものがある.OpenCVすごい便利!
カスケード分類機で認識した顔領域をを手動で入力した大きさにいい感じに切り出し. 後は後述の誤検出を減らす目的でガウシアンフィルタを顔認識の前に掛けてるけど 気休め程度にしかなっていない感じ….

カスケード分類器はopenCVのソースと同じところにあるので探して同じフォルダにおいてください.

コード

import cv2
import numpy as np

cascadePath =  "./cascades/haarcascade_frontalface_default.xml"
img_path = "./input.jpg"

#出力画像のサイズを選択
print("切り出したい写真の幅,高さを入力してください.") 
print("x y")
X,Y=(int(x) for x in input().split())
print(X, "x", Y,"のサイズに画像を切り出します")

#ガウシアンカーネル
kernel = np.array([[1/16, 1/8, 1/16],
                   [1/8, 1/4, 1/8],
                   [1/16, 1/8, 1/16]])

#画像の下処理
src = cv2.imread(img_path)
gray = cv2.cvtColor(src,cv2.COLOR_BGR2GRAY)
dst = cv2.filter2D(gray, -1, kernel)
#dst =gray #ガウシアンフィルタをかけない場合

#カスケード分類器による顔の検出
cascade = cv2.CascadeClassifier(cascadePath)
rect = cascade.detectMultiScale(dst)

#画像の切り出し
if len(rect) > 0:
    i = 0
    for x, y, w, h in rect:
        W = int(1.4 * w)
        H = int(W * Y / X)

        #幅の微調整
        inty = int(y - 0.2 * H)
        intx = int(x - 0.15 * W)

        faceCut = src[inty:inty+H, intx:intx+W]
        outImg = cv2.resize(faceCut, (X, Y))

        outName = "./output" + str(i) + ".jpg"
        cv2.imwrite(outName ,outImg)
        i += 1
else:
    print("顔を認識できませんでした")
print(str(i), "人の顔を切り出しました")

結果

入力画像

自分の顔はできたら晒したくないので適当なフリー画像をお借りしました.写真のサイズは1600×1066px.

フリー素材ぱくたそ(www.pakutaso.com)より

出力画像

300×400pxの画像として出力,画像は表示の都合上縮小しました. なんかそれっぽい感じ,もちろん被写体が1人でもできます.OpenCV本当に便利ですね.

縦横比4:3で出力

おまけ

普通のサイズの画像なら大体上手くいくんですが,8000×5500pxくらいの高画質の画像でやると若干の誤検出が起きました.カスケード分類器に使ってる特徴量自体は結構シンプルなのでしょうがないのかなーと思いますが….

一応気休め程度に画像平滑化用のガウシアンフィルタをかけたら誤検出がちょっとだけ減ったので一応かけました.ちゃんとやるならカスケード分類器の設計からちゃんとやった方がいいかもしれません.

おしまい!!