一.项目介绍
1.概述
本项目是将树莓派摄像头采集到的视频流发送至实时监控网站,如果出现人像则通过人脸识别确定,并截取该图像发送至收信邮箱。
2.思路与分工
本次实验,我们大体划分成三个模块。
- 第一:人脸识别模块。采用opencv的cv2.CascadeClassifier级联分类器进行人脸识别捕捉。由翁正凯完成。
- 第二:搭建实时视频流网站将采集到的实时视频发送到我们建立的网页,这一步我们采用flask框架搭建。由张启荣完成。
- 第三:发送采集到的人脸关键帧图片至我们指定的qq邮箱,这--步我们是采用smtp来进行的。由李家涌完成。
二.项目实现
1.安装opencv
实验七已经安装过opencv环境,所以不再赘述,直接沿用上次的环境。
2.安装项目依赖
requestment.txt
imutils
flask
picamera[array]
Flask-BasicAuth==0.2.0
pip install -r requirements.txt
3.人脸识别模块
camera.py
import cv2
from imutils.video.pivideostream import PiVideoStream #处理视频流的库
import imutils #图像处理工具包,也可用于视频的处理,如摄像头、本地文件等
import time
import numpy as np #运行速度非常快的数学库,主要用于数组计算
class VideoCamera(object):
def __init__(self, flip = False): #类初始化,self指向类实例对象本身
self.vs = PiVideoStream().start() #启动线程视频流
self.flip = flip
time.sleep(2.0)
def __del__(self): #对象销毁时调用,用于释放资源
self.vs.stop() #停止线程并释放所有资源
def flip_if_needed(self, frame): #翻转当前这帧的图片, frame:帧
if self.flip:
return np.flip(frame, 0) #np.flip用于翻转数组,NumPy的np.flip()函数允许沿着某一个轴翻转数组的内容。当使 用np.flip时,指定要反转的数组和轴。如果不指定轴将沿着输入数组的所有轴反转内容。
return frame #np.flip(frame,0),0:按行翻转,1:按列翻转,不指定:按行按列翻转
def get_frame(self): #得到当前帧
frame = self.flip_if_needed(self.vs.read()) #self.vs.read()返回当前帧
ret, jpeg = cv2.imencode('.jpg', frame) #cv2.imencode()函数是将图片格式转换(编码)成流数据,赋值到内存缓 存中;主要用于图像数据格式的压缩,方便网络传输。
return jpeg.tobytes() #image.tobytes()函数,以字节对象的形式返回图像
def get_object(self, classifier): #调用分类器识别人脸,返回识别到的人脸图片
found_objects = False
frame = self.flip_if_needed(self.vs.read()).copy() #复制视频流采集到的帧,用于进行人脸判断
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) #将读取到的帧进行颜色空间转换,有些图像可能在 RGB 颜 色空间信息不如转换到其它颜色空间更清晰
objects = classifier.detectMultiScale( #调整函数的参数使检测结果更加精确
gray,
scaleFactor=1.1,
minNeighbors=5,
minSize=(30, 30),
flags=cv2.CASCADE_SCALE_IMAGE
)
if len(objects) > 0: #如果有识别到人脸
found_objects = True
# Draw a rectangle around the objects #在人脸周围画一个矩形框
for (x, y, w, h) in objects:
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
ret, jpeg = cv2.imencode('.jpg', frame)
return (jpeg.tobytes(), found_objects) #如果识别到人脸,就将其图片转换成流数据并返回
4,建立实时监控网站并发送实时视频流
main.py
import cv2
import sys
from mail import sendEmail #从mail.py导入
from flask import Flask, render_template, Response
from camera import VideoCamera #从camera.py导入
from flask_basicauth import BasicAuth #
import time
import threading
email_update_interval = 5 # 发送邮件时间间隔 秒为单位
video_camera = VideoCamera(flip=True) # 创建一个camera对象
object_classifier = cv2.CascadeClassifier("models/facial_recognition_model.xml" # 级联分类器 具体模型是网上找的开源已经训练好的
# https://flask-basicauth.readthedocs.io/en/latest/ 用于简单安全认证
app = Flask(__name__)
app.config['BASIC_AUTH_USERNAME'] = 'pi'
app.config['BASIC_AUTH_PASSWORD'] = 'pi'
app.config['BASIC_AUTH_FORCE'] = True
basic_auth = BasicAuth(app)
last_epoch = 0
def check_for_objects():#确定当前有识别到人脸并且已经过了上次发送间隔
global last_epoch
while True:
try:
frame, found_obj = video_camera.get_object(object_classifier) #得到人脸识别的关键帧
if found_obj and (time.time() - last_epoch) > email_update_interval:
last_epoch = time.time()
print ("Sending email...")
sendEmail(frame)
print ("done!") #成功
except:
print ("Error sending email: "), sys.exc_info()[0] #抛出异常
@app.route('/')
@basic_auth.required
def index():
return render_template('index.html') #模板渲染
#https://www.jianshu.com/p/a262b3c42386
def gen(camera):#使用生成器产生实时数据 用于实时看到监控
while True:
frame = camera.get_frame()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')#每一帧包含的信息
@app.route('/video_feed')
def video_feed(): #发送视频流至搭建的网站 在index.html进行响应
return Response(gen(video_camera),
mimetype='multipart/x-mixed-replace; boundary=frame') #实现视频流的基本格式
if __name__ == '__main__': #创建线程
t = threading.Thread(target=check_for_objects, args=())
t.daemon = True #线程守护
t.start()
app.run(host='0.0.0.0', debug=False)
5.使用smtp发送图片至目标邮箱
注意:若要使用QQ邮箱的SMTP服务,需要到QQ邮箱开启相关服务。
mail.py
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from PIL import Image
# 邮件发送方(QQ邮箱)
fromEmail = '[email protected]'
# 发送方QQ邮箱授权码
fromEmailPassword = 'hpftfrejtseff***'
# 收件方
toEmail = '[email protected]'
def sendEmail(image):
# 多个MIME对象的集合
msgRoot = MIMEMultipart()
# 主题
msgRoot['Subject'] = 'Security Update'
# 收件人邮箱
msgRoot['From'] = fromEmail
# 发件人邮箱
msgRoot['To'] = toEmail
msgRoot.preamble = 'Raspberry pi security camera update'
msgAlternative = MIMEMultipart('alternative')
msgRoot.attach(msgAlternative)
msgText = MIMEText('Smart security cam found object')
msgAlternative.attach(msgText)
msgText = MIMEText('', 'html')
msgAlternative.attach(msgText)
# 添加捕获的图片
msgImage = MIMEImage(image)
msgImage.add_header('Content-ID', '')
msgRoot.attach(msgImage)
# 连接SMTP服务器
smtp = smtplib.SMTP('smtp.qq.com', 587)
smtp.starttls()
# 登录邮箱
smtp.login(fromEmail, fromEmailPassword)
# 发送邮件
smtp.sendmail(fromEmail, toEmail, msgRoot.as_string())
# 退出,断开连接
smtp.quit()
6.本地初步运行
python3 main.py
注:终端提示”Sending email“即表明摄像头识别到人脸,“done”表发送邮件至指定收件箱完成。
7.布置内网穿透
采用Sunny-Ngrok实现内网穿透。使得通过外部网络可以访问实时视频流监控网站,而不局限于同一局域网
(1)首先到Sunny-Ngrok官网注册一个账号并开通隧道
(2)下载客户端
这里选择的版本是Linux ARM版本
(3)解压至树莓派
(4)运行
./sunny clientid 2f39592879c6728f
注:后面跟着的这串字符就是注册得到的隧道ID,先运行上面这句,然后再运行main.py,此时就可以在外部网络通过访问http://daydream.free.idcfengye.com 来查看监控视频.
查看到的监控视频
三.项目部署至docker
1.项目结构
2.自定义镜像
(1)dockerfile
FROM opencv1
RUN mkdir /myapp
WORKDIR /myapp
COPY myapp .
(2)构建镜像
docker build -t opencv3 .
(3)创建并运行容器
docker run -it --device=/dev/vchiq --device=/dev/video0 --name security opencv3
安装依赖
pip install -r requestments.txt
(4)保存镜像
(5)运行
进入上面创建好的容器内,运行main.py
pythn3 main.py
四.总结
(1)组员总结
翁正凯:本次实验我负责的是摄像头模块的人脸识别部分,一开始我并不知道怎么对摄像头采集到视频进行处理,如何截取每一帧图片进行逐帧分析并识别该帧中是否存在人脸,以及要用到哪些包之类的。但是没办法,也只能硬着头皮去做,在网上查了很多资料后,才知道要处理视频流要用到 imutils 下的 pivideostream 这个包,于是在GitHub上找到了imutils的源码库,进行阅读,并试着慢慢写出代码。遇到的最大一个问题还是对python的不熟悉,语法上的不熟悉使我吃了蛮大的亏,比如定义python类时,为什么一定要定义init()方法等等。不过leaning by doning,在写代码的过程中,学习到了很多新知识。总的来说,本次实验着实令我开阔了眼界,知道了原来树莓派还可以这么玩,从某种程度上来讲,我们这次的项目也让我见识到了什么叫“万物互联”。
张启荣:总的来说这次实验的代码量并不大,但是我们自认为是一个流程较为完善的小而精致的项目,未来还具有拓展性,通过本次实验我学习到了对于一个人脸识别项目的完整流程,我们除了没有自己训练人脸数据,其余都是我们自己写,我所负责的部分是网站搭建和视频流传输,这让我对flask也有了更深一些了解,因为事情太多了,后续如果有时间,我们可能会对应用进行拓展,然后对应用进行精修,比如对网站优化美化,降低延迟,提高精度等。总的来说这个项目让我受益匪浅。
李家涌:这次实验我主要负责的是发送邮件和内网穿透的实现,实验过程中了解到了SMTP,以及python中smtp的客户端实现的模块smtplib,实现邮件消息处理的模块email,学会了使用python来发送邮件。内网穿透方面由于没有公网ip和vps采用的是现成服务器,没能自己实现还是比较遗憾的。总的来说还是收获满满。
(2)组内贡献比
小组成员 | 中期分工 | 本次分工 | 贡献比 |
---|---|---|---|
翁正凯 | 树莓派操作,人脸识别模块 | 树莓派操作、微服务部署 | 33.3% |
张启荣 | 视频流网站搭建 | 微服务部署 | 33.3% |
李家涌 | 基于SMTP的邮件发送模块 | 实现内网穿透 | 33.3% |