【動画あり】モザイクを顔にリアルタイムでかける【Python/Opencv】

こんにちは! けい(Twitter)です.
今回は,カメラから取得した映像にリアルタイムでモザイクをかける方法についてご紹介していきたいと思います.

Youtubeやりたいけど,顔出しするのが恥ずかしいという人も使えますね(笑)

準備

顔検出をするためには,顔を認識する分類器が必要です.
しかし分類器は自作する必要がなく,公開されているのでそれを用います.

https://github.com/opencv/opencv/tree/master/data/haarcascades

上のリンクより,haarcascadesつまり分類器を任意のディレクトリに置いてください.

分類器はxmlファイルとなっています.

顔検出

ソースコード

import cv2
import numpy as np
import matplotlib.pyplot as plt

face_cascade = cv2.CascadeClassifier("DATA/haarcascades/haarcascade_frontalface_default.xml")

def detect_face(img):
    face_img = img.copy()
    face_rects = face_cascade.detectMultiScale(face_img,scaleFactor=1.2,minNeighbors=5)
    for (x,y,w,h) in face_rects:
        cv2.rectangle(face_img,(x,y),(x+w,y+h),(255,255,255),10)
    return face_img
    
cap = cv2.VideoCapture(0)
ret, frame = cap.read()
while True:
    ret, frame = cap.read()
    if ret == False:
        break
    frame = detect_face(frame)
    cv2.imshow("Video Face Detect", frame)
    k = cv2.waitKey(1)
    if k == 27:
        print(frame.shape)
        print(frame.shape[:2][::-1])
        break
cap.release()
cv2.destroyAllWindows()

結果

上のコードを実行すると,このような結果になります.
画像は,ガッキーオフィシャルファンブログより拝借しています.

コード解説

分類器オブジェクトをcv2.CascadeClassifier()関数で作ります.
中身に,先ほどのリンクでダウンロードしたxmlファイルを入力します.

detect_face関数で,受け取った画像から顔検出をして白い枠線で囲う処理をしています.
face_cascade.detectMultiScale()関数で顔を検出し,返り値として[[x,y,w,h]]が取得できます.
detectMultiScaleのscaleFactorとminNeighborsは任意なのでなくても大丈夫です.このパラメータは自分が認識したいものに合わせて,最適なものを探す必要があります.

(x,y)が顔の左上の座標で,hが高さ,wが幅となっています.

顔にモザイク

ソースコード

import cv2
import numpy as np
import matplotlib.pyplot as plt

face_cascade = cv2.CascadeClassifier("DATA/haarcascades/haarcascade_frontalface_default.xml")

def mosaic(img, ratio=0.05):
    small = cv2.resize(img, None, fx=ratio, fy=ratio, interpolation=cv2.INTER_NEAREST)
    big = cv2.resize(small, img.shape[:2][::-1], interpolation=cv2.INTER_NEAREST)
    return big

def mosaic_area(img, x, y, w, h, ratio=0.05):
    dst = img.copy()
    dst[y:y+h,x:x+w] = mosaic(dst[y:y+h,x:x+w], ratio)
    return dst

def face_mosaic(img):
    face_rects = face_cascade.detectMultiScale(img)
    for x, y, w, h in face_rects:
        img = mosaic_area(img, x, y, w, h)
    if face_rects == () or w<100 or h<100:
        img = mosaic_area(img, 0, 0, 640, 480)
    return img

cap = cv2.VideoCapture(0)
ret, frame = cap.read()
while True:
    ret, frame = cap.read()
    if ret == False:
        break
    frame = face_mosaic(frame)
    cv2.imshow("Video Face Mosaic", frame)
    k = cv2.waitKey(1)
    if k == 27:
        print(frame.shape)
        print(frame.shape[:2][::-1])
        break
cap.release()
cv2.destroyAllWindows()

結果

実行結果は以下のようになります.

これは誰か分からなくなるので,自分の顔でやってみました.
顔認識ができていないときは,画面全体がモザイクがかかるようにしてあります.
サブリミナルではないです(笑)

コード解説

顔を検出するための分類器は先ほどのコードと同様です.

モザイクをかけるために,顔検出で抜き出す四角形を,縮小して拡大するという処理を行います.これは下の図のようなイメージで,画像サイズを小さくして元の大きさに戻すと,圧縮されたような画像になり,荒くなります.
これをモザイクに使います.

関数としてはcv2.resize()で行います.resize()関数の引数,fx,fyでx軸,y軸を何倍にするかを決めることができます.よってsmallに,0.05倍した画像を格納します.
次に拡大処理を行います.これもresize()関数で行います.拡大する時は,元画像のサイズがわかっているので,何倍というように拡大するのではなく,サイズを指定します.resize()関数の第二引数に(x,y)でサイズを渡せばその大きさでしてくれます.img.shapeでは(高さ=y,幅=x,チャンネル数)の三次元の値を持つので,それを(幅=x,高さ=y)に変換して渡しています.

次に,mosaic_area()関数でモザイクをかける範囲の指定を行っています.

face_mosaic()関数で実際にモザイクをかける処理を行っています.
たまに顔が認識されなくて,顔が分かってしまう時があります.なのでもし顔認識が正しく行われなかったら,画面全体にモザイクをかける処理を行います.

それがこの文です.

if face_rects == () or w<100 or h<100:
    img = mosaic_area(img, 0, 0, 640, 480)

認識されず空あるいは,幅が100以下あるいは,高さが100以下としました.
この部分は使う環境によって様々だと思うので,もし試して認識できないなら,この部分を調整してみて下さい.

まとめ

今回は,顔にリアルタイムでモザイクをかける処理を行ってみました.
Opencvは目に見えて動くコードが作れるので,やはりおもしろいですね.

Pythonを初めてみたいという方や,ステップアップしたいという方にお勧めです.