LoginSignup
12
14

More than 5 years have passed since last update.

SNOW機能を実装してみた

Last updated at Posted at 2018-12-08

モチベーション

可愛くなりたい。ただそれだけ。

実装

Input: 正面を向いた顔画像と各パーツのリサイズ比

今回はゆりやんレトリィバァの顔画像を借用。
yuriyan.jpg
この画像を以下のリサイズ比で加工した結果…
* 目: 横に1.1倍、縦に1.3倍
* 鼻: 横に0.8倍、縦に1.1倍
* 口: 横に1.2倍、縦に1.1倍

Output: 可愛くなった顔画像

こんな可愛くなりました笑
beauty_yuriyan.jpg

Step

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

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

12
14
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
14