abc760で使ったカメラ台はカメラが前方に飛び出し、しっかりと固定しないと不安定である。そこで、フリーのstlデータを参考に形状を修正し、新たに3Dプリンタを使ってカメラ台を作成した。サーボモータ(SG-90)の取付にはサーボ付属のホーンを加工無く使う構造とした。
カメラ台用stlデータ(camera-sg90.zip)
作成したパーツ | サーボにホーンを取付 | パーツを組上げネジで固定 |
画像処理が行えるOpenCV(CompuerVision)を使って、ラズパイカメラで取り込んだ画像の中で、人の顔の検出を行い、カメラの中心になるようにモータを制御する。
書籍「実例で学ぶRaspberryPi電子工作」(講談社)を参考にした。
画像処理前 | 2値化 | エッジ検出 |
原画(Lena)512x512 | 円検出(ライト、赤丸) | 顔検出(赤角) |
顔認識&サーボ制御 <tracking-face.py>
# -*- coding: utf-8 -*- import picamera import picamera.array import cv2 import math import pigpio cascade_path = "/usr/share/opencv/haarcascades/haarcascade_frontalface_alt.xml" cascade = cv2.CascadeClassifier(cascade_path) pi = pigpio.pi() # === サーボ用定数 Pan = 17 # 左右サーボ接続ピン Tlt = 18 # 上下サーボ接続ピン Pan_hm = 1500 Tlt_hm = 1500 Pan_max = 2000 # Pan動作範囲 Pan_min = 1000 Tlt_max = 1700 # Tilt動作範囲 Tlt_min = 1200 # === 画像用定数 Width_x = 320 # 画像の横画素数 Width_y = 240 # 画像の縦画素数 Cent_x = Width_x / 2 Cent_y = Width_y / 2 Ratio_x = 1.0 # 画素数とサーボ移動量の比 Ratio_y = 1.0 # === 初期化 pan_pos = Pan_hm # サーボを初期位置に tlt_pos = Tlt_hm pi.set_servo_pulsewidth(Pan, pan_pos) pi.set_servo_pulsewidth(Tlt, tlt_pos) with picamera.PiCamera() as camera: with picamera.array.PiRGBArray(camera) as stream: camera.resolution = (Width_x, Width_y) while True: camera.capture(stream, 'bgr', use_video_port = True) # stream.arrayにBGRの順で映像データを格納 gray = cv2.cvtColor(stream.array, cv2.COLOR_BGR2GRAY) # 映像データをグレースケール画像grayに変換 # === grayから顔を探す facerect = cascade.detectMultiScale(gray, scaleFactor = 1.3, minNeighbors = 2, minSize = (30,30), maxSize = (150,150)) if len(facerect) > 0: # 顔が見つかったら、複数見つかった顔のうち、画像中心に最も近いものを探す mindist = Width_x+Width_y minindx = 0 indx = 0 for rect in facerect: dist = math.fabs(rect[0] + rect[2]/2 - Cent_x) + math.fabs(rect[1] + rect[3]/2 - Cent_y) if dist < mindist: # より近距離の顔が見つかった? mindist = dist minindx = indx indx += 1 # === 現在の顔の位置(枠の中心) face_x = facerect[minindx][0] + facerect[minindx][2]/2 face_y = facerect[minindx][1] + facerect[minindx][3]/2 # === 元の画像(system.array)上の、顔がある位置に赤い四角を描画 cv2.rectangle(stream.array, tuple(facerect[minindx][0:2]), tuple(facerect[minindx][0:2]+facerect[minindx][2:4]), (0,0,255), thickness=2) dx = int(((Cent_x - face_x) * Ratio_x)/10)*10 # 左右中央からのずれ。(移動方向が逆なら項を入れ替)※ dy = int(((face_y - Cent_y) * Ratio_y)/10)*10 # 上下中央からのずれ。(移動方向が逆なら項を入れ替)※ # === サーボモーターの移動許容範囲でモータを回転 if Pan_min <= pan_pos + dx <= Pan_max: pan_pos += dx pi.set_servo_pulsewidth(Pan, pan_pos) if Tlt_min <= tlt_pos + dy <= Tlt_max: tlt_pos += dy pi.set_servo_pulsewidth(Tlt, tlt_pos) # === system.array(画像)をウインドウに表示 cv2.imshow('frame', stream.array) # === "q"を入力でアプリケーション終了 if cv2.waitKey(1) & 0xFF == ord('q'): break # === streamをリセット stream.seek(0) stream.truncate() cv2.destroyAllWindows() # 顔認識スケッチを終了 |
顔認識テスト中 |
車工具・オーニングハンドル | 外部電源ケーブル・工具 | 折り畳みテーブル |
炊飯器・紙食器・・ | キッチン用品・非常食・・ | 飲料水・歯磨き用品 |
電子レンジ | マルチシェード(百均収納袋) | 湯沸かし・コーヒー・紙コップ |
ダッシュ照明 |
||
昼 | 夜(LEDライト点灯) | |
収納ラック |
||
改造前(網) | 改造後(縦・横書類ケース) |