Python & OpenCVでやってみました
OpenCVでカメラ画像を取り込み、顔検出してその場所と大きさに調整して他の画像を載せた動画を作ります。
基本的なところは下の参考サイト組み合わせです。
まず、動画画像から顔を抽出するには、OpenCVで行います。
GitHubから好きなモデルを選択しますが、ここでは正面を向いた顔の抽出を行いました。
https://github.com/opencv/opencv/tree/master/data/haarcascades
cascade_path = "haarcascade_frontalface_default.xml" # カスケード分類器の特徴量を取得する cascade = cv2.CascadeClassifier(cascade_path)
カメラで動画を取得します。
引数は複数のカメラ(USBカメラなど)があるときは1とか2にします。
ここではPCの内蔵カメラ(Web会議などに使うインカメラ)を使うので0を指定します。
cap = cv2.VideoCapture(0)
上書きする画像を取得します。
ここでは、画像背景を透過にしたいのでPNGで取得します。
アイコン画像を加工して使用しました。
faceImg = cv2.imread('s51517765.png', cv2.IMREAD_UNCHANGED)
動画から顔を認識します。
認識出来たら、少し大きめ(130%)に画像を調整します。
顔認識と同じサイズでは頭や顎が出てしまうためです。
また、拡大した分座標を中心が一致するようにoffsetします。
facerect = cascade.detectMultiScale(image_gray, scaleFactor=1.1, minNeighbors=2, minSize=(30, 30)) if len(facerect) > 0: faceImg = cv2.resize(faceImg, ((int)(rect[2]*1.3), (int)(rect[3]*1.3)), cv2.IMREAD_UNCHANGED) rect[0] -= rect[2]*0.15 # x_offset rect[1] -= rect[3]*0.15 # y_offset
透過処理をして画像を重ねます。
frame[rect[1]:rect[1]+faceImg.shape[0],rect[0]:rect[0]+faceImg.shape[1]] = frame[rect[1]:rect[1]+faceImg.shape[0], rect[0]:rect[0]+faceImg.shape[1]] * (1 - faceImg[:, :, 3:] / 255) + faceImg[:, :, :3] * (faceImg[:, :, 3:] / 255)
動画を表示します。
cv2.imshow('cv2', frame)
1st tryがこんな感じですが、首元や背景に余計な顔誤認識があります。
Python&OpenCVで顔を隠すのやってみた。 pic.twitter.com/VpHU10PRfa
— とりてん (@s51517765) 2021年8月21日
そこで、少し工夫をしました。
顔の認識サイズを移動平均で取得し、これに対して大きく外れている場合は誤認識とみなしてスキップします。
顔認識の縦横サイズを両方採用しましたが、ほぼ同じサイズなのでどちらかでも大丈夫かもしれません。
ここでは、移動平均の95%以下をスキップするようにしました。
aveSize = aveSize*0.8+rect[2]*0.1+rect[3]*0.1 thresh = aveSize*0.95 # 移動平均の95%以上を閾値 if rect[2] < thresh or rect[3] < thresh: break
安定するようになりました。
Python&OpenCVで顔を隠すのやってみた(Ver.2)。
— とりてん (@s51517765) 2021年8月21日
小さい顔が入り込んでしまう(間違った認識がされる)のを改良。 pic.twitter.com/nEHyBvVpQY
まとめ
別件でPythonでカメラを扱う案件があってカメラの使い方を調べたので、テレビでたまに見る似顔絵で顔を隠すやつをやってみました。
Zoomミーティングとかで使ってみようかな。
ソースコード
#!/usr/bin/env python # -*- coding: utf-8 -*- import cv2 import os # https://github.com/opencv/opencv/tree/master/data/haarcascades cascade_path = "haarcascade_frontalface_default.xml" # カスケード分類器の特徴量を取得する cascade = cv2.CascadeClassifier(cascade_path) def VideoCapt(): cap = cv2.VideoCapture(0) # カメラが複数あるときは 0を1や2にする aveSize = 0 count = 0 while cap.isOpened(): try: # マネしたい人はイラストやのぶたさんでもつかってね # https://www.irasutoya.com/2017/09/blog-post_966.html faceImg = cv2.imread('s51517765.png', cv2.IMREAD_UNCHANGED) _, frame = cap.read() # グレースケール変換 image_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) facerect = cascade.detectMultiScale( image_gray, scaleFactor=1.1, minNeighbors=2, minSize=(30, 30)) # print(facerect) color = (255, 255, 255) # 白 # 検出した場合 if len(facerect) > 0: for rect in facerect: count += 1 if count < 4: aveSize += (rect[2]+rect[3])/2 break elif count == 4: aveSize += (rect[2]+rect[3])/2 aveSize /= 5 else: aveSize = aveSize*0.8+rect[2]*0.1+rect[3]*0.1 thresh = aveSize*0.95 # 移動平均の95%以上を閾値 if rect[2] < thresh or rect[3] < thresh: break # 検出した顔を囲む矩形の作成 #cv2.rectangle(frame, tuple(rect[0:2]), tuple( rect[0:2]+rect[2:4]), color, thickness=2) faceImg = cv2.resize( faceImg, ((int)(rect[2]*1.3), (int)(rect[3]*1.3)), cv2.IMREAD_UNCHANGED) rect[0] -= rect[2]*0.15 # x_offset rect[1] -= rect[3]*0.15 # y_offset frame[rect[1]:rect[1]+faceImg.shape[0], rect[0]:rect[0]+faceImg.shape[1]] = frame[rect[1]:rect[1]+faceImg.shape[0], rect[0]:rect[0]+faceImg.shape[1]] * (1 - faceImg[:, :, 3:] / 255) + \ faceImg[:, :, :3] * (faceImg[:, :, 3:] / 255) cv2.imshow('cv2', frame) except: pass if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() print("Camera is closed.") print('Python Active: ', os.getcwd()) path = os.path.dirname(__file__) print('Script location: ', path) os.chdir(path) if __name__ == '__main__': VideoCapt()