OpenCVを使ってリアルタイムの顔にイラストを合成するよ

おはよう。:relaxed:だよ。

今日は前から気になっていたopenCVのお勉強です。
年末、みんな忙しいよね？　
集中している時に声かけられて思考が途切れると思わず:expressionless:ってなっちゃうよね？
リアルタイムに顔を認識して、愛想良さげなイラストを合成しちゃうよ。


##OpenCVって何？
Qiitaにたくさんわかりやすい記事があるので、ここではざくっと。
画像や動画を処理するのに便利なライブラリだよ。画像の表示や編集はもちろん、物体検知やテンプレートマッチングなども得意だよ。個人的にはpythonで画像を触りたい=openCVを使おう！な感じで、画像処理の権威だと思ってます。
今回は顔を認識するために、デフォルトで用意されているHaar特徴ベースのカスケード型分類器をつかうよ。
もちろん自分でカスケード型の分類器を作ることもできるんだけど、今絶賛頓挫中だよ。

##ざくっと、Haar特徴ベースのカスケード型分類器って？
:relaxed:を:relaxed:だと判断するにはどうしたら良いかな？
「ほっぺが赤いなー」「まゆが下がっているなー」「口元は笑っているなー」「おっとりした感じだなー」って特徴を見るよね。
カスケード型の分類器も同じように、識別したい物（正例）と識別したい物ではない物（負例）から、識別したい物の特徴データを学習して作られるよ。
Haar特徴ベースでは特徴データを画像の明暗差で測るよ。目の部分よりも鼻の部分の方が明るいなーとかかな。


では、早速用意されているカスケード型分類器を使ってみるよ。


## まずは画像を読み込んで、どんな風に判別されるか見てみよう。
公式のチュートリアルにしたがって、顔の判別と目の判別をやってみるよ。


```python:checkPhoto.py
import cv2
import sys

args = sys.argv
targetImage = args[1]

# 分類器の指定するよ

face_cascade_file = "haarcascade_frontalface_alt2.xml"
eyes_cascade_file = "haarcascade_eye.xml"
face_cascade      = cv2.CascadeClassifier(face_cascade_file)
eyes_cascade      = cv2.CascadeClassifier(eyes_cascade_file)

#判別したい画像を読み込み
img = cv2.imread(targetImage)


##グレースケールに変換
##画像を判別する際に情報量が多すぎるとメモリを圧迫するから、
##カラーからグレーに変えて、情報を排除することで計算量を抑えている。

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

#顔を検知
faces = face_cascade.detectMultiScale(gray)
for (x,y,w,h) in faces:
  # 検知した顔を囲む
  center = (x + w//2, y + h//2)
  cv2.ellipse(img, center, (w//2, h//2), 0, 0, 360, (255, 0, 255), 4)
  # グレー
  faceROI_gray = gray[y:y+h, x:x+w]
  # カラー
  faceROI = img[y:y+h, x:x+w]

  ##目を検知
  eyes = eyes_cascade.detectMultiScale(faceROI_gray)
  for (x2,y2,w2,h2) in eyes:
    eye_center = (x + x2 + w2//2, y + y2 + h2//2)
    radius = int(round((w2 + h2)*0.25))
    cv2.circle(img, eye_center, radius, (255, 0, 0 ), 4)

cv2.imshow('img',img)

key = cv2.waitKey(0)
cv2.imwrite('mark_new.jpeg', img);
cv2.destroyAllWindows()

```


これで、こちらの画像を読み込むと・・

![ariyoshi.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482103/8ee82158-353e-cedc-b62f-1de3087b210c.jpeg)




![mark_new.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482103/916a6e3e-e43c-b479-93c1-10a5c1483d45.jpeg)

目が認知されていない・・？
あと小宮さんも認知されていない・・？

と思って人数を絞って顔がはっきり映った画像で試してみたよ。


![mark_new.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482103/5faac789-fa30-ba71-9f7d-a6c9b33029a2.jpeg)

