前言
okey, 差不多,再写一篇博文,那么五月份应该也开始发布了四篇文章了。那么今天的话,带来的也是一个深度学习项目。有关注我的朋友因该知道,其实先前的话,我就有再不断得去介绍类似的项目。或者说是把这个项目的一些技术细节分模块给说了出来。
那么今天的话也是把一个bate版本给开放一下了。关于具体的算法方面的实现和基本原理的话,看我先前的文章都是看得到的,现在咱们更像是把这个系列进行整合了。串起来了,作为了一个系列。当然按照我的习惯是我不喜欢拆来的,但是没办法东西确实多,合并在一起,文章太长,而且也不好写,同时对我来说也不好看。
那么废话不多说,看看这个bate版本有啥吧。
首先:
智能安防系统是一种利用先进的技术手段来提高安全性和便利性的系统,它可以让使用者更加方便、快捷、准确地控制和监控其所在环境。智能安防系统通常包括视频监控系统、报警系统、智能门禁系统等,通过这些不同的设备和技术手段,可以实现对区域内的人员、车辆和物品的实时监控、识别和管理,进而保障用户的生命财产安全。
智能安防系统的开发与实战需要涉及多个方面的知识和技能,包括硬件设计、软件开发、数据库管理、网络通信等等。在开发过程中,需要对系统的需求进行详细分析和设计,选择合适的设备和技术手段,并进行系统的实现、测试和调试,确保系统的稳定性和可靠性。
在实际应用中,智能安防系统已经被广泛应用于商业建筑、住宅区、公共场所等,为用户提供了全面的安全保障和智能化的便利。例如,通过视频监控系统可以实现对区域内的人员、车辆和物品的实时监控和记录,报警系统可以在发现异常情况时及时向使用者发出警报提示,智能门禁系统可以实现对进出人员和车辆的自动识别和控制等等。这些功能的实现不仅为用户提供了全面的安全保障,同时也提高了用户的生活和工作效率,为用户带来了更加智能化、便捷化的使用体验。
首先是咱们的这个首页:
进去之后是这样的:
当然最重要的是这个:
对以及录入人员的一个信息统计。
算法实现
首先,明确一点,我们先前所作的功课,那就是原来,我们的这算法部分是在先前就已经说明了的:
具备人脸识别功能的多目标在线实时行为检测(yolov5+deepsort+slowfast)
整个项目结构你都可以看到:
和我们先前透露的其实非常像。
只是现在,我们加入了flask服务端,当然还有对我们的算法进行修改提供,对应服务的功能。
人脸识别
在我们的算法当中,人脸识别的部分还是老样子。
在这里主要是通过dlib实现,不过这段代码先前给过,就不复述了。
但是,为了能够符合具体的功能,我们在这个基础代码的基础上,进行了一个封装。这样一来就可以实现到人脸识别的功能,并且整合到我们的Flask服务当中去。
class ProcessAlg(object):
face_name_id_cache = {}
def set_cache(self,face_name_id_cache):
self.face_name_id_cache = face_name_id_cache
def updateFace(self,stream,faceid):
"""
更新人脸信息
:param video:
:param faceid:
:return:
"""
frame_array = np.frombuffer(stream.getvalue(), dtype=np.uint8)
cap = cv2.VideoCapture(frame_array)
result = FaceImg.query.filter(FaceImg.face_id == faceid).all()
if(len(result)==0):
raise Exception("no such face!!!")
# 获取视频帧数
num_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
# 计算需要保留的帧数
sample_rate = max(num_frames // 50, 1)
images = []
for i in range(num_frames):
ret, frame = cap.read()
if not ret:
break
if i % sample_rate == 0:
images.append(frame)
# 释放资源
cap.release()
#处理人脸信息
col = Collection()
face = result[0]
col.collection_images(images,face.imgs_path, None, None)
#更新人脸信息
build = BuildFace()
def up_model_data(face_id,face_name,feature_vector):
now = datetime.now()
session = SessionFactory()
record = FaceData.query.filter(FaceData.face_id == face_id).first()
record.update_time = now
record.feature_vector=feature_vector
session.commit()
build.building_select([int(face.face_id)],[int(face.face_id)],up_model_data,None)
def creatFace(self,stream,face_name):
"""
stream = io.BytesIO(file.read())
创建人脸
注意,这些信息,我们在数据库里面有备份
同时在csv文件当中都是有备份的
:param video:
:param face_name:
:return:
"""
frame_array = np.frombuffer(stream.getvalue(), dtype=np.uint8)
cap = cv2.VideoCapture(frame_array)
# 获取视频帧数
num_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
# 计算需要保留的帧数
sample_rate = max(num_frames // 50, 1)
images = []
for i in range(num_frames):
ret, frame = cap.read()
if not ret:
break
if i % sample_rate == 0:
images.append(frame)
# 释放资源
cap.release()
#处理人脸信息
col = Collection()
def process(face_name,file_path,face_id):
face_img = FaceImg(face_name=face_name, create_time=datetime.now(),
imgs_path=file_path,face_id=face_id)
session = SessionFactory()
session.add(face_img)
session.commit()
#此时col将完成人脸图像的采集,同时完成采集之后直线process函数内的方法
col.collection_images(images,None,process,face_name)
#此时数据库当中已经有对于的人的名字了,但是还不够,我们还需要在faceName.txt当中记录
faceNameTxt = FACE_FILE.faceName_path
with open(faceNameTxt, 'a+') as f:
f.write('{}n'.format(face_name))
#开始构建人脸了
session = SessionFactory()
result = session.query(FaceImg).filter_by(face_name=face_name).all()
face = result[0]
build = BuildFace()
def save_model_data(face_id,face_name,feature_vector):
now = datetime.now()
data = FaceData(
face_id=face_id, face_name=face_name, feature_vector=feature_vector,
create_time=now, update_time=now
)
session = SessionFactory()
session.add(data)
session.commit()
build.building_select([int(face.face_id)],[int(face.face_id)],save_model_data,face_name)
def saveVideo(self,image_list,start_time,end_time,cream_id):
"""
:param image_list: cv2.imread()对象列表
:return:
"""
current_time = datetime.datetime.now()
year = current_time.year
month = current_time.month
day = current_time.day
image_uuid = str(uuid.uuid4())
save_dir = os.path.join('Resources', 'video', str(year), str(month), str(day))
save_path = os.path.join(save_dir, f'{image_uuid}.mp4')
# 如果保存路径不存在,就创建它
if not os.path.exists(save_dir):
os.makedirs(save_dir)
# 获取第一张图片的大小
height, width, channels = image_list[0].shape
# 定义VideoWriter对象
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter(save_path, fourcc, 20.0, (width, height))
# 写入每一帧图像
for image in image_list:
out.write(image)
# 释放资源
out.release()
session = SessionFactory()
# 创建一条新的数据
new_video = Videos(
start_time=start_time,
end_time=end_time,
in_day=start_time.date(),
save_path=save_path,
cream_id=cream_id
)
session.add(new_video)
session.commit()
session.close()
def __save_unimg(self,img,id,time_im,cream_id,action_name):
current_time = datetime.datetime.now()
year = current_time.year
month = current_time.month
day = current_time.day
image_uuid = str(uuid.uuid4())
save_dir = os.path.join('Resource', 'img', str(year), str(month), str(day))
save_path = os.path.join(save_dir, f'{image_uuid}.jpg')
# 如果保存路径不存在,就创建它
if not os.path.exists(save_dir):
os.makedirs(save_dir)
# 保存图片
cv2.imwrite(save_path, img)
face_unusual = FaceUnusual()
face_unusual.active_time = time_im
face_unusual.face_id = id
face_unusual.unusual_img = save_path
face_unusual.cream_id = cream_id
face_unusual.action_name = action_name
session = SessionFactory()
# 将FaceUnusual实例添加到会话中并提交更改
session.add(face_unusual)
session.commit()
session.close()
def unusualAction(self,im, ava_label, box, time_im,detectFace:DetectFace,cream_id):
bad_actions = {"fall down":"摔倒","jump/leap":"跳跃","lie/sleep":"躺下","run/jog":"奔跑"}
# 传入图片,传入动作标签,还有识别的区域,发生时间,人类识别器
if(ava_label in bad_actions):
names = detectFace.detect_from_image(im)
if(len(names)):
for name in names:
t_id = self.face_name_id_cache.get(name,-1)
if(t_id==-1):
session = SessionFactory()
#更新一下cache
query_result = session.query(FaceImg).filter_by(face_name=name).first()
if query_result:
face_id = query_result.face_id
self.face_name_id_cache[name] = face_id
#然后再尝试更新,如果还是不行,说明就是陌生人
t_id = self.face_name_id_cache.get(name, -1)
self.__save_unimg(im,t_id,time_im,cream_id,bad_actions.get(ava_label,None))
这个工具类基本上实现了我们的,这个人脸录入,人脸信息更新的功能。同时对接到我们的数据库。
数据库设计
没有办法,我们需要对一些数据进行保存,所以我们设计了几个简单的表,用来存储。
class BehaviorLevel(Base):
__tablename__ = 'behavior_levels'
level_id = Column(INTEGER(11), primary_key=True, comment='等级id')
level_name = Column(String(255, 'utf8_unicode_ci'), nullable=False, comment='名称')
class Behaviors(Base):
__tablename__ = "behaviors"
behaviors_id = Column(Integer, primary_key=True, autoincrement=True, comment="行为主键")
behaviors_name = Column(String(255), nullable=False, comment="行为名称")
behaviors_level = Column(Integer, comment="行为等级")
def __str__(self):
return "behaviors_id:{},behaviors_name:{},behaviors_level:{}".format(self.behaviors_id,self.behaviors_name,self.behaviors_level)
class Creams(Base):
""" 必须继承Base """
# 数据库中存储的表名
__tablename__ = "creams"
creams_id = Column(Integer, primary_key=True, autoincrement=True, comment="摄像头主键")
creams_name = Column(String(255), nullable=False, comment="摄像头名称")
start_time = Column(DateTime, nullable=False, comment="开启时间")
open_cr = Column(Integer, comment="开启摄像头")
source_path = Column(String(255), nullable=False, comment="摄像地址")
def __str__(self):
return "creams_id:{},creams_name:{},start_time:{},open_cr:{}"
.format(self.creams_id,self.creams_name,self.start_time,self.open_cr)
class FaceData(Base):
__tablename__ = 'face_data'
data_id = Column(BIGINT, primary_key=True, comment='数据编号')
face_id = Column(BIGINT, comment='编号')
face_name = Column(String(255, 'utf8_unicode_ci'), nullable=False, comment='人脸名称')
feature_vector = Column(String(255, 'utf8_unicode_ci'), comment='特征向量')
update_time = Column(DATETIME)
create_time = Column(DATETIME, comment='创建时间')
def __str__(self):
return "data_id:{},face_id:{},face_name:{},feature_vector:{},update_time:{},create_time:{}".
format(self.data_id,self.face_id,self.face_name,self.feature_vector,self.update_time,self.create_time)
class FaceImg(Base):
__tablename__ = 'face_img'
img_id = Column(BigInteger, primary_key=True, comment='编号')
face_name = Column(String(255), nullable=True, comment='人脸名称')
create_time = Column(DateTime, nullable=False, comment='创建时间')
imgs_path = Column(String(255), nullable=True, comment='文件路径')
face_id = Column(BigInteger, nullable=False, comment='人脸编号')
def __str__(self):
return f"FaceImg(img_id={self.img_id}, imgs_path='{self.imgs_path}', face_id={self.face_id})"
class FaceUnusual(Base):
__tablename__ = 'face_unusual'
unusual_id = Column(BigInteger, primary_key=True, comment='异常id')
active_time = Column(DateTime, nullable=False, comment='发生时间')
video_id = Column(BigInteger, nullable=False, comment='videoId')
face_id = Column(BigInteger, nullable=False, comment='人脸id')
unusual_img = Column(String(255), nullable=False, comment='捕捉图像')
cream_id = Column(Integer, nullable=False, comment='摄像头id')
action_name = Column(String(255), nullable=False, comment='动作名称')
def __str__(self):
return f"FaceUnusual(unusual_id={self.unusual_id}, active_time='{self.active_time}', video_id={self.video_id}, face_id={self.face_id}, unusual_img='{self.unusual_img}', cream_id={self.cream_id},action_name={self.action_name})"
class SystemInfo(Base):
__tablename__ = 'system_info'
id = Column(Integer, primary_key=True)
name = Column(String(255))
key = Column(String(255))
root_user = Column(String(255))
index = Column(String(255))
start_time = Column(DateTime)
end_time = Column(DateTime)
def __repr__(self):
return f"<SystemInfo(name='{self.name}', key='{self.key}', root_user='{self.root_user}', index='{self.index}', start_time='{self.start_time}', end_time='{self.end_time}')>"
class Videos(Base):
__tablename__ = 'videos'
video_id = Column(BigInteger,primary_key=True, autoincrement=True,comment="视频id主键")
start_time = Column(DateTime, nullable=False, comment='开始时间')
end_time = Column(DateTime, nullable=False, comment='结束时间')
in_day = Column(Date, nullable=False, comment='视频日期')
save_path = Column(String(255), nullable=False, comment='视频地址')
cream_id = Column(Integer, nullable=False, comment='摄像头id')
# 添加时间索引
__table_args__ = (
Index('idx_videos_start_time', start_time),
)
def __str__(self):
return f"Videos(video_id={self.video_id}, start_time='{self.start_time}', end_time='{self.end_time}', in_day='{self.in_day}', save_path='{self.save_path}', cream_id={self.cream_id})"
之后的话,我们在算法运行过程当中就会将结果存储起来
算法启动
之后的话是我们算法启动。
这里的话,我们还是设计了一个启动接口。同时封装了一个进程池。
class ProcessPool:
def __init__(self, max_processes=mp.cpu_count()):
self.max_processes = max_processes
self.tasks = queue.Queue()
self.processes = {}
self._stop_event = mp.Event()
# 注册 Ctrl+C 信号处理函数,确保在程序被终止时能够正常清理资源
signal.signal(signal.SIGINT, self._handle_signals)
signal.signal(signal.SIGTERM, self._handle_signals)
def add_process(self, process_id, target, args=()):
if len(target.__code__.co_varnames) > len(args):
raise ValueError("Too few arguments")
p = mp.Process(target=self._wrapper, args=(process_id, target, args), daemon=True)
p.start()
self.processes[process_id] = p
while len(self.processes) >= self.max_processes:
for process_id, p in list(self.processes.items()):
if not p.is_alive():
del self.processes[process_id]
break
else:
time.sleep(0.1)
if not p.is_alive():
return
while not self.tasks.empty():
process_id, target, args = self.tasks.get()
p = mp.Process(target=self._wrapper, args=(process_id, target, args), daemon=True)
p.start()
self.processes[process_id] = p
def stop(self):
"""
停止进程池。
"""
self._stop_event.set()
for p in self.processes.values():
p.terminate()
self.processes.clear()
def restart(self, process_id):
"""
重启指定 id 的进程。
:param process_id: 进程id。
"""
if process_id not in self.processes:
raise ValueError("Process not found")
p = self.processes[process_id]
p.terminate()
p.join()
target, args = self.tasks.queue[0][1:]
p = mp.Process(target=self._wrapper, args=(process_id, target, args), daemon=True)
p.start()
self.processes[process_id] = p
def kill(self, process_id):
"""
结束指定 id 的进程。
:param process_id: 进程id。
"""
if process_id not in self.processes:
raise ValueError("Process not found")
p = self.processes[process_id]
p.terminate()
def _handle_signals(self, signum, frame):
"""
信号处理函数,用于在进程被终止时正常清理资源。
"""
self.stop()
@staticmethod
def _wrapper(process_id, target, args):
"""
包装目标函数,以便能够支持动态参数。
:param process_id: 进程id。
:param target: 进程要执行的目标函数。
:param args: 目标函数的参数列表。
"""
target(*args)
class AlgApplication(object):
"""
算法启动类,多个摄像头开启
然后开启多个进程
"""
def __init__(self):
self.open = True
session = SessionFactory()
self.face_Names_cahe = {}
for face_img in session.query(FaceImg).all():
self.face_Names_cahe[face_img.face_name] = face_img.face_id
session.close()
self.maxPoolSize = sysConfig.maxPoolSize
self.pool = ProcessPool(max_processes=self.maxPoolSize)
def go_fire(self,creams:Creams,face_name_cache):
"""
1. 初始化工具类
2. 初始化算法
:param cream_id:
:return:
"""
alg_process = ProcessAlg()
alg_process.set_cache(face_name_cache)
pose = PoseRec()
source_path = creams.source_path
try:
source_path_ = int(source_path)
source_path = source_path_
except Exception as e:
pass
"""
process1:是保存图片的
process2:是保存视频的
"""
pose.detect_from_video_realTime(source_path,
str(creams.creams_id),
show=False,
process1=alg_process.unusualAction,
process2=alg_process.saveVideo,
cream_id=creams.creams_id)
def init_start(self):
#初始化,开启摄像头
session = SessionFactory()
# 使用查询语句读取所有 Creams 表中的数据
creams_list = session.query(Creams).all()
index = 0
# 开启所有任务
for creams in creams_list:
if(creams.open_cr==1):
if(index>self.maxPoolSize):
warnings.warn('摄像头数量大于线程池最大容量,无法开启全部摄像头', UserWarning)
continue
self.pool.add_process(creams.creams_id,self.go_fire,args=(creams,self.face_Names_cahe,))
index+=1
def stop_cream(self,cream_id):
"""
关闭摄像头
:param cream_id:
:return:
"""
self.pool.kill(cream_id)
def start_cream(self,creams):
"""
开启摄像头
:param cream_id:
:return:
"""
self.pool.add_process(creams.creams_id,self.go_fire,args=(creams,self.face_Names_cahe,))
def restart_cream(self,cream_id):
self.pool.restart(cream_id)
def run(self):
"""
此时执行的轮询函数,每隔1分钟,轮询数据库设置,这里我已经不想再写什么进程通讯了
:return:
"""
# 初始化,开启摄像头
self.init_start()
#每隔一分钟轮询
while(self.open):
time.sleep(60)
session = SessionFactory()
creams_list = session.query(Creams).all()
for creams in creams_list:
if (creams.open_cr == 1):
if(creams.creams_id not in self.pool.processes):
if (len(self.pool.processes)+1> self.maxPoolSize):
warnings.warn('摄像头数量大于线程池最大容量,无法开启全部摄像头', UserWarning)
continue
self.pool.add_process(creams.creams_id, self.go_fire, args=(creams, self.face_Names_cahe,))
elif(creams.open_cr==0):
if(creams.creams_id in self.pool.processes):
self.pool.kill(creams.creams_id)
之后在我们的,这里启动,也就是app.py里面
"""
负责提供curd接口
"""
from flask import Flask, Blueprint
from flask_cors import CORS # 引入 CORS
from client.server.blueprints.test_api import test_api
from client.server.blueprints.face_img_api import face_imgs_api
from client.server.blueprints.behavior_levels_api import behavior_levels_api
from client.server.blueprints.behaviors_api import behaviors_api
from client.server.blueprints.creams_api import creams_api
from client.server.blueprints.face_data_api import face_data_api
from client.server.blueprints.face_unusuals_api import face_unusual_api
from client.server.blueprints.system_info_api import system_info_api
from client.server.blueprints.video_api import video_api
from client.server.blueprints.common_api import common_api
from client.server.sysConfig import is_Show
app = Flask(__name__,static_folder='./Resources')
CORS(app, resources=r'/*') # 启用 CORS,允许所有跨域请求
app.register_blueprint(test_api)
app.register_blueprint(behaviors_api)
app.register_blueprint(behavior_levels_api)
app.register_blueprint(creams_api)
app.register_blueprint(face_data_api)
app.register_blueprint(face_imgs_api)
app.register_blueprint(face_unusual_api)
app.register_blueprint(system_info_api)
app.register_blueprint(video_api)
app.register_blueprint(common_api)
#Dome演示模式,不启动,不执行,不启动云端,带不动!!!
import multiprocessing
def worker():
from client.server.alg_utils import AlgApplication
"""同步启动算法,演示时不开启,开不动"""
algAppliction = AlgApplication()
algAppliction.run()
if __name__ == '__main__':
if(not is_Show):
p = multiprocessing.Process(target=worker)
p.start()
app.run(debug=True,host="127.0.0.1",port=8000)
不过,这里值得一提的是,这个只是我们客户端的代码。
我们完整的架构其实是这样的:
客户端其实又是这样的:
遗憾的是,由于开发时间紧张,这个端我没做。
项目完成简介
人脸识别
ok,现在让我们总览一下这个项目。
在本项目中,人脸识别主要使用了第三方库dlib提供的算法实现。通过采集人脸图像,对其进行特征提取计算,得到对应人脸的特征向量。待识别时,重新计算待识别人脸的特征向量,并将其与库中已录入人脸进行比较,从而完成对比。该过程的流程下:
运行效果图如下:
多目标检测跟踪与行为识别
在本项目中,我们采用了 YOLOv5、DeepSORT 和 SlowFast 三种模型,并将它们有机地结合起来,成功实现了多目标的行为动作检测任务。这三个模型分别拥有不同的优势和特点,通过相互配合,可以更好地满足视频监控的多目标检测和行为动作识别需求。首先,YOLOv5是一个轻量化的网络架构,它能够高效、快速地进行目标检测,并且保证了一定的准确率。它采用了一些优秀的设计思路,比如采用SPP结构对输入图片进行处理,将不同尺度的信息融合在一起以提高检测精度;使用FPN结构进行特征融合,实现多层次的特征提取和分类;还有针对小目标的改进等。此外,YOLOv5还采用了后处理算法,能够对检测到的目标进行筛选和分类,并给出目标的位置和姿态信息。因此,YOLOv5在本项目中被用来进行多目标检测任务。然而在本项目当中,仅仅进行目标检测是不够的,还需要对目标进行跟踪和行为动作的识别。因此,我们引入了 DeepSORT 模型。DeepSORT模型是一种基于深度学习的多目标跟踪模型,能够对检测到的目标进行追踪,从而保证目标的连续性和正确性。它采用了一些创新的设计思路,比如基于外观和运动特征进行目标的相似性匹配,使用卡尔曼滤波对目标位置进行预测,以及采用一个时序嵌入网络(TSN)来学习目标的运动表示等。此外,DeepSORT还能够处理多个相似的目标,通过矩阵计算和模板匹配的方式区分它们,从而提高了跟踪的准确度和鲁棒性。在本项目中,我们将YOLOv5模型的检测结果输入到DeepSORT模型中,DeepSORT利用目标的运动轨迹和特征进行目标跟踪DeepSORT 模型还可以处理多个相似的目标,并通过矩阵计算和模板匹配等方式进行区分,提高目标跟踪的准确度和鲁棒性。最后,我们应用 SlowFast 模型来实现对目标的行为动作识别。SlowFast模型是一种先进的视频分类和动作识别网络结构,在本项目中被用来实现目标的行为动作识别。SlowFast模型的优点在于能够同时捕捉目标的上下文信息和细节信息,从而更加精准地进行行为动作识别。它采用了慢速和快速两个分支的设计,其中慢速分支可以捕捉目标的上下文信息,快速分支可以捕捉目标的细节信息,从而更好地进行动作分类和识别。在本项目中,我们将DeepSORT模型得到的目标轨迹输入到SlowFast模型中,经过一系列卷积、池化和全连接层的处理,可以输出目标在轨迹上所表现出的不同动作行为。之后当检测到了对应的危险动作之后,我们将立即对改目标进行人脸识别,并记录对应的时间点,视频截图,已经对应的视频时间点。便于后续快速调取当时发生的时间点视频,有利于后期进行安全评估和分析。
Web 操作界面
我们的应用不仅有基于 Vue 框架开发的 Web UI,同时基于 Flask 框架开发的后端服务器,二者紧密配合,形成了一个完整的完整的项目应用。Vue 框架提供了友好的用户界面和直观的交互方式,用户可以通过简单的操作调整视频源和视频参数,启动和停止多目标行为动作检测任务,并实时查看检测结果和统计信息。同时,在 Web UI 中我们使用 Echarts 图表库实现了一系列可视化图表,如柱状图、折线图等,让用户更加清晰地了解检测结果和统计信息。Flask 框架提供了高效稳定的后端服务,处理前端传来的视频数据,并进行多目标行为动作检测。同时,将检测结果实时推送给前端,并在前端可视化显示。通过提供 API 接口和数据库服务,方便用户进行数据查询和管理。同时,根据配置参数和环境变量,满足应用的各种需求和限制。将二者相互结合,我们实现了全栈式的应用开发,使得用户可以更加便捷地进行多目标行为动作检测任务,实现了全方位的数据处理与分析:
摄像头查看情况:
查看人脸
录入和修改页面
之后是关于,成员行为的记录,此模块主要分为两大块,人员近期的行为记录,对应时间点的记录,以及对应时间点的详细情况,包括事发时间,事发视频,以及对应的精准视频关键帧。
总结
以上就是全部内容了,其实也是当初做大创的内容,欸,不得不说 现在基本上都是做Application + 机器学习/深度学习, 真的卷哇。不过始终记住,工具就是工具,没什么特别的,用就对了,学就对了,你不学,被干趴下的就是你。
那么这里的话,来个小小的私心,点赞超过100才开源,我只是默默分享~(注: the_way_inf == Huterox)