Pythonプログラムで動画に写る人の顔にマスクを着ける方法
動画に写る人の顔にマスクを合成して、マスクを着けているように見えるPythonプログラムを紹介します。
黒色背景のマスクは私がスマホで撮った写真になります。
動画は「Pexels」さんの動画を使わさせてもらいました。
この投稿以外に画像合成プログラムを紹介している投稿がこちら↓になります。
「人の顔にマスクを合成するPythonプログラムを紹介します」
「画像合成を交えて色を数値で確認する方法 Python」
「Pythonを使った画像合成をColaboratoryで実行」
「画像合成で使う緑色背景の色を変更 Python」
「画像合成で使う画像の色を数値で確認する方法」
目次
・画像合成で使ったマスクの画像
・マスクを合成する前の動画
・動画に写る人の顔にマスクを合成するPythonプログラム
・動画に写る人の顔にマスクを合成して、マスクを着けているように見える様子
・1月28日 修正
画像合成で使ったマスクの画像

このマスク画像について
顔が大きい、小さいでマスク画像のサイズを変えて合成していたら、汎用性がないと思ったので、顔が大きい、小さい関係なしにちょうどいいくらいのサイズにしています。
マスクを合成する前の動画
動画の読み込みと表示はこちら→「Python OpenCVを使った動画の読み込みと表示」の投稿で紹介したプログラムを使いました。
動画に写る人の顔にマスクを合成するPythonプログラム
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
import cv2 def mask_gousei(img,mask):# global half,d1,d2,d3,d4#globalにしないと、顔、目の検出ができないときに34行目のimg[d1:d2,d3:d4]=halfが実行できないからエラーになる img_gray=cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)#グレイ画像 images = face_cascade.detectMultiScale(img_gray)#顔の検出 for x,y,w,h in images:#顔検出した座標の数繰り返す face=img[y:y+h,x:x+w]#顔切り取る face_gray=img_gray[y:y+h,x:x+w]#顔のグレイ画像 eye = eye_cascade.detectMultiScale(face_gray)#グレーにした顔から目を検出 if len(eye) != 0:#目を検出できたら変数eyeに座標のデータが入る。 half=img[y+h//2:y+h,x:x+w]#顔の下半分の画像を変数halfに入れる d1=y+h//2#人画像に合成したマスクを埋め込むときに使う d2=y+h d3=x d4=x+w height, width = half.shape[:2]#顔の下半分の高さ、幅を取り出す mask=cv2.resize(mask,(width,height))#顔の下半分とマスクの大きさを合わせる for x in range(height):#マスクの背景の色を取り除いて、顔の下半分にマスクを合成 for y in range(width): b, g, r = mask[x, y]#色を取り出す if (b >= 0 and b <= 160) and (g >= 0 and g <= 160) and (r >= 0 and r <= 160):#黒色背景を除く continue half[x, y]=mask[x, y]#合成 img[d1:d2,d3:d4]=half#人画像にマスクを合成した画像を埋め込む return img face_cascade=cv2.CascadeClassifier("haarcascade_frontalface_alt2.xml")#顔を検出するカスケードファイル eye_cascade = cv2.CascadeClassifier("haarcascade_eye.xml")#目を検出するカスケードファイル mv= cv2.VideoCapture('Pexels Videos 1608393.mp4')#動画の読み込み mask_img=cv2.imread("DSC_1210 (5).jpg")#マスク画像 frame_count =int(mv.get(cv2.CAP_PROP_FRAME_COUNT))#動画を画像にしたときの総枚数 for i in range(frame_count):#総枚数の数繰り返す for i in range(5):#コマ飛ばしで読み込まなとスローに見えます。 ch,frame=mv.read()#1フレーム(画像)ずつ取り出す if ch==True:#画像を取り出すことができたら、下の処理をします frame=cv2.resize(frame,(600,400))#表示する大きさを調整 mask_gousei_show=mask_gousei(frame,mask_img)#マスクを合成する自作関数に、人の画像とマスクの画像を渡す cv2.imshow('movie', mask_gousei_show)#表示 k=cv2.waitKey(1)#1ミリ秒間フレームを表示 if k==27:#ESCキーを押したとき終了 break mv.release() cv2.destroyAllWindows() |
上のプログラムはこちら→「人の顔にマスクを合成するPythonプログラムを紹介します」の投稿で紹介したプログラムとあまり変わらないので、追加または変更した箇所だけプログラムの説明をします。
41行目はcv2.VideoCaptureで動画を読み込んでいます。
44行目は読み込んだ動画を画像にしたときの総枚数を求めています。
そして、その総枚数を46行目のfor文で繰り返す回数にしています。
47行目はfor i in range(5):でコマ飛ばしで表示しています。コマ飛ばしをしないと処理が遅いので、スローに見えます。
for文を使ったコマ飛ばしについて投稿しているのがこちら→「for文を使って動画をコマ飛ばしで表示、早送り風」になります。
動画に写る人の顔にマスクを合成して、マスクを着けているように見える様子
1月28日 修正 動画に写る人の顔にマスクを合成するPythonプログラム
上のプログラムは動画を読み込む最初の1フレーム(画像)に顔と目を検出できなかった場合、エラーがでてしまうので、
顔と目を検出できなかった場合を考慮したプログラムを下に載せておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
import cv2 def mask_gousei(img,mask):# half=[] img_gray=cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)#グレイ画像 images = face_cascade.detectMultiScale(img_gray)#顔の検出 if len(images) != 0:#顔を検出できた場合 for x,y,w,h in images:#顔検出した座標の数繰り返す face=img[y:y+h,x:x+w]#顔切り取る face_gray=img_gray[y:y+h,x:x+w]#顔のグレイ画像 eye = eye_cascade.detectMultiScale(face_gray)#グレーにした顔から目を検出 if len(eye) != 0:#目を検出できた場合 half=img[y+h//2:y+h,x:x+w]#顔の下半分の画像を変数halfに入れる d1=y+h//2#人画像に合成したマスクを埋め込むときに使う d2=y+h d3=x d4=x+w else:#for文の繰り返しがすべて終わるまで、continue continue if len(half) != 0:#顔の検出と目の検出、両方ができた場合 height, width = half.shape[:2]#顔の下半分の高さ、幅を取り出す mask=cv2.resize(mask,(width,height))#顔の下半分とマスクの大きさを合わせる for x in range(height):#マスクの背景の色を取り除いて、顔の下半分にマスクを合成 for y in range(width): b, g, r = mask[x, y]#色を取り出す if (b >= 0 and b <= 160) and (g >= 0 and g <= 160) and (r >= 0 and r <= 160):#黒色背景を除く continue half[x, y]=mask[x, y]#合成 img[d1:d2,d3:d4]=half#人画像にマスクを合成した画像を埋め込む return img else:#顔は検出できたが、目を検出できなかった場合 読み込んだ画像をそのまま返す return img else:#顔を検出できなかった場合 読み込んだ画像をそのまま返す return img face_cascade=cv2.CascadeClassifier("haarcascade_frontalface_alt2.xml")#顔を検出するカスケードファイル eye_cascade = cv2.CascadeClassifier("haarcascade_eye.xml")#目を検出するカスケードファイル mv= cv2.VideoCapture('Pexels Videos 1608393.mp4')#動画の読み込み mask_img=cv2.imread("DSC_1210 (5).jpg")#マスク画像 frame_count =int(mv.get(cv2.CAP_PROP_FRAME_COUNT))#動画を画像にしたときの総枚数 for i in range(frame_count):#総枚数の数繰り返す for i in range(5):#コマ飛ばしで読み込まなとスローに見えます。 ch,frame=mv.read()#1フレーム(画像)ずつ取り出す if ch==True:#画像を取り出すことができたら、下の処理をします frame=cv2.resize(frame,(600,400))#表示する大きさを調整 mask_gousei_show=mask_gousei(frame,mask_img)#マスクを合成する自作関数に、人の画像とマスクの画像を渡す cv2.imshow('movie', mask_gousei_show)#表示 k=cv2.waitKey(1)#1ミリ秒間フレームを表示 if k==27:#ESCキーを押したとき終了 break mv.release() cv2.destroyAllWindows() |
顔と目を検出できなかった場合は引数に渡した画像をそのままreturnで呼び出し元に返すようにしているので、マスクを合成していない画像が表示されます。