视频关键字、关键帧过滤系统
综述
本系统可以根据用户提供的关键字和人脸图像,实时过滤选定文件中的内容,并提取出包含关键字和人脸图像的视频片段。
例如:由于某些原因我们要封杀xz,我们不希望一段视频里出现任何涉及xz的内容,包括语音和图像,那么我们会输入过滤关键字:xz(名字),然后输入xz的人脸,然后系统开始截取视频中有xz的语音,并且比对每一帧是否包含xz的人脸,将包含xz的片段剪出来!
源码可从此处获得
工程实现
流媒体服务模块
-
使用multiprocessing.Process创建一个进程,让该进程管理流媒体服务,和主进程并行。
vdsource = multiprocessing.Process(target=node_start)
-
使用subprocess创建子进程,后台模式启动流媒体服务。使用阻塞模式,将该进程的输出重定向到当前进程,实时打印流媒体服务的状态。
subprocess.check_output(command, stdin=open(os.devnull), shell=use_shell)
-
流媒体服务基于Nodejs实现,可以通过Rtmp、Http的方式向该服务推流,客户端通过指定的播放地址来向该服务拉取视频流,实现流媒体播放。
Flask框架
- 使用Flask框架来进行前后端通信,前端界面通过Get和Post方式向后台发送请求,以获得相应数据
- Get方式获得后台视频库中的本地视频信息,包括视频文件名,路径和缩略图。
- Post方式主要用于过滤器启动后返回检测到的片段机器信息,以及前台对后台过滤器的操作命令(包括初始化、开始、停止和状态检查)
过滤器(Vdfilter)
- 类初始化主要完成基础变量设置,包括过滤模式(本地和在线)、视频地址、推流地址
- 初始化中会创建一个流对象,该对象自底向上继承了推流、语音识别、视频切边以及文件处理所有类,方便各个类之间共享状态信息
- 用多线程方式初始化流对象中三个主要过程为三个线程,即视频切片(在线模式为视频抓流)、语音识别和过滤、视频推流三个线程并设置为守护模式,主进程退出后,这些线程自动退出
- 强制停止:这里涉及到强制kill一个python线程,使用
_async_raise()
函数实现
def _async_raise(tid, exctype):
"""raises the exception, performs cleanup if needed"""
tid = ctypes.c_long(tid)
if not inspect.isclass(exctype):
exctype = type(exctype)
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
if res == 0:
raise ValueError("invalid thread id")
elif res != 1:
# """if it returns a number greater than one, you're in trouble,
# and you should call it again with exc=NULL to revert the effect"""
ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
raise SystemError("PyThreadState_SetAsyncExc failed")
# 调用方式:
publish = threading.Thread(target=newfilter.push) # 创建一个线程
publish.start()
if publish.is_alive():
_async_raise(publish.ident, SystemExit)
publish.join()
- clean() 函数在过滤器自然退出后看门狗清理进程极临时文件
- get_fildthumbpic() 函数用于返回当前已经过滤出来的视频片段缩略图,返回给客户端
- get_chipsdata() 返回过滤缓存队列中的数据,即已过滤好的片段,返回后从队列中删除
看门狗
- 使用多线程的方式在主进程中创建一个线程,执行看门狗函数
- 看门狗每隔5秒检测一次主进程中的过滤任务是否已经结束,如果结束,销毁
Vdfilter
对象,以便开始新的视频过滤。
视频切片CutVideo.py
- 这个模块功能相对简单并独立,主要使用ffmpeg来将视频每10s为一个片段切片,存入指定文件夹,用subprocess方式调用shell命令启动ffmpeg,以备过滤
视频抓流FetchStream.py
- 该模块和视频切片属于同级模块,当过滤模式为在线模式时,该模块主要完成抓取流媒体,并存入指定文件夹,以备过滤
- 抓流采用多线程方式抓取
语音识别+人脸检测+过滤主函数AutoFilter
- 该模块实现了两个功能:1,对上一级切好(或者抓好)的视频进行语音识别+人脸检测;2,根据识别和检测结果和要检测的内容进行比对实现过滤
- 关于语音识别和人脸检测的算法方案详见算法部分,本部分只讨论工程实现
- filter_start():过滤器控制函数,实时监测过滤状态和外界指令,如过滤完成或者接收到停止信号,退出循环,并等待主进程终结
- filter_save():过滤实现,对传入的文件调用语音识别和人脸识别检测器进行检测,若检测到切割检测到的片段,并保存到指定文件夹
推流模块Stream.py
- 该模块功能也相对简单,当过滤模式为本地模式,则将原视频推流到流媒体服务,客户端直接拉取流媒体播放
- 当过滤模式为直播模式,客户端从直播源直接拉取,此处不做任何任务。
算法篇
- 语音识别
- 人脸识别
- 人脸识别主要采用dlib库中的人脸检测和特征提取详见代码
- 为了提高检测速度以达到实时监测,主要采取了这几个策略:
- 若源视频分辨率太高则相应降低分辨率,若分辨率太低相应增大图片尺寸
- 使用dib的GPU版本,使用GPU加速检测
- 通过用矩阵运算实现人脸特征匹配的加速
心得体会
- 本项目前期遇到的最大问题就是关于多进程和多线程的处理问题,本项目有三个模块需要并行执行,而其中的抓流和语言识别模块又需要多线程处理实现高并发。做此项目之际,初次使用多进程和多线程,使用方式甚是简陋,而且很不规范(进程套线程,线程套进程)。若有大神能实现一个更好的版本,可否给小弟瞻仰学习一番,以发扬当代码农之同甘共苦精神!
- 项目中后期遇到的主要问题就是检测精度不高并且速度问题,语音识别由于包装了原有的工程,没有调整一些参数,使得检测不太精准。人脸识别即使开了GPU,当图像分辨率变大时候计算效率依然捉襟见肘,GPU使用效率却不高,由此可见计算密集型代码使用C++实现的重要性!
- 其它,想起来再补充吧,暂时就这了
写在最后
- 本系统为笔者研究生练手项目,代码设计思路笨拙,实现方式蹩脚。功能虽然实现了,但是却有无数bug正在潜伏,伺机而动。
- 前期致力于功能实现,后面又有论文追赶(主要是菜),此项目就搁置,没有做过多的优化!