旅する情報系大学院生

旅と留学とプログラミング

カメラから取得した映像に目を認識してメガネかけてくれる

ネタ的にはこの記事の続きです。
yamaguchi-1024.hatenablog.com
ただし、今回は顔認識でなく目認識です。

目認識準備

上のリンクの記事を参考にしていただいて、detector.svmを作ったのと同様の手順でeyes_detector.svmを作ります。

メガネ準備

適当なサイトからいい感じのメガネの画像を取ってきます。
f:id:yamaguchi_1024:20151115172045j:plain
画像の白い背景を透明にします。

$ mogrify 9.png -fuzz 60% -transparent white

こうなります。(実は背景はちゃんと透明になってます)
f:id:yamaguchi_1024:20151115172254p:plain

メガネ男子かわいい

このスクリプトと同じディレクトリにeyes_detector.svmを入れ、glasses/というディレクトリにメガネを入れます。
下記のサンプルでは、qを押すと終了、sで保存、3~9とw,e,r,tでかけてくれるメガネが変わるようにしてあります。

#!/usr/bin/python
# -*- coding: utf-8 -*-
import math
import os
import sys 

import dlib
from skimage import io

import cv2 

##入力している。
detector = dlib.simple_object_detector("eyes_detector.svm")
cap=cv2.VideoCapture(0)
glass=cv2.imread("glasses/2_tra.png",-1)#最初のメガネはこれ。-1をつけるとアルファチャンネルも読んでくれるみたい。

##メガネの中心の座標
center_glasses=[[214,214],[490,214]]

nyan=0 #後で使う

while(cap.isOpened()):
  ##そのままだと遅れが生じてしまうので、時間稼ぎしている。
  cap.read()
  cap.read()
  cap.read()
  cap.read()
  cap.read()
  cap.read()

  ##いっかいのwhile文の中で使い捨てるglass_useをつくる
  glass_use=glass
  height,width=glass_use.shape[:2]

  ##読み込んでいる。
  ret,frame = cap.read()
  
  ##detectしている。ついでに目の中心の座標center_eyeも求めている。
  dets = detector(frame)
  center_eye=[[0,0],[0,0]]
  i=0 
  for d in dets:
    if i<2:
      cv2.rectangle(frame, (d.left(), d.top()), (d.right(), d.bottom()), (0, 0, 255), 2)
      center_eye[i]=[math.fabs(d.right()+d.left())/2,math.fabs(d.top()+d.bottom())/2]
      i=i+1

  print "center_eye=",center_eye
  if (center_eye[0][0]!=0 and center_eye[1][0]!=0 and center_eye[0][1]!=0 and center_eye[1][1]!=0):

    ##目の中心から目の中心の距離とメガネの中心からメガネの中心の距離を求め、メガネの倍率を求めている。
    dist=math.fabs((center_eye[1][0]-center_eye[0][0])**2+(center_eye[1][1]-center_eye[0][1])**2)
    sqrt=math.sqrt(dist)
    proportion=sqrt/math.fabs(center_glasses[0][0]-center_glasses[1][0])
    #print "proportion=",proportion
    #print dist

    ##glass_useがproportionに調度良くなるようにresizeしている。
    #print "x,y=",(math.fabs(proportion*width),math.fabs(proportion*height))
    width2=int(math.fabs(proportion*width))#整数のほうが見やすく、特に問題がなかったのでキャストしてあります。
    height2=int(math.fabs(proportion*height))
    #glass_use2=cv2.createImage(cvSize(math.fabs(proportion*width),math.fabs(proportion*height)),IPL_DEPTH_8U,3)
    #cv2.resize(glass_use,glass_use2,(math.fabs(proportion*width),math.fabs(proportion*height)),0,0,0)
    glass_use2=cv2.resize(glass_use,(width2,height2))
    #cv2.imshow("glass",glass_use2)

    ##メガネをresizeしてしまったのでメガネの中心の座標もresizeします。
    center_glass_newx=center_glasses[0][0]*proportion
    center_glass_newy=center_glasses[0][1]*proportion

    ##メガネが目の位置に合うように、初期位置(0,0)からずらすglass_x,glass_yを作っている。
    if center_eye[0][0]<center_eye[1][0]:
      glass_x=center_eye[0][0]-center_glass_newx
      glass_y=center_eye[0][1]-center_glass_newy
    else:
      glass_x=center_eye[1][0]-center_glass_newx
      glass_y=center_eye[1][1]-center_glass_newy
    glass_x=math.fabs(int(glass_x))
    glass_y=math.fabs(int(glass_y))

    ##メガネの画像のアルファチャンネルだけ取り出したmaskを作っている。
    mask=glass_use2[:,:,3]
    #cv2.imshow("mask",mask)
    mask=cv2.cvtColor(mask,cv2.cv.CV_GRAY2BGR)
    mask=mask/255.0
    print "width2=",width2
    print "height2=",height2
    #print "mask=",mask
    #glass=glass[:,:,:3]

    ##frameに重ねている。
    print "glass_x=",glass_x
    print "glass_y=",glass_y
    frame[glass_y:height2+glass_y,glass_x:width2+glass_x]*=1-mask#スライス怖かったです
    frame[glass_y:height2+glass_y,glass_x:width2+glass_x]+=glass_use2[:,:,:3]*mask
    #frame[0:height2,0:width2]=glass_use2[:,:,:3]

  ##表示させている。あとはキー入力のおまけ。
  cv2.imshow("nyan",frame)
  k=cv2.waitKey(2) & 0xFF
  if k==ord('q'):
    sys.exit()
  elif k==ord('s'):
    image="gift"+str(nyan)+".png"
    cv2.imwrite(image,frame)
  elif k==ord('3'):
    glass=cv2.imread("glasses/3_tra.png",-1)
  elif k==ord('4'):
    glass=cv2.imread("glasses/4_tra.png",-1)
  elif k==ord('5'):
    glass=cv2.imread("glasses/5_tra.png",-1)
  elif k==ord('6'):
    glass=cv2.imread("glasses/6_tra.png",-1)
  elif k==ord('7'):
    glass=cv2.imread("glasses/7_tra.png",-1)
  elif k==ord('8'):
    glass=cv2.imread("glasses/8_tra.png",-1)
  elif k==ord('9'):
    glass=cv2.imread("glasses/9_tra.png",-1)
  elif k==ord('w'):
    glass=cv2.imread("glasses/10_tra.png",-1)
  elif k==ord('e'):
    glass=cv2.imread("glasses/11_tra.png",-1)
  elif k==ord('r'):
    glass=cv2.imread("glasses/13_tra.png",-1)
  elif k==ord('t'):
    glass=cv2.imread("glasses/14_tra.png",-1)

初心者なので、画像はどうしても(x,y)というイメージを持っていたのですが行列として処理しているため(列、行)みたいな感じで混乱しました。

結果

C=5でもまあまあ良く出来ています。誤判定はほぼなく、正面を向いてできるだけカメラを見て斜め前方から光を当てれば認識してくれます。
・・・ってかなりダメダメな気もしますが今回はこの程度で十分なのでよしとしました。