前回の記事と前々回の記事でそれぞれ画像の中から顔を検出し、モザイクをかけることができるようになりました。
今回は画像の中から顔を検出し、検出した顔の周囲にのみモザイクをかける処理をやってみようと思います。
最終的な完成コードはこちらになります。
#写真の中の顔を検出して自動でモザイクをかける処理
import cv2
cascade_file= "haarcascade_frontalface_default.xml"
clas = cv2.CascadeClassifier(cascade_file)
img = cv2.imread("photo.jpg")
#写真の中の顔をすべて検出して周囲の四角形の座標を取得
#検出できた顔のすべてについて[四角の左上のx座標、四角の左上のy座標、幅、高さ]の数値を取得
face_list = clas.detectMultiScale(img, scaleFactor = 1.1, minSize=(30,30))
#モザイク処理
#[四角の左上のx座標、四角の左上のy座標、幅、高さ]をそれぞれ変数x, y, w, hに代入
for x, y, w, h in face_list:
face= img[y:y+h, x:x+w]
small_pic = cv2.resize(face, (8,8))
mosaic = cv2.resize(small_pic,(w,h))
img[y:y+h, x:x+w]=mosaic
cv2.imshow("photo_processed.jpg",img)
cv2.waitKey()
cv2.destroyAllWindows()
####顔の部分だけモザイク処理
前回の記事までとかぶる内容は割愛するとしまして、今回の肝は下記の部分です。
for x, y, w, h in face_list:
face= img[y:y+h, x:x+w]
small_pic = cv2.resize(face, (8,8))
mosaic = cv2.resize(small_pic ,(w,h))
img[y:y+h, x:x+w]=mosaic
for x, y, w, h in face_list:
検出した顔について①四角の左上のx座標、②四角の左上のy座標、③幅、④高さをそれぞれ変数x, y, w, hに代入しています。
face= img[y:y+h, x:x+w]
imread()で取り込んだ画像=imgの中から、座標[y:y+h, x:x+w]の部分を切り取ってface変数に代入しています。座標というと[x, y]の順番に書いてしまいそうですが、OpenCVの場合yを先に記述することも多いので注意が必要です。ついでに言いますと、y座標は数値が増えるにつれて位置的に下に下がっていくので慣れるまで混乱しました。。
座標を図式化すると、四角の左上は単純に[x, y]ですが、右下はそれぞれ幅と高さを足し合わせているので[x+w, y+h]になります。代入するときにはx座標とy座標が入れ替わります。
small_pic = cv2.resize(face, (8,8))
resize()を使って切り取った顔をいったんものすごく小さく加工してsmall_picに代入しています。resize()についてはpython + OpenCVで画像を加工①かOpenCVのチュートリアルを参照していただけたらよいかと思います。
mosaic = cv2.resize(small_pic ,(w,h))
今度は切り取った写真の顔部分を元の大きさに戻しています。画像を小さくして画素数を減らし、画素数の少なくなった画像を無理やり大きいサイズに戻すのでいわゆるモザイクの状態になるということですね。(w, h)で切り取った画像の大きさを指定しているので、これで大きさは元に戻ります。
img[y:y+h, x:x+w]=mosaic
img[y:y+h, x:x+w]、つまり写真を切り取った場所にmosaic変数=モザイクのかかった顔を当てはめています。
face= img[y:y+h, x:x+w]
small_pic = cv2.resize(face, (8,8))
mosaic = cv2.resize(small_pic ,(w,h))
img[y:y+h, x:x+w]=mosaic
これで1人分の顔を切り取る→顔の周囲だけ縮小→元の大きさに戻す→切り取った場所に戻す、という作業をやっています。whileのループ処理で検出された顔の数だけこれを繰り返すことで、検出した顔のすべてにモザイク処理が施されるという理屈です。
cv2.imshow("photo_processed.jpg",img)
cv2.waitKey()
cv2.destroyAllWindows()
加工した写真を表示して完了。ためしに妻と撮影した写真はこんな感じになりました。
#写真の中の顔を検出して自動でモザイクをかける処理
import cv2
cascade_file= "haarcascade_frontalface_default.xml"
clas = cv2.CascadeClassifier(cascade_file)
img = cv2.imread("photo.jpg")
#写真の中の顔をすべて検出して周囲の四角形の座標を取得
#検出できた顔のすべてについて[四角の左上のx座標、四角の左上のy座標、幅、高さ]の数値を取得
face_list = clas.detectMultiScale(img, scaleFactor = 1.1, minSize=(30,30))
#モザイク処理
#[四角の左上のx座標、四角の左上のy座標、幅、高さ]をそれぞれ変数x, y, w, hに代入
for x, y, w, h in face_list:
face= img[y:y+h, x:x+w]
small_pic = cv2.resize(face, (8,8))
mosaic = cv2.resize(small_pic,(w,h))
img[y:y+h, x:x+w]=mosaic
cv2.imshow("photo_processed.jpg",img)
cv2.waitKey()
cv2.destroyAllWindows()
####平滑化(ぼかし)もやってみた
さらに、前回の記事で触れたblur()関数を使って画像をぼかす効果も使ってみました。モザイクの代わりに顔部分をぼかしてみます。
#写真の中の顔を検出して自動で「ぼかし効果」をかける処理
import cv2
cascade_file= "cascade_classification.xml"
clas = cv2.CascadeClassifier(cascade_file)
img = cv2.imread("photo.jpg")
face_list = clas.detectMultiScale(img, scaleFactor = 1.1, minSize=(30,30))
for x, y, w, h in face_list:
#平均化(ぼかし)処理
face= img[y:y+h, x:x+w]
img_blur= cv2.blur(face,(55,55))
img[y:y+h, x:x+w]=img_blur
cv2.imshow("photo_processed.jpg",img)
cv2.waitKey()
cv2.destroyAllWindows()
ちょっとモザイクっぽい感じもしますが、、、平滑化については別のアルゴリズムで動く関数もあるようなので今後勉強しようと思います。
ちなみに、「モザイクやぼかしのかかった顔部分を切り取った場所に戻す」というのが理屈ではいまいちピンとこなかったのでテストしてみました。
下のカーテンの適当な部分を切り取って、上の風景写真の一部にはめ込んでみます。
import cv2
view = cv2.imread("view.jpg")
curtain = cv2.imread("curtain.jpg")
#カーテンの画像を一部切り取る
curtain_rectangle = curtain[100:300, 200:400]
#切り取ったカーテンの画像を風景画像に挿入
view[100:300, 100:300] = curtain_rectangle
cv2.imwrite("view_curtain.jpg",view)
cv2.destroyAllWindows()
個人的には、この結果を見てようやく理解できました。
今後は、動画の顔を検出してモザイク処理をやってみたいなと思っています。