・demo_face1.pyはべゼリーが顔を認識したらセリフを言うプログラムです。騒がしい場所に置く場合など、音声認識が使えないときに適しています。
モジュールの読み込み
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#!/usr/bin/python # -*- coding: utf-8 -*- # 顔認識デモ # for Bezelie Edgar # for Raspberry Pi # by Jun Toyoda (Team Bezelie) # from Aug15th2017 from datetime import datetime # 現在時刻取得 from random import randint # 乱数の発生 from time import sleep # ウェイト処理 import subprocess # 外部プロセスを実行するモジュール import json # jsonファイルを扱うモジュール import csv # CSVファイルを扱うモジュール import sys # python終了sys.exit()のために必要 import picamera # カメラ用モジュール import picamera.array # カメラ用モジュール import cv2 # Open CVモジュール import bezelie # べゼリー専用サーボ制御モジュール |
・ラズパイカメラ用モジュールpycameraと、画像処理モジュールOpenCVをimportしています。
初期設定
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
csvFile = "/home/pi/bezelie/chatDialog.csv" # 対話リスト jsonFile = "/home/pi/bezelie/edgar/data_chat.json" # 設定ファイル ttsFile = "/home/pi/bezelie/edgar/exec_openJTalk.sh" # 音声合成 debugFile = "/home/pi/bezelie/debug.txt" # debug用 # 設定ファイルの読み込み f = open (jsonFile,'r') jDict = json.load(f) name = jDict['data0'][0]['name'] # べゼリーの別名。 user = jDict['data0'][0]['user'] # ユーザーのニックネーム。 # mic = jDict['data0'][0]['mic'] # マイク感度。62が最大値。 vol = jDict['data0'][0]['vol'] # スピーカー音量。 # 変数の初期化 alarmStop = False # アラームのスヌーズ機能(非搭載) waitTime = 5 # autoモードでの会話の間隔 # OpenCV cascade_path = "/usr/share/opencv/haarcascades/haarcascade_frontalface_alt.xml" # 顔認識xml cascade = cv2.CascadeClassifier(cascade_path) |
・39~40行目で顔を認識するための定義ファイルを読み込んでいます。
関数定義
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# 関数 def timeCheck(): # 活動時間内かどうかのチェック f = open (jsonFile,'r') jDict = json.load(f) awake1Start = jDict['data1'][0]['awake1Start'] awake1End = jDict['data1'][0]['awake1End'] awake2Start = jDict['data1'][0]['awake2Start'] awake2End = jDict['data1'][0]['awake2End'] t = datetime.now() if int(t.hour) > int(awake1Start[0:2]) and int(t.hour) < int(awake1End[0:2]): flag = True elif int(t.hour) == int(awake1Start[0:2]) and int(t.minute) >= int(awake1Start[3:5]): flag = True elif int(t.hour) == int(awake1End[0:2]) and int(t.minute) <= int(awake1End[3:5]): flag = True elif int(t.hour) > int(awake2Start[0:2]) and int(t.hour) < int(awake2End[0:2]): flag = True elif int(t.hour) == int(awake2Start[0:2]) and int(t.minute) >= int(awake2Start[3:5]): flag = True elif int(t.hour) == int(awake2End[0:2]) and int(t.minute) <= int(awake2End[3:5]): flag = True else: flag = False # It is not Active Time return flag def replyMessage(keyWord): # 対話 data = [] # 対話ファイル(csv)を変数dataに読み込む with open(csvFile, 'rb') as f: # csvFileをオープン for i in csv.reader(f): # ファイルから1行ずつiに読み込む data.append(i) # dataに追加 data1 = [] # dataから質問内容がキーワードに一致している行をdata1として抜き出す for index,i in enumerate(data): # index=連番 if unicode(i[0], 'utf-8')==keyWord: # i[0]はstrなのでutf-8に変換して比較する必要がある j = randint(1,100) # 1から100までの乱数を発生させる data1.append(i+[j]+[index]) # data1=質問内容,返答,乱数,連番のリスト if data1 == []: # data1が空っぽだったらランダムで返す for index,i in enumerate(data): j = randint(1,100) data1.append(i+[j]+[index]) maxNum = 0 # 複数の候補からランダムで選出。data1から欄数値が最大なものを選ぶ for i in data1: if i[2] > maxNum: maxNum = i[2] ansNum = i[3] # 設定ファイルの読み込み f = open (jsonFile,'r') jDict = json.load(f) # mic = jDict['data0'][0]['mic'] # マイク感度の設定。 vol = jDict['data0'][0]['vol'] # スピーカー音量。 bez.moveRnd() subprocess.call('amixer cset numid=1 '+vol+'% -q', shell=True) # スピーカー音量 subprocess.call("sh "+ttsFile+" "+data[ansNum][1], shell=True) bez.stop() def debug_message(message): print message # writeFile(message) # pass # sys.stdout.write(message) def writeFile(text): # デバッグファイル出力機能 f = open (debugFile,'r') textBefore = "" for row in f: textBefore = textBefore + row f.close() f = open (debugFile,'w') f.write(textBefore + text + "\n") f.close() |
メインループ
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# サーボの初期化 bez = bezelie.Control() # べゼリー操作インスタンスの生成 bez.moveCenter() # サーボの回転位置をトリム値に合わせる # メインループ def main(): try: subprocess.call('amixer cset numid=1 '+vol+'% -q', shell=True) # スピーカー音量 subprocess.call("sh "+ttsFile+" "+u"顔認識モード", shell=True) stageAngle = 0 # ステージの初期角度 stageDelta = 5 # ループごとにステージを回転させる角度 stageSpeed = 8 # ループごとにステージを回転させる速度 bez.moveHead(15) with picamera.PiCamera() as camera: # Open Pi-Camera as camera with picamera.array.PiRGBArray(camera) as stream: # Open Video Stream from Pi-Camera as stream camera.resolution = (640, 480) # Display Resolution # camera.resolution = (1280, 720) # Display Resolution # camera.resolution = (1920, 1080) # Display Resolution camera.hflip = True # Vertical Flip camera.vflip = True # Horizontal Flip while True: if timeCheck(): # 活動時間だったら動く camera.capture(stream, 'bgr', use_video_port=True) # Capture the Video Stream gray = cv2.cvtColor(stream.array, cv2.COLOR_BGR2GRAY) # Convert BGR to Grayscale facerect = cascade.detectMultiScale(gray, # Find face from gray scaleFactor=1.9, # 1.1 - 1.9 :the bigger the quicker & less acurate minNeighbors=3, # 3 - 6 : the smaller the more easy to detect minSize=(100,120), # Minimam face size maxSize=(640,480)) # Maximam face size if len(facerect) > 0: for rect in facerect: cv2.rectangle(stream.array, # Draw a red rectangle at face place tuple(rect[0:2]), # Upper Left tuple(rect[0:2]+rect[2:4]), # Lower Right (0,0,255), thickness=2) # Color and thickness replyMessage(u"顔認識") # cv2.imshow('frame', stream.array) # 画面に表示したい場合はコメント外してください if cv2.waitKey(1) & 0xFF == ord('q'): # Quit operation break stream.seek(0) # Reset the stream stream.truncate() stageAngle = stageAngle + stageDelta if stageAngle > 25 or stageAngle < -25: stageDelta = stageDelta*(-1) bez.moveStage(stageAngle,stageSpeed) else: # 活動時間外は動作しない subprocess.call('amixer cset numid=1 60% -q', shell=True) # スピーカー音量を調整 subprocess.call("sh "+ttsFile+" "+"活動時間外です", shell=True) print "活動時間外なので発声・動作しません" sleep (600) # 10分待機 subprocess.call('amixer cset numid=1 '+vol+'% -q', shell=True) # スピーカー音量を戻す cv2.destroyAllWindows() except KeyboardInterrupt: # CTRL+Cで終了 debug_message('keyboard interrupted') bez.moveCenter() bez.stop() sys.exit(0) if __name__ == "__main__": debug_message('---------- started ----------') main() sys.exit(0) |
・127~128行目でステージを回転させる速度を設定しています。
・132行目の表示サイズは、お使いのディスプレイの解像度に合わせて調整してください。
・認識する顔のサイズや認識精度と速度のバランスは142~145行目で調整できます。