2020系统综合实践 期末大作业 27组

一.项目介绍

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
2020系统综合实践 期末大作业 27组_第1张图片 2020系统综合实践 期末大作业 27组_第2张图片

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邮箱开启相关服务。

  • 2020系统综合实践 期末大作业 27组_第3张图片
  • 2020系统综合实践 期末大作业 27组_第4张图片

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

2020系统综合实践 期末大作业 27组_第5张图片

注:终端提示”Sending email“即表明摄像头识别到人脸,“done”表发送邮件至指定收件箱完成。

2020系统综合实践 期末大作业 27组_第6张图片 2020系统综合实践 期末大作业 27组_第7张图片

7.布置内网穿透

采用Sunny-Ngrok实现内网穿透。使得通过外部网络可以访问实时视频流监控网站,而不局限于同一局域网

(1)首先到Sunny-Ngrok官网注册一个账号并开通隧道

2020系统综合实践 期末大作业 27组_第8张图片

(2)下载客户端

这里选择的版本是Linux ARM版本

2020系统综合实践 期末大作业 27组_第9张图片

(3)解压至树莓派

2020系统综合实践 期末大作业 27组_第10张图片

(4)运行

./sunny clientid 2f39592879c6728f

注:后面跟着的这串字符就是注册得到的隧道ID,先运行上面这句,然后再运行main.py,此时就可以在外部网络通过访问http://daydream.free.idcfengye.com 来查看监控视频.

2020系统综合实践 期末大作业 27组_第11张图片

查看到的监控视频

2020系统综合实践 期末大作业 27组_第12张图片

三.项目部署至docker

1.项目结构

2020系统综合实践 期末大作业 27组_第13张图片

2.自定义镜像

(1)dockerfile

FROM opencv1
RUN mkdir /myapp
WORKDIR /myapp
COPY myapp .
2020系统综合实践 期末大作业 27组_第14张图片

(2)构建镜像

docker build -t opencv3 .

2020系统综合实践 期末大作业 27组_第15张图片 2020系统综合实践 期末大作业 27组_第16张图片

(3)创建并运行容器

docker run -it --device=/dev/vchiq --device=/dev/video0 --name security opencv3 

安装依赖

pip install -r requestments.txt
2020系统综合实践 期末大作业 27组_第17张图片

(4)保存镜像

2020系统综合实践 期末大作业 27组_第18张图片

(5)运行

进入上面创建好的容器内,运行main.py

pythn3 main.py

2020系统综合实践 期末大作业 27组_第19张图片 2020系统综合实践 期末大作业 27组_第20张图片 2020系统综合实践 期末大作业 27组_第21张图片

四.总结

(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%

你可能感兴趣的:(2020系统综合实践 期末大作业 27组)