「実例で学ぶ RaspberryPi 電子工作」第7章 7.3 画像処理結果をもとに対象物追跡
pi@raspberrypi:~ $ sudo ./webiopi/streamer-stop.sh 1010 mjpg_streamer stopped停止の確認。
pi@raspberrypi:~ $ ps ax |grep mjpg_streamer 3523 pts/0 S+ 0:00 grep --color=auto mjpg_streamer
pi@raspberrypi:~ $ cd ~/Programs/opencv/bb2 pi@raspberrypi:~/Programs/opencv/bb2 $
pi@raspberrypi:~/Programs/opencv/bb2 $ python3 circle2.py
pi@raspberrypi:~/Programs/opencv/bb2 $ cp bb2-07-08-tracking-circle-pca9685.py tracking-circle-pca9685.py pi@raspberrypi:~/Programs/opencv/bb2 $ vi tracking-circle-pca9685.py # -*- coding: utf-8 -*- import picamera import picamera.array import cv2 import math import Adafruit_PCA9685 version = cv2.__version__.split(".") CVversion = int(version[0]) ## PCA9685サーボモーターコントロール class servo_Class: #ChannelはPCA9685のサーボモータの接続チャンネル #ZeroOffsetはサーボモータの基準位置調節用パラメータ def __init__(self, Channel, ZeroOffset): self.Channel = Channel self.ZeroOffset = ZeroOffset #Adafruit_PCA9685の初期化 self.pwm = Adafruit_PCA9685.PCA9685(address=0x40) self.pwm.set_pwm_freq(60) # 角度設定 def SetPos(self,pos): #PCA9685はパルスで角度を制御しており、パルス150~650が角度0~180に対応 pulse = int((650-150)*pos/180+150+self.ZeroOffset) self.pwm.set_pwm(self.Channel, 0, pulse) # 終了処理 def Cleanup(self): #サーボを90°にセット self.SetPos(90) Servo0 = servo_Class(Channel=0, ZeroOffset=0) ## Pan Servo1 = servo_Class(Channel=1, ZeroOffset=0) ## Tilt # 2軸のサーボモーターコントロールのためのマクロ # id: 0 = Horizontal, 1 = Vertical # duty: 0.0 ~ 0.5 ~ 1.0 # degr: 30 ~ 90 ~ 150 (角度) def setServo(id, duty): degr = (150-30)*float(duty) + 30 if int(id) == 0: Servo0.SetPos(degr) elif int(id) == 1: Servo1.SetPos(degr) prev_x = 160 prev_y = 120 prev_input_x = 0.5 prev_input_y = 0.5 with picamera.PiCamera() as camera: with picamera.array.PiRGBArray(camera) as stream: camera.resolution = (320, 240) camera.framerate = 15 while True: # stream.arrayにBGRの順で映像データを格納 camera.capture(stream, 'bgr', use_video_port=True) # 映像データをグレースケール画像grayに変換 gray = cv2.cvtColor(stream.array, cv2.COLOR_BGR2GRAY) # ガウシアンぼかしを適用して、認識精度を上げる blur = cv2.GaussianBlur(gray, (9,9), 0) # ハフ変換を適用し、映像内の円を探す if CVversion == 2: circles = cv2.HoughCircles(blur, cv2.cv.CV_HOUGH_GRADIENT, dp=1, minDist=50, param1=120, param2=40, minRadius=5, maxRadius=100) else: circles = cv2.HoughCircles(blur, cv2.HOUGH_GRADIENT, dp=1, minDist=50, param1=120, param2=40, minRadius=5, maxRadius=100) if circles is not None: # 複数見つかった円のうち、以前の円の位置に最も近いものを探す mindist = 320+240 minindx = 0 indx = 0 for c in circles[0]: dist = math.fabs(c[0]-prev_x) + math.fabs(c[1]-prev_y) if dist < mindist: mindist = dist minindx = indx indx += 1 # 現在の円の位置 circle_x = circles[0][minindx][0] circle_y = circles[0][minindx][1] # 現在の円の位置に赤い円を元の映像(system.array)上に描画 cv2.circle(stream.array, (circle_x, circle_y), circles[0][minindx][2], (0,0,255), 2) dx = circle_x-160 # 左右中央からのずれ dy = circle_y-120 # 上下中央からのずれ ## サーボモーターを回転させる量を決める定数 ratio_x = -0.001 ratio_y = 0.001 duty0 = ratio_x*dx + prev_input_x if float(duty0) > 1.0: duty0 = 1.0 if float(duty0) < 0.0: duty0 = 0.0 setServo(0, duty0) duty1 = ratio_y*dy + prev_input_y if float(duty1) > 1.0: duty1 = 1.0 if float(duty1) < 0.0: duty1 = 0.0 print('duty0: ' + str(float(duty0))) print('duty1: ' + str(float(duty1))) setServo(1, duty1) ## サーボモーターに対する入力値を更新 prev_input_x = duty0 prev_input_y = ratio_y*dy + prev_input_y # 以前の円の位置を更新 prev_x = circle_x prev_y = circle_y # system.arrayをウインドウに表示 cv2.imshow('frame', stream.array) # "q"を入力でアプリケーション終了 if cv2.waitKey(1) & 0xFF == ord('q'): break # streamをリセット stream.seek(0) stream.truncate() ## カメラを中央に戻す Servo0.Cleanup() Servo1.Cleanup() cv2.destroyAllWindows()
pi@raspberrypi:~/Programs/opencv/bb2 $ python3 face2.py
pi@raspberrypi:~/Programs/opencv/bb2 $ cp bb2-07-08-tracking-face-pca9685.py tracking-face-pca9685.py pi@raspberrypi:~/Programs/opencv/bb2 $ vi tracking-face-pca9685.py # -*- coding: utf-8 -*- import picamera import picamera.array import cv2 import math import Adafruit_PCA9685 cascade_path = "/usr/share/opencv/haarcascades/haarcascade_frontalface_alt.xml" cascade = cv2.CascadeClassifier(cascade_path) ## PCA9685サーボモーターコントロール class servo_Class: #ChannelはPCA9685のサーボモータの接続チャンネル #ZeroOffsetはサーボモータの基準位置調節用パラメータ def __init__(self, Channel, ZeroOffset): self.Channel = Channel self.ZeroOffset = ZeroOffset #Adafruit_PCA9685の初期化 self.pwm = Adafruit_PCA9685.PCA9685(address=0x40) self.pwm.set_pwm_freq(60) # 角度設定 def SetPos(self,pos): #PCA9685はパルスで角度を制御しており、パルス150~650が角度0~180に対応 pulse = int((650-150)*pos/180+150+self.ZeroOffset) self.pwm.set_pwm(self.Channel, 0, pulse) # 終了処理 def Cleanup(self): #サーボを90°にセット self.SetPos(90) Servo0 = servo_Class(Channel=0, ZeroOffset=0) ## Pan Servo1 = servo_Class(Channel=1, ZeroOffset=0) ## Tilt # 2軸のサーボモーターコントロールのためのマクロ # id: 0 = Horizontal, 1 = Vertical # duty: 0.0 ~ 0.5 ~ 1.0 # degr: 30 ~ 90 ~ 150 (角度) def setServo(id, duty): degr = (150-30)*float(duty) + 30 if int(id) == 0: Servo0.SetPos(degr) elif int(id) == 1: Servo1.SetPos(degr) prev_x = 160 prev_y = 120 prev_input_x = 0.5 prev_input_y = 0.5 with picamera.PiCamera() as camera: with picamera.array.PiRGBArray(camera) as stream: camera.resolution = (320, 240) camera.framerate = 15 while True: # stream.arrayにBGRの順で映像データを格納 camera.capture(stream, 'bgr', use_video_port=True) # 映像データをグレースケール画像grayに変換 gray = cv2.cvtColor(stream.array, cv2.COLOR_BGR2GRAY) # grayから顔を探す facerect = cascade.detectMultiScale(gray, scaleFactor=1.3, minNeighbors=2, minSize=(30,30), maxSize=(150,150)) if len(facerect) > 0: # 複数見つかった顔のうち、以前の顔の位置に最も近いものを探す mindist = 320+240 minindx = 0 indx = 0 for rect in facerect: dist = math.fabs(rect[0]+rect[2]/2-prev_x) + math.fabs(rect[1]+rect[3]/2-prev_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 = face_x-160 # 左右中央からのずれ dy = face_y-120 # 上下中央からのずれ ## サーボモーターを回転させる量を決める定数 ratio_x = -0.001 ratio_y = 0.001 duty0 = ratio_x*dx + prev_input_x if float(duty0) > 1.0: duty0 = 1.0 if float(duty0) < 0.0: duty0 = 0.0 setServo(0, duty0) duty1 = ratio_y*dy + prev_input_y if float(duty1) > 1.0: duty1 = 1.0 if float(duty1) < 0.0: duty1 = 0.0 print('duty0: ' + str(float(duty0))) print('duty1: ' + str(float(duty1))) setServo(1, duty1) ## サーボモーターに対する入力値を更新 prev_input_x = duty0 prev_input_y = ratio_y*dy + prev_input_y # 以前の顔の位置を更新 prev_x = face_x prev_y = face_y # system.arrayをウインドウに表示 cv2.imshow('frame', stream.array) # "q"を入力でアプリケーション終了 if cv2.waitKey(1) & 0xFF == ord('q'): break # streamをリセット stream.seek(0) stream.truncate() ## カメラを中央に戻す Servo0.Cleanup() Servo1.Cleanup() cv2.destroyAllWindows()
# system.arrayをウインドウに表示 cv2.imshow('frame', stream.array)表示部分(変更語) 640x480
# system.arrayをウインドウに表示 dst = cv2.resize(stream.array, (640, 480)) cv2.imshow('frame', dst)