Elsaの技術日記(徒然なるままに)

主に自分で作ったアプリとかの報告・日記を記載

MENU

OpenCVでの顔へのモザイク処理

最近、久々に触ったOpenCVにハマっていますww。
f:id:Elsammit:20200916224647p:plain

顔認識ができたので顔にモザイクでもつけてみようかな?と思い実施。
合わせて目にのみもモザイクを付けてみたのでそちらも報告!!
使用しているプログラミング言語pythonです。


■モザイク処理方法

特定領域のモザイク処理はこちらで実行できます。

import cv2

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

srcにはモザイクをかけたい画像領域を渡します。
渡されたsrcに対して、ratioの値でモザイク処理を行います。
ratioの値は高いほどモザイクが細かく、低いほどモザイクが荒くなります。


例えば顔の領域全体にモザイク処理を行う場合にはこんな感じで実行すればよいです。

import cv2

ratio = 0.08

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

cascade_path = "haarcascade_frontalface_default.xml"

img1 = cv2.imread('lena.jpg')
img1 = cv2.resize(img1,(500,500))

# RGBからGray画像に変換.
gry_img = cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)
    
# カスケードファイル読み込み.
cascade = cv2.CascadeClassifier(cascade_path)

facerect = cascade.detectMultiScale(gry_img,scaleFactor=1.5,minNeighbors=2,minSize=(30,30))

if len(facerect) > 0:    
    x = facerect[0][0]
    y = facerect[0][1]
    w = facerect[0][2]
    h = facerect[0][3]


    face = img1[y:y+h,x:x+w]
    msc = mosaic(face)
    img1[y:y+h,x:x+w] = msc

# フレームを表示する
cv2.imshow("test", img1)
    

# escキーが押されたらループ終了
cv2.waitKey(0)

モザイク処理を行う上で重要なのはこちらで、

facerect = cascade.detectMultiScale(gry_img,scaleFactor=1.5,minNeighbors=2,minSize=(30,30))
if len(facerect) > 0:    
    x = facerect[0][0]
    y = facerect[0][1]
    w = facerect[0][2]
    h = facerect[0][3]

    face = img1[y:y+h,x:x+w]
    msc = mosaic(face)
    img1[y:y+h,x:x+w] = msc

facerectで顔だと判定した領域を取得し、
顔判定した始点をx,yに格納すると同時に画像の幅や高さをw,hに格納。
顔領域のみをface変数に格納し、先ほどのmosaic関数に突っ込むとモザイクした結果が得られます。
最後に、モザイク結果を元画像に代入します。

ratioを切り替えた時のモザイク画像はこちらになります。
【元画像】
f:id:Elsammit:20200919164938j:plain


【ratio = 0.2】
f:id:Elsammit:20200919165126p:plain


【ratio = 0.08】
f:id:Elsammit:20200919165153p:plain

【ratio = 0.03】
f:id:Elsammit:20200919165217p:plain


ついでに、、、
ratioを小さくしすぎると荒いモザイクが作成できずエラーとなります。

error: (-215:Assertion failed) !dsize.empty() in function 'cv::resize'


■目にだけモザイク処理

OpenCVには顔のみでなく目のみを検知するカスケードファイルが存在します。
haarcascade_eye.xml
です。
目に対してのみモザイク処理を実施する場合には、

import cv2

ratio = 0.08

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

cascade_path = "haarcascade_frontalface_default.xml"

img1 = cv2.imread('lena.jpg')
img1 = cv2.resize(img1,(500,500))

# RGBからGray画像に変換.
gry_img = cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)
    
# カスケードファイル読み込み.
cascade = cv2.CascadeClassifier(cascade_path)

facerect = cascade.detectMultiScale(gry_img,scaleFactor=1.5,minNeighbors=2,minSize=(30,30))

