SNOW機能を実装してみた

# モチベーション
可愛くなりたい。ただそれだけ。

# 実装

### Input: 正面を向いた顔画像と各パーツのリサイズ比
今回は[ゆりやんレトリィバァの顔画像](https://townwork.net/magazine/life/49237/)を借用。
![yuriyan.jpg](https://qiita-image-store.s3.amazonaws.com/0/269839/e45b02e8-eb54-25fd-c46d-7c7049b02b1f.jpeg)
この画像を以下のリサイズ比で加工した結果…
* 目: 横に1.1倍、縦に1.3倍
* 鼻: 横に0.8倍、縦に1.1倍
* 口: 横に1.2倍、縦に1.1倍

### Output: 可愛くなった顔画像
こんな可愛くなりました笑
![beauty_yuriyan.jpg](https://qiita-image-store.s3.amazonaws.com/0/269839/5be75073-04ab-505b-9b32-67d5f3a144cf.jpeg)


### Step
1. ファイルからの画像取得
2. 画像中の目・鼻・口を囲む矩形領域の抽出
3. 各矩形領域に対するサイズ調整
4. サイズ調整後の各矩形領域の重ね合わせ

```python

import cv2

#Input画像とOutput画像の名前を入力
inimgname = "yuriyan.jpg"
outimgname = "beauty_"+ inimgname
#各パーツのリサイズ比を入力
eyex=1.1
eyey=1.3
nosex=0.8
nosey=1.1
mouthx=1.2
mouthy=1.1

#1. ファイルからの画像取得
img = cv2.imread(inimgname)
cv2.imshow("input",img)
cv2.waitKey(1)
# cv2.destroyAllWindows()

#2. 画像中の目・鼻・口を囲む矩形領域の抽出

 #カスケード分類器の準備
facecc_xml ="haarcascade_frontalface_alt2.xml" #cc<-cascade
lefteyecc_xml = "haarcascade_mcs_lefteye_alt.xml"
righteyecc_xml = "haarcascade_mcs_righteye_alt.xml"
nosecc_xml = "haarcascade_mcs_nose.xml"
mouthcc_xml = "haarcascade_mcs_mouth.xml"
facecc = cv2.CascadeClassifier(facecc_xml)
lefteyecc = cv2.CascadeClassifier(lefteyecc_xml)
righteyecc = cv2.CascadeClassifier(righteyecc_xml)
nosecc = cv2.CascadeClassifier(nosecc_xml)
mouthcc = cv2.CascadeClassifier(mouthcc_xml)


#顔の検出
facerects = facecc.detectMultiScale(img) #rect<-rectangle

for facerect in facerects:
    (x,y,w,h) = facerect
    face = img[y:y+h,x:x+w]
#     img = cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),1)
    
    #左目の検出
    leftupface = face[:int(len(face)/2),int(len(face[0])/2):]
    lefteyerects = lefteyecc.detectMultiScale(leftupface)
    for lefteyerect in lefteyerects:
        (lex,ley,lew,leh) = lefteyerect
#         img = cv2.rectangle(img,(x+int(len(face[0])/2)+lex,y+ley),(x+int(len(face[0])/2)+lex+lew,y+ley+leh),(0,0,255),1)
        
    #右目の検出
    rightupface = face[:int(len(face)/2),:int(len(face[0])/2)]
    righteyerects = righteyecc.detectMultiScale(rightupface)
    for righteyerect in righteyerects:
        (rex,rey,rew,reh) = righteyerect
#         img = cv2.rectangle(img,(x+rex,y+rey),(x+rex+rew,y+rey+reh),(0,0,255),1)
    
    #鼻の検出
    middleface = face[int(len(face)/4):int(len(face)*3/4),int(len(face)/4):int(len(face)*3/4)]
    noserects = nosecc.detectMultiScale(middleface)
    for noserect in noserects:
        (nx,ny,nw,nh) = noserect
#         img = cv2.rectangle(img,(int(len(face)/4)+x+nx,int(len(face)/4)+y+ny),(int(len(face)/4)+x+nx+nw,int(len(face)/4)+y+ny+nh),(0,0,255),1)
   
    #口の検出
    bottomface = face[int(len(face)/2):,int(len(face)/4):int(len(face)*3/4)]
    mouthrects = mouthcc.detectMultiScale(bottomface)
    for mouthrect in mouthrects:
        (mx,my,mw,mh) = mouthrect
#         img = cv2.rectangle(img,(int(len(face)/4)+x+mx,int(len(face)/2)+y+my),(int(len(face)/4)+x+mx+mw,int(len(face)/2)+y+my+mh),(0,0,255),1)
   

    #3. 各矩形領域に対するサイズ調整
   
    #左目を大きく
    for lefteyerect in lefteyerects:
        (lex,ley,lew,leh) = lefteyerect
        lefteye = leftupface[ley:ley+leh,lex:lex+lew]
        biglefteye = cv2.resize(lefteye,None,fx=eyex,fy=eyey)
        #4. サイズ調整後の各矩形領域の重ね合わせ
        starty = int(y+ley+len(lefteye)/2-len(biglefteye)/2)
        endy = int(y+ley+len(lefteye)/2+len(biglefteye)/2)
        startx = int(x+len(face[0])/2+lex+len(lefteye[0])/2-len(biglefteye[0])/2)
        endx = int(x+len(face[0])/2+lex+len(lefteye[0])/2+len(biglefteye[0])/2)
        img[starty:endy,startx:endx]=biglefteye
        
    #右目を大きく
    for righteyerect in righteyerects:
        (rex,rey,rew,reh) = righteyerect
        righteye = rightupface[rey:rey+reh,rex:rex+rew]
        bigrighteye = cv2.resize(righteye,None,fx=eyex,fy=eyey)
        #4. サイズ調整後の各矩形領域の重ね合わせ
        starty = int(y+rey+len(righteye)/2-len(bigrighteye)/2)
        endy = int(y+rey+len(righteye)/2+len(bigrighteye)/2)
        startx = int(x+rex+len(righteye[0])/2-len(bigrighteye[0])/2)
        endx = int(x+rex+len(righteye[0])/2+len(bigrighteye[0])/2)
        img[starty:endy,startx:endx]=bigrighteye
    
    #鼻を小さく
    for noserect in noserects:
        (nx,ny,nw,nh) = noserect
        nose = middleface[ny:ny+nh,nx:nx+nw]
        smallnose = cv2.resize(nose,None,fx=nosex,fy=nosey)
        #4. サイズ調整後の各矩形領域の重ね合わせ
        starty = int(len(face)/4+y+ny+len(nose)/2-len(smallnose)/2)
        endy = int(len(face)/4+y+ny+len(nose)/2+len(smallnose)/2)
        startx = int(len(face)/4+x+nx+len(nose[0])/2-len(smallnose[0])/2)
        endx = int(len(face)/4+x+nx+len(nose[0])/2+len(smallnose[0])/2)
        img[starty:endy,startx:endx]=smallnose
        
    #口を大きく
    for mouthrect in mouthrects:
        (mx,my,mw,mh) = mouthrect
        mouth = bottomface[my:my+mh,mx:mx+mw]
        bigmouth = cv2.resize(mouth,None,fx=mouthx,fy=mouthy)
        #4. サイズ調整後の各矩形領域の重ね合わせ
        starty = int(len(face)/2+y+my+len(mouth)/2-len(bigmouth)/2)
        endy = int(len(face)/2+y+my+len(mouth)/2+len(bigmouth)/2)
        startx = int(len(face)/4+x+mx+len(mouth[0])/2-len(bigmouth[0])/2)
        endx = int(len(face)/4+x+mx+len(mouth[0])/2+len(bigmouth[0])/2)
        img[starty:endy,startx:endx]=bigmouth
        
    cv2.imshow("output",img)
    cv2.waitKey(100)
cv2.imwrite(outimgname,img)
cv2.destroyAllWindows()
```
# まとめ
こんな感じで、大雑把に顔を可愛くすることができた。
~~ていうかこれ、SNOW機能というかプリクラ機能ですね。~~
今後の目標は、黄金比に基づいた可愛い顔や美人顔を生成したい。
あと、もっと綺麗なコード書きたい。
また改良したり新しく作ったりしたものがあれば記事を書く予定。

# 参考にしたURL
https://qiita.com/FukuharaYohei/items/ec6dce7cc5ea21a51a82
https://note.nkmk.me/python-opencv-face-detection-haar-cascade/
http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_objdetect/py_face_detection/py_face_detection.html
https://www.amazon.co.jp/OpenCV-2-%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E3%83%96%E3%83%83%E3%82%AF-OpenCV-2-2-2-3%E5%AF%BE%E5%BF%9C/dp/4839941262