树莓派脸部追踪
硬件材料
树莓派4B、云台、摄像头
思路
1、电脑上显示摄像头拍摄的视频,并得到人脸坐标,将人脸坐标发给树莓派。
2、树莓派来控制舵机旋转
3、电脑和树莓派之间和socket通信
4、树莓派上使用motion将摄像头内容输出到“192.168.6.179:8081”,从而让电脑获取视频源【192.168.6.179是树莓派地址】
注意:
1、树莓派可能需要关掉防火墙:ufw disable
2、树莓派要先启动motion:sudo motion【只用启动一次即可,一直在后台运行】
人脸跟踪的算法
第一种
获得人脸矩阵中心点坐标【x,y】,再获得视频中心坐标,计算两者误差,从而让摄像头旋转相应角度,旋转时要尽量一度一度的转,不要过激,否则容易让抖动。
当然,我写的只是简单的计算两个中心误差再旋转,缺点是旋转不平滑,改进方式是用PID算法
PID算法参考1:https://pyimagesearch.com/2019/04/01/pan-tilt-face-tracking-with-a-raspberry-pi-and-opencv/
PID算法参考2:https://bcxiaobai.eu.org/post/383.html
第二种
参考:https://blog.csdn.net/rikeilong/article/details/126446567
当人脸矩阵左边或右边快要超出视频边界时再旋转,也是要尽量一度一度的转
代码
电脑上
电脑上client.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import socketclass connect_Raspberry (): def __init__ (self,host,port ): print ("客户端开启" ) self.mySocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try : self.mySocket.connect((host, port)) print ("连接到服务器" ) except : print ('连接RASP不成功' ) def send (self, words ): msg = words self.mySocket.send(msg.encode("utf-8" )) def close (self ): self.mySocket.close() print ("与树莓派丽连接中断\n" ) exit()
电脑上main.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 import cv2import mediapipe as mpimport numpy as npimport clientmp_face_detection = mp.solutions.face_detection mp_drawing = mp.solutions.drawing_utils myRaspConnection = client.connect_Raspberry('192.168.6.179' , 8888 ) if __name__ == "__main__" : capture = cv2.VideoCapture("http://192.168.6.179:8081" ) ref, frame = capture.read() fps = 0.0 while (True ): ref, frame = capture.read() h, w, _ = np.shape(frame) if not ref: break image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) with mp_face_detection.FaceDetection(model_selection=0 , min_detection_confidence=0.8 ) as face_detection: results = face_detection.process(image) if results.detections: for detection in results.detections: box = detection.location_data.relative_bounding_box cx = box.xmin cy = box.ymin cw = box.width ch = box.height cv2.rectangle(image, (int (cx * w), int (cy * h)), (int ((cx + cw) * w), int ((cy + ch) * h)), (0 , 255 , 0 ), 2 ) msg = str (abs (int (cx * w))) + " " + str (abs (int (cy * h))) + " " + str (abs (int ((cx + cw) * w))) + " " + str ( abs (int ((cy + ch) * h))) print (msg) myRaspConnection.send(msg) frame = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) cv2.imshow("video" , frame) c = cv2.waitKey(1 ) & 0xff if c == 27 : capture.release() break print ("Video Detection Done!" ) capture.release() cv2.destroyAllWindows()
树莓派上
树莓派上sever.py
1 2 3 4 5 6 7 8 9 import socketprint ("服务开启" )mySocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) host = "192.168.6.179" port = 8888 mySocket.bind((host, port)) mySocket.listen(10 )
树莓派上main.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 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 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 import timeimport severimport RPi.GPIO as GPIOfrom PCA9685 import PCA9685import mathpwm=PCA9685() pwm.setPWMFreq(50 ) pwm.setRotationAngle(5 ,0 ) if __name__ == '__main__' : pid_X_P=0 pid_Y_P=0 print ("等待连接" ) client,address = sever.mySocket.accept() print ("新连接" ) print ("IP is %s" % address[0 ]) print ("port is %d\n" % address[1 ]) beangle = 90 beangle0 = 45 channel1 = 4 channel2 = 8 angleFreq = 1 changeFreqX = 100 changeFreqY = 20 error_x=500 last_error_x=100 error_y=250 last_error_y=50 wight=900 height=480 piv_x=90 piv_y=45 step=1 try : print ("开始" ) while True : msg = client.recv(1024 ) msg = msg.decode("utf-8" ) if msg != "" : mess = msg.split(' ' ) x0 = int (mess[0 ]) y0 = int (mess[1 ]) x1 = int (mess[2 ]) y1 = int (mess[3 ]) x_mean=int ((x0+x1)/2 ) y_mean=int ((y0+y1)/2 ) print ("x_mean" ,x_mean,"y_mean" ,y_mean) error_x=int (x_mean-wight/2 ) error_y=int (y_mean-height/2 ) print ("error_x" ,error_x,"error_y" ,error_y) if error_x<0 and abs (error_x)>100 : step_x=math.exp(abs (error_x)/(wight/2 )) print (step_x) beangle+=step if beangle >= 180 : beangle = 180 print ("向左偏" ,beangle) pwm.setRotationAngle(1 ,beangle) if error_x>0 and abs (error_x)>100 : step_x=math.exp(abs (error_x)/(wight/2 )) print (step_x) beangle-=step if beangle <=10 : beangle = 10 print ("向右偏" ,beangle) pwm.setRotationAngle(1 ,beangle) if error_y<0 and abs (error_y)>70 : try : step_y=math.exp(abs (error_y)/(height/2 )) except : step_y=2 print (step_y) beangle0-=step if beangle0 <=10 : beangle0 = 10 print ("向上偏" ,beangle0) pwm.setRotationAngle(0 ,beangle0) if error_y>0 and abs (error_y)>70 : try : step_y=math.exp(abs (error_y)/(height/2 )) except : step_y=2 print (step_y) beangle0+=step if beangle0 >= 85 : beangle0 = 95 print ("向下偏" ,beangle0) pwm.setRotationAngle(0 ,beangle0) except ValueError as e: pwm.exit_PCA9685() print ("退出" ) print (e) exit()
运行
1、树莓派上先运行main.py
2、电脑上再运行main.py ,电脑上可见一个视频窗口,此时摄像头开始追踪人脸
参考
PID算法:https://pyimagesearch.com/2019/04/01/pan-tilt-face-tracking-with-a-raspberry-pi-and-opencv/
思路:https://blog.csdn.net/rikeilong/article/details/126446567?spm=1001.2014.3001.5502