初步效果设想
像SIRI那样的语音唤醒,开启服务
只有当摄像头检测到人脸时,舵机才开始调整位置,让人脸保持C位
检测人脸是否戴口罩了,没带就发出“提示”,戴了就不管
长时间不戴例如8s,就拍一张照,并发到我的qq邮箱
应用场景:疫情期间,超市、食堂、餐厅用该装置,相比让工作人员去提醒,可以省去大量人力、财力
分五大模块开发:语音唤醒、控制舵机、人脸追踪定位、人脸口罩识别、发送人脸图片至qq邮箱
1.语音唤醒模块
2.控制舵机模块
用两个舵机做一个二自由度云台, 云台外壳可从淘宝买
①servoControl.py封装了controlTwoServos(pin1, angle1, pin2, angle2)方法,用来控制舵机
pin1、pin2分别指的是舵机1、舵机2的信号线所接引脚(BCM编码方式)
angle1、angle2分别指舵机1、舵机2要旋转的角度
舵机1控制水平旋转方向, 舵机2控制垂直旋转方向
servoControl.py
import RPi.GPIO as GPIO
from time import sleep
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
# 旋转角度转换到PWM占空比
def angleToDutyCycle(angle):
return 2.5 + angle / 180.0 * 10
# 控制两个舵机的函数
# pin1、pin2分别指的是舵机1、舵机2的信号线所接引脚
# angle1、angle2分别指舵机1、舵机2要旋转的角度
def controlTwoServos(pin1, angle1, pin2, angle2):
GPIO.setup(pin1, GPIO.OUT, initial=False)
GPIO.setup(pin2, GPIO.OUT, initial=False)
p1 = GPIO.PWM(pin1, 50) # 舵机1初始频率为50HZ
p1.start(angleToDutyCycle(90)) # 舵机1初始化角度为90
sleep(0.5)
p1.ChangeDutyCycle(angleToDutyCycle(angle1))
p2 = GPIO.PWM(pin2, 50) # 舵机2初始频率为50HZ
p2.start(angleToDutyCycle(90)) # 舵机2初始化角度为90
p2.ChangeDutyCycle(angleToDutyCycle(angle2))
sleep(0.5)
②测试
main.py (main.py、servoControl.py在同一目录)
from servoControl import controlTwoServos
if __name__ == '__main__':
controlTwoServos(23, 80, 24, 80)
测试运行 python3 main.py
3.人脸追踪定位模块
算法如下(参考我上面画的图):
① 我这里规定摄像头拍摄的照片分辨率 640 x 480,所以照片中心坐标(320, 240)
② 利用face_recognition库中的face_locations()函数,算出人脸框的中心坐标
face_locations()会返回一个list,有几个人脸,list就有几个tuple,tuple=(top,right,bottom,left)
top指人脸框上边框到x轴的距离,right指人脸框右边框到y轴的距离
bottom指人脸框下边框到x轴的距离,left指人脸框左边框边到y轴的距离
人脸框的中心坐标((left+right)/2, (top+bottom)/2)
③ 根据照片中心坐标和人脸框的中心坐标很容易算出△x,△y
后面算法参考博客 博客地址
faceTracking.py
# coding = utf-8
import face_recognition as fr
import cv2
from time import sleep
from servoControl import controlTwoServos
while True:
video_capture = cv2.VideoCapture(0) # 创建视频对象,打开摄像头
print('Capturing image...')
# 摄像头抓取一帧照片
# ret是布尔值,读取帧成功返回True,文件读到结尾,返回False(如果你读取的是视频,而不是在线拍摄)
# frame就是每一帧的图像,一个三维矩阵
# 默认拍摄的画面分辨率是640x480,想改可以去翻opencv官方文档
ret, frame = video_capture.read()
video_capture.release()
# 缩小图片,加快处理速度,fx,fy表示x,y轴方向的缩小系数
frame = cv2.resize(frame, (0,0), fx=0.5, fy=0.5)
rgb_frame = frame[:, :, ::-1] # 将cv2用的BGR颜色转换为face_recognition用的RGB颜色
face_locations = fr.face_locations(rgb_frame) # 获取这一帧里所有脸的位置
# 如果画面中有人脸,任选一张人脸,记录下它的位置
if len(face_locations) > 0:
face_location = face_locations[0]
top = face_location[0]
right = face_location[1]
bottom = face_location[2]
left = face_location[3]
center_x = (top + bottom) / 2
center_y = (left + right) / 2
print('捕捉到人脸!')
else:
center_x, center_y = 160, 120
dx = (center_x - 160) * 0.3
dy = (center_y - 120) * 0.3
print(dx)
print(dy)
# 设置一个阈值,当角度大于3时,才移动
if abs(dx) >= 3 or abs(dy) >= 3:
x0 = 90 + dx
if x0 > 180: # 设置界限,超出范围不再转动,下同
x0 = 180
elif x0 < 0:
x0 = 0
y0 = 90 + dy
if y0 > 180:
y0 = 180
elif y0 < 0:
y0 = 0
controlTwoServos(23, x0, 24, y0)
4.人脸口罩识别模块
鉴于树莓派没有Nivida显卡,树莓派4的显卡是VideoCore VI GPU,树莓派3的显卡我也不清楚,所以很多项目跑不了,调百度的API好像还要花钱o(╥﹏╥)o
我找了一个只需要opencv的项目,但是效果一般
【武汉加油!中国加油!】挑战七天 实现机器视觉检测有没有戴口罩系统——第四五六七天_云敬山-CSDN博客
我又找了一个基于opencv的,感觉可以
博客地址
环境配置:安装opencv、numpy和pygame
pip3 install opencv-python
pip3 install numpy==1.21.4 # 安装numpy,我这里指定了版本
pip3 install pygame
我这里安装的opencv版本是4.5.4,默认有dnn模块,opencv如果版本低了好像还要安装扩展,反正保证要有dnn模块
在windows上跑没问题,在树莓派上跑有点问题,我改了一下
后来我还做了一点改动,察觉没有戴口罩就播放提示语音,还有,视频窗口就不展示了,树莓派配置差点
5.图片发送模块
①先基于下文进行配置
②send_pic.py封装一个send_mail(file_path)方法,只要传入图片路径file_path,就能实现图片发送到我的qq邮箱
send_pic.py
# coding=utf-8
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
from email.mime.multipart import MIMEMultipart # 创建附件类型
# 设置发件人和收件人信息
my_sender='xxxxxxxxxxxxxxxxx' # 发件人邮箱账号
my_pass = 'xxxxxxxxxxxxxxxxx' # 发件人邮箱密码(之前获取的授权码)
my_user='xxxxxxxxxxxxxxxxx' # 收件人邮箱账号
msg = MIMEMultipart('related') # 邮件信息为空,相当于信封,related表示使用内嵌资源的形式,将邮件发送给对方
def send_mail(file_path):
ret=True
try:
# 发送信息内容
msg_html = MIMEText('自拍照', 'html', 'utf-8')
msg.attach(msg_html)
# 发送图片,以附件形式
msg_image = MIMEText(open(file_path, 'rb').read(), 'base64', 'utf-8')
msg_image['Content-disposition'] = "attachment;filename='me.jpg'" # 设置图片在附件中的名字
msg.attach(msg_image)
msg['From']=formataddr(["Jack",my_sender]) # 括号里的对应发件人邮箱昵称、发件人邮箱账号
msg['To']=formataddr(["Rose",my_user]) # 括号里的对应收件人邮箱昵称、收件人邮箱账号
msg['Subject']="我的自拍照" # 邮件的主题,也可以说是标题
server=smtplib.SMTP("smtp.qq.com", 587) # 发件人邮箱中的SMTP服务器,端口是587
server.login(my_sender, my_pass) # 括号中对应的是发件人邮箱账号、邮箱密码
server.sendmail(my_sender,[my_user,],msg.as_string()) # 括号中对应的是发件人邮箱账号、收件人邮箱账号、发送邮件
server.quit() # 关闭连接
except Exception:
ret=False
return ret
③测试上面封装的send_mail(file_path)函数
main.py (main.py、send_pic.py、test.jpg在同一目录)
from send_pic import send_mail
if __name__ == '__main__':
if send_mail('test.jpg'):
print('发送邮件成功!')
else:
print('发送邮件失败!')
6.模块整合
7.深入思考
①如果很多人不戴口罩,用qq邮件一张张发送人脸照片太麻烦了,还不如存到一个固定文件夹中
②如果一个人在监控范围内因为长时间不戴口罩(尽管有语音提示)而被记录下人脸,他走出监控范围才戴了口罩,第二次进入监控范围,如何消除上次记下的人脸记录?
其实我想了想,长时间不戴口罩就已经算是危险分子了,就不消除记录了吧,哈哈
③在超市、餐厅这样的地方,需要放多个这样的装置才能覆盖更大的范围
④人脸追踪云台监控相比传统固定监控,优势在于扩大了监控的范围
⑤舵机不能360度控制也是一个缺陷