吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5001|回复: 27
收起左侧

[Python 原创] 【树莓派】云台人脸识别并追踪

  [复制链接]
Lthero 发表于 2022-10-12 21:07
本帖最后由 Lthero 于 2022-10-12 21:14 编辑

树莓派脸部追踪


硬件材料
树莓派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:[color=var(--a-color)]https://bcxiaobai.eu.org/post/383.html


第二种
参考:https://blog.csdn.net/rikeilong/article/details/126446567
当人脸矩阵左边或右边快要超出视频边界时再旋转,也是要尽量一度一度的转


请把代码中 ​ 去掉

代码
电脑上
电脑上client.py
[Python] 纯文本查看 复制代码
 import socket
 ​
 class connect_Raspberry():
     def __init__(self,host,port):
         print("客户端开启")
         # 套接字接口
         self.mySocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         # 设置ip和端口
 ​
         try:
             self.mySocket.connect((host, port))  #连接到服务器
             print("连接到服务器")
         except:  #连接不成功,运行最初的ip
             print('连接RASP不成功')
 ​
     def send(self, words):
         # 发送消息
         msg = words
         # 编码发送
         self.mySocket.send(msg.encode("utf-8"))
         # print("成功发送消息")
 ​
     def close(self):
         self.mySocket.close()
         print("与树莓派丽连接中断\n")
         exit()

​
​电脑上main.py
[Python] 纯文本查看 复制代码
import cv2 import mediapipe as mp
 import numpy as np
 import client
 ​
 # 检测脸部
 mp_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,cy,cw,ch=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.rectangle(frame, (int(cx*w) , int(cy*h)), (int((cx+cw)*w) , int((cy+ch)*h)),(0, 255, 0), 2)
 ​
         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
[Python] 纯文本查看 复制代码
print("服务开启")
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
[Python] 纯文本查看 复制代码
import socket ​



 import time
 import sever
 import RPi.GPIO as GPIO
 from PCA9685 import PCA9685
 import math
 pwm=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])#左上角x
                 y0 = int(mess[1])#左上角y
                 x1 = int(mess[2])#右下角x
                 y1 = int(mess[3])#右下角y
 ​
 ​
 # 方法1:超出中间就偏转
                 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)
 ​
                 # 误差大于100,要向左偏
                 if error_x<0  and abs(error_x)>100:
                     # temp_x=abs(error_x)/(wight/2)*45
                   
                     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)
 &#8203;
                 # 误差大于50,要向上偏
                 if error_y<0  and abs(error_y)>70:
                     # if abs(error_y)>=100:
                     #     error_y=100
                     # temp_x=abs(error_x)/(wight/2)*45
                     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:
                     # if abs(error_y)>=100:
                     #     error_y=100
                     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)
 &#8203;
                 
                 
                 # 方法2:快超出屏幕时再旋转
                 # if x0 < changeFreqX:
                 #     beangle += angleFreq
                 #     if beangle >= 180:
                 #         beangle = 180
                 #     pwm.setRotationAngle(1,beangle)
                 #     #set_servo_angle(channel1,beangle)
                 
                 # if y0 < changeFreqY:
                 #     beangle0 -= angleFreq
                 #     if beangle0 <= 10:
                 #         beangle0 = 10
                 #     pwm.setRotationAngle(0,beangle0)
                 #     #set_servo_angle(channel2,beangle0)
 &#8203;
                 # if x1 > 640 - changeFreqX: #窗口宽为640
                 #     beangle -= angleFreq
                 #     if beangle <= 10:
                 #         beangle = 10
                 #     pwm.setRotationAngle(1,beangle)
                 #     #set_servo_angle(channel1,beangle)
                 
                 # if y1 > 480 - changeFreqY: #窗口高为480
                 #     beangle0 += angleFreq
                 #     if beangle0 >= 85:
                 #         beangle0 = 85
                 #     pwm.setRotationAngle(0,beangle0)
                 #     set_servo_angle(channel2,beangle0)
                 # print("beangle",beangle,"beangle0:",beangle0)
     except ValueError as e:
         pwm.exit_PCA9685()
         print("退出")
         print(e)
         exit()
[size=14px]
&#8203;

运行
1、树莓派上先运行main.py
2、电脑上再运行main.py,电脑上可见一个视频窗口,此时摄像头开始追踪人脸


免费评分

参与人数 4威望 +1 吾爱币 +22 热心值 +4 收起 理由
hwh425 + 1 我很赞同!
fontic + 1 + 1 谢谢@Thanks!
wushaominkk + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
AG9000 + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

plauger 发表于 2022-10-12 21:50
以目前树莓派的算力应该直接做人脸追踪都够了,不需要电脑参与。多年前做过相关应用,AdaBoost算法做图像定位切割,运算量不算太大的。
 楼主| Lthero 发表于 2022-10-18 20:31
头铁又刚 发表于 2022-10-17 16:42
对,想问下,最大支持多大的盘?外接供电,网上大多是一两个T的,内网我打算花生壳,我得想法是接上nas扩 ...

我也只用过1TB的硬盘,再大的我也没尝试过,没什么经验。不过您可以上b站找找用树莓派做NAS的相关视频。外接供电的话,分用笔记本硬盘和台式机硬盘两种,台式机硬盘需要外部供电。至于花生壳的话,据说网络时好时坏,我当时用个自己服务器做的穿透,延迟还比较低。我当时只是尝试了下做个人网盘的可行性,但实现后我发现平时需要外网使用网盘情况较少,就没继续研究下去了。如果您想长期使用的话,我建议买个专业的NAS设备和监控级别的硬盘,不论是传输速率还是对硬盘保护性来说效果更好。
lansemeiying 发表于 2022-10-12 21:17
happyaguang 发表于 2022-10-12 21:18
&#8203;
chenxiaodada 发表于 2022-10-12 21:30
很好的啊,我去试试
 楼主| Lthero 发表于 2022-10-12 22:38

&#8203;是什么?我从typora复制过到吾爱编辑器就有这个……
 楼主| Lthero 发表于 2022-10-12 22:41
plauger 发表于 2022-10-12 21:50
以目前树莓派的算力应该直接做人脸追踪都够了,不需要电脑参与。多年前做过相关应用,AdaBoost算法做图像定 ...

我怕在树莓派运算处理的延迟会高,所以先在电脑端做图片处理,不过有空会尝试直接部署在树莓派上运行。
DRLLL 发表于 2022-10-12 23:11
挺有意思,多谢分享
hckj1919 发表于 2022-10-13 00:01
谢谢分享
Piz.liu 发表于 2022-10-13 00:20
先收藏,回头看看
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 17:41

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表