([かが屋 賀屋さんのTwitter](https://twitter.com/kagayahirai?lang=ja) よりお借りしました。)

うん、判別されているね。



##カメラを使ってリアルタイムで判別しよう。
これまでは写真を使って判別してみたけれど、今度はカメラを使って映像を読み込みながら処理してみよう。
imreadではなく、VideoCaptureでカメラからの画像を取り込んで、
読み込んだ画像に対し、判別を行うところがさっきと違うよ。

今回はループ処理の中で、
1. 印をつける。
2. 画像を貼り付ける
の２パターンをやってみたよ。試してみるときはどちらかをコメントアウトして使ってね。



```python:faceChange.py

import cv2

## 顔を変えるための関数だよ
def effect_face_func(img, rect):
    ###　合成したい画像をセットしてね
    image_file  = "images/effect.png"
    marge_image = cv2.imread(image_file)

    marge_gray = cv2.cvtColor(marge_image, cv2.COLOR_BGR2GRAY)

    (x1, y1, x2, y2) = rect

    w = x2 - x1
    h = y2 - y1

    img_face = cv2.resize(marge_gray, (w, h))

    img2 = img.copy()
    img2[y1:y2, x1:x2]      = img_face
    return img2



# 定数定義
ESC_KEY     = 27     # Escキー
INTERVAL    = 33     # 待ち時間
FRAME_RATE  = 30  # fps
WINDOW_NAME = "faceWindow"
DEVICE_ID   = 0     # カメラのデバイスIDだよ。Macに内蔵のカメラを使っているよ。

# 分類器の指定するよ
cascade_file = "haarcascade_frontalface_default.xml"
face_cascade = cv2.CascadeClassifier(cascade_file)


# カメラから映像を取得するよ
cap = cv2.VideoCapture(DEVICE_ID)

# 初期フレームの読込
end_flag, c_frame = cap.read()
height, width, channels = c_frame.shape

# 画像を表示するウィンドウを準備
cv2.namedWindow(WINDOW_NAME)


# 変換処理ループ
while end_flag == True:

    # 画像の取得と顔の検出
    img      = c_frame
    gray     = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    faces    = face_cascade.detectMultiScale(gray, minSize=(100, 100))

    # 検出した顔の座標を使うよ
    for (x, y, w, h) in faces:
        ## 1. 顔に印をつける場合 ##
        color = (0, 0, 225)
        pen_w = 3
        cv2.rectangle(gray, (x, y), (x+w, y+h), color, thickness = pen_w)

        ## 2.顔にイメージををつける場合 ##
        gray = effect_face_func(gray, (x+10, y+10, x+w+10, y+h+10))

    # フレームに表示をする
    cv2.imshow(WINDOW_NAME, gray)

    # Escキーで終了
    key = cv2.waitKey(INTERVAL)
    if key == ESC_KEY:
        break

    # 次のフレーム読み込み
    end_flag, c_frame = cap.read()

# 終了処理
cv2.destroyAllWindows()
cap.release()



```

例えば、２で猫ちゃんの画像を組み合わせるとー
![neko (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482103/c410fc17-9e46-cccf-d10f-d7b6f8eae853.gif)


ほい、こんな感じ。髪ボサボサだね。
日常生活でもこんな感じでニッコリ笑った顔を貼り付けておいてほしいね:relaxed:



### まとめ
openCVを使って顔認識したよ。
画像の処理で実装の幅が広がって、面白いこと、色々できそうだよね。
カスケードの自作もぜひやりたいけれど、なかなか難航中＆他にもやりたいことがたくさんあるよーということでお正月に期待！


### 参考にさせていただいたサイト　

[openCV チュートリアル：動画を扱う](http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_gui/py_video_display/py_video_display.html)
[openCV チュートリアル：画像を扱う](http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_gui/py_image_display/py_image_display.html)
[カスケード型分類器について](http://opencv.jp/opencv-2svn/py/objdetect_cascade_classification.html)