ネスカフェ バリスタ にラズベリーパイ を接続してUSBカメラからキャプチャーした画像をOpenCV を使ったPython プログラムで顔認識させ、人の顔が認識されたら、”コーヒー飲まない?”と、お誘いするバリスタ 君にしてみた。
VIDEO youtu.be
バリスタ とラズパイ以外の使用機材は、以下のようなもの
USBカメラ:iBUFFALO BSW20KM15
WiFi アダプタ:WLI-UC-GNU2
セルフパワーのUSBハブ
ラズベリーパイ には、Python 用のOpenCV をインストール。
以下のサイトを参考にさせていただいた。
Raspberry Piで画像処理ライブラリ”OpenCV”使って”顔認識”試してみた: EeePCの軌跡
上記の参考サイトを見ながら、OpenCV とサンプルをインストール
$ sudo apt-get install libopencv-dev
$ sudo apt-get install python-opencv サンプルは、上記参考サイトにあるように、face.xml とfacedetect.py をコピーし、まずは、顔認識をテストしてみる。
が、以前にも書いたが、これまで使っていたtightVNCserver では、うまく表示させることができなかったので、x11VNC というソフトをインストール。 ( $ sudo apt-get install x11vnc )
が、そのままでは、解像度が低く、VNC で画面の一部しか表示されなかったので、
$ sudo nano /boot/config.txt
以下の部分を修正(#をはずす)
#framebuffer_width = 1280
#framebuffer_height = 720 と、設定を少しいじくる。
これで、なんとか顔認識ができるようになった。
いろいろなサイトで紹介されているようにモニター画面に映った顔の部分に赤い四角で枠が入るあれ、です。
このサンプルをもとに、顔が認識されたら、シリアル通信でAquesTalkPicoという音声合成 ICにメッセージを送ってお話させます。
シリアル通信で発声させるのは、前回
ラズベリーパイからシリアル通信で音声合成ICを発声させてみた - 満腹居士 七転八倒の記
でも書いたが、ラズパイのPin#01, 06, 08 からICにつなぐ。
プログラムは、facedetect.py のオリジナルの部分も含めて、以下のようにした。
"""
This program is demonstration for face and object detection using haar-like features.
The program finds faces in a camera image or video stream and displays a red box around them.
Original C implementation by: ?
Python implementation by: Roman Stanchak, James Bowman
"""
import sys
import cv2.cv as cv
from optparse import OptionParser
import time
import serial
min_size = (20 , 20 )
image_scale = 2
haar_scale = 1.2
min_neighbors = 2
haar_flags = 0
count_facecheck = 0
con=serial.Serial('/dev/ttyAMA0' , 9600 , timeout=10 )
def detect_and_draw (img, cascade):
global count_facecheck
gray = cv.CreateImage((img.width,img.height), 8 , 1 )
small_img = cv.CreateImage((cv.Round(img.width / image_scale),
cv.Round (img.height / image_scale)), 8 , 1 )
cv.CvtColor(img, gray, cv.CV_BGR2GRAY)
cv.Resize(gray, small_img, cv.CV_INTER_LINEAR)
cv.EqualizeHist(small_img, small_img)
if (cascade):
t = cv.GetTickCount()
faces = cv.HaarDetectObjects(small_img, cascade, cv.CreateMemStorage(0 ),
haar_scale, min_neighbors, haar_flags, min_size)
t = cv.GetTickCount() - t
if faces:
for ((x, y, w, h), n) in faces:
pt1 = (int (x * image_scale), int (y * image_scale))
pt2 = (int ((x + w) * image_scale), int ((y + h) * image_scale))
cv.Rectangle(img, pt1, pt2, cv.RGB(255 , 0 , 0 ), 3 , 8 , 0 )
print "detection time = %gms" % (t/(cv.GetTickFrequency()*1000. ))
print count_facecheck
count_facecheck += 1
if count_facecheck == 5 :
con.write("? ko-hi'- nomima/se'nka? \r " )
elif count_facecheck == 50 :
con.write("? ne'e ne'e, ko-hi'- nomo'uyo \r " )
elif count_facecheck == 100 :
con.write("ko-hi'- noma'naika na'- \r " )
elif count_facecheck == 300 :
count_facecheck = 0
else :
count_facecheck = 0
cv.ShowImage("result" , img)
if __name__ == '__main__' :
parser = OptionParser(usage = "usage: %prog [options] [filename|camera_index]" )
parser.add_option("-c" , "--cascade" , action="store" , dest="cascade" , type ="str" , help ="Haar cascade file, default %default" , default = "../data/haarcascades/haarcascade_frontalface_alt.xml" )
(options, args) = parser.parse_args()
cascade = cv.Load(options.cascade)
if len (args) != 1 :
parser.print_help()
sys.exit(1 )
input_name = args[0 ]
if input_name.isdigit():
capture = cv.CreateCameraCapture(int (input_name))
else :
capture = None
cv.NamedWindow("result" , 1 )
width = 320
height = 240
if width is None :
width = int (cv.GetCaptureProperty(capture, cv.CV_CAP_PROP_FRAME_WIDTH))
else :
cv.SetCaptureProperty(capture,cv.CV_CAP_PROP_FRAME_WIDTH,width)
if height is None :
height = int (cv.GetCaptureProperty(capture, cv.CV_CAP_PROP_FRAME_HEIGHT))
else :
cv.SetCaptureProperty(capture,cv.CV_CAP_PROP_FRAME_HEIGHT,height)
if capture:
frame_copy = None
while True :
frame = cv.QueryFrame(capture)
if not frame:
cv.WaitKey(0 )
break
if not frame_copy:
frame_copy = cv.CreateImage((frame.width,frame.height),
cv.IPL_DEPTH_8U, frame.nChannels)
if frame.origin == cv.IPL_ORIGIN_TL:
cv.Copy(frame, frame_copy)
else :
cv.Flip(frame, frame_copy, 0 )
detect_and_draw(frame_copy, cascade)
if cv.WaitKey(10 ) >= 0 :
break
else :
image = cv.LoadImage(input_name, 1 )
detect_and_draw(image, cascade)
cv.WaitKey(0 )
cv.DestroyWindow("result" )
発声部分のプログラムは、簡単なのですぐわかると思うが、
顔を認識した回数をカウントして、何回かになると発声するようにしている。
最初は、「コーヒー飲みませんか?」 と、お誘いする。
2回目は、「ねぇ、ねぇ、コーヒー飲もうよ!」と、ちょっと、うざい感じで・・・。
3回目は、「コーヒー飲まないかなぁ~?」と、ちょっと、未練たらしく・・・。
3回発声すると、リセットされて、しばらく間をとる。3回の間に顔認識がはずれた時にも、リセットされて、また、一回目の発声からはじまる。
てな、具合。
別に、バリスタ なくても良いんじゃないかって?
あっ、気付いちゃった?
まぁ、そうなんだけど、コミュニケーションとるのに、何か、それなりのネタがいるじゃないですか。
だから、バリスタ で入れるコーヒーをネタにナンパをしかけるという作戦で・・・。
これまでの一連の試みで、人が近くに来たのを認識し、「コーヒーはいかが?!」と、声をかけて、音声認識 で返事を受け取って、コーヒーを入れる。と、いう、サービスがほぼ自動化できるようになった。
これに、手足をつけてロボット化すると、給仕ロボットができちゃいそうですね。