if len(eyerect) > 0:
    ex_left = eyerect[0][0]
    ey_left = eyerect[0][1]
    ew_left = eyerect[0][2]
    eh_left = eyerect[0][3]
    ex_right = eyerect[1][0]
    ey_right = eyerect[1][1]
    ew_right = eyerect[1][2]
    eh_right = eyerect[1][3]

    eyes_left = img1[ey_left:ey_left+eh_left,ex_left:ex_right+ew_left]
    msc_left = mosaic(eyes_left)

    eyes_right = img1[ey_right:ey_right+eh_right,ex_right:ex_right+ew_right]
    msc_right = mosaic(eyes_right)

    img1[ey_left:ey_left+eh_left,ex_left:ex_right+ew_left] = msc_left

# フレームを表示する
cv2.imshow("test", img1)
    

# escキーが押されたらループ終了
cv2.waitKey(0)

とすればOKです。

下記パラメータの意味ですが、

    ex_left = eyerect[0][0]
    ey_left = eyerect[0][1]
    ew_left = eyerect[0][2]
    eh_left = eyerect[0][3]
    ex_right = eyerect[1][0]
    ey_right = eyerect[1][1]
    ew_right = eyerect[1][2]
    eh_right = eyerect[1][3]

こちらのような関係となっております。
f:id:Elsammit:20200919171200p:plain

上記を実行させた結果はこんな感じになっており、
目の領域を長方形でモザイク処理しております。
f:id:Elsammit:20200919171433p:plain

■カメラモジュールからストリーミング配信された画像に対するモザイク処理

今までは画像に対してモザイク処理を行ってきましたが、今度はストリーミング配信された画像に対するモザイク処理です。
ソースはこんな感じ。

import cv2

ratio = 0.05

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

# ストリーミング配信されるリンクを指定.
cap = cv2.VideoCapture("リンクパス")
cascade_path = "haarcascade_frontalface_default.xml"

img = cv2.imread('hat.png', cv2.COLOR_BGR2RGB)
img = cv2.resize(img,(100,100))

# リンクが開けなかった場合
if not cap.isOpened():
    print("Cannot open a video capture.")
    exit(-1)


while True:
    # 送られてくる映像のフレームを取得.
    ret, frame = cap.read()

    frame_mosaic = mosaic(frame)

    # RGBからGray画像に変換.
    gry_mosaic = cv2.cvtColor(frame_mosaic,cv2.COLOR_BGR2GRAY)
    gry_img = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
    
    # カスケードファイル読み込み.
    cascade = cv2.CascadeClassifier(cascade_path)

    facerect = cascade.detectMultiScale(gry_img,scaleFactor=1.5,minNeighbors=2,minSize=(30,30))

    # 顔認証部分を囲う色の指定.
    rectange_color = (255,0,0)

    # 顔認識部分を四角で囲う.
    if len(facerect) > 0:    
        x = facerect[0][0]
        y = facerect[0][1]
        w = facerect[0][2]
        h = facerect[0][3]

        face = frame[y:y+h,x:x+w]
        msc = mosaic(face)
        frame[y:y+h,x:x+w] = msc
        
    # もしフレームが取得できなかった場合
    if not ret:
        continue

    # フレームを表示する
    cv2.imshow("EV3 Streaming", gry_mosaic)
    cv2.imshow("test", frame)
    

    # escキーが押されたらループ終了
    k = cv2.waitKey(1)

    if k == 27:
        break

# キャプチャーの開放&ウィンドウ閉じる
cap.release()
cv2.destroyAllWindows()

実行した結果はこんな感じ!!
f:id:Elsammit:20200919172458g:plain


ちゃんと顔領域のみがモザイクかかってますね。
精度もまぁ、こんなもんかな?って感じです。

■最後に

結構しっかりとモザイク処理が出来ていて結構楽しかったです!!
AIを用いるとモザイク処理した結果を復元できるようなので、今度調べてみようかな??
写真のモザイクを除去して“ほぼ”復元させるAIが登場 | Ledge.ai