csuzilong 2019-09-02
Unix
Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊。普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。
子进程永远返回0,而父进程返回子进程的ID。这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID。
由于Windows没有fork调用,Windows上无法使用,因此python提供了一个跨平台的多进程支持。multiprocessing模块就是跨平台版本的多进程模块。多进程 Multiprocessing 和多线程 threading 类似, 都是在 python 中用来并行运算的. 由于python存在GIL全局锁的功能,影响了python的多线程并行计算能力,为了解决此类问题,python提供了多进程的概念。
queue的功能是将每个核或线程的运算结果放在队里中, 等到每个线程或核运行完毕后再从队列中取出结果, 继续加载运算。因为多线程或者多进程调用的函数不能有返回值, 所以使用Queue存储多个线程或者进程运算的结果
有了python多进程的基础知识,我们来优化上期代码,来进行视频流的实时对象检测
1 导入需要的第三方库
import 第三方库
2 初始化训练模型
#14 # 15 输入prototxt与caffe 的模型文件地址
#17 class列表是SSD caffe检测模型中的20个label标签,还包括一个background
#21 针对每个label,随机建一个颜色,以便后期检测图片时 ,使用不同的颜色框,以便区分
# 22 使用cv的dnn加载模型数据
初始化模型
3 新建多进程处理任务函数
由于net.forward 函数是阻塞式函数,顾名思义本次检测未完成前,不允许程序执行下一条指令,所以导致了图片检测的速度较慢,我们多进程函数主要来处理此函数,以便快速检测
# 28 判断输入队列中是否有数据
# 29 使用get方法获取输入队列中的图片
#30 31 设置图片尺寸,并计算blob值
# 32 blob值传递神经网络
#33 正向传递预测
#34 把预测结果通过put方法加入队列
4 新建video 类函数提取视频中的图片
#37 初始化预测值,默认none
#38 40 创建VideoStream、创建FPS
#43 读取视频中的帧图片
#44 调整图片宽度
#45 获取图片宽度与高度
#46 47 若输入队列中无image数据,向输入队列中保存image数据
#48 49 若输出队列中有检测结果数据, 获取检测结果数据
5 获取检测结果,并把检测结果加入图片中
本段代码跟前几期的文章只在第一条上有差别,因为我们前期初始化了detections,我们判断一下detections是否是none,来加速无效代码的运行
6 显示最终图片
当for循环结束后,我们把当前帧获取的图片检测结果实时显示出来,由于多进程的设计,当进程执行时,video_start函数可以继续执行,并不是阻塞式程序,便加快了图片检测的速度,
当输入q退出时,便可以看到多进程处理的速度大概是每秒60帧
电脑配置不是太高: i5+4G
7、主函数入口
由于多进程的特殊性,新建进程与启动进程,需要在if __name__ == '__main__':函数中
#81 初始化输入队列,设置最大值1
#82 初始化输出队列,设置最大值1
设置输入输出队列都是1,避免进程中数据混淆
# 初始化多进程,进程初始化,使用如下函数,特别注意:target 是多进程的函数名称,无小括号
p = Process(target=multiProcess, args=(inputQueue, outputQueue,))
'''
p.daemon = True: 主进程运行完不会检查子进程的状态(是否执行完),直接结束进程;
p.daemon = False: 主进程运行完先检查子进程的状态(是否执行完),子进程执行完后,直接结束进程;
daemon默认值为False
'''
#86 开始进程
#87 开始实时视频检测
以上便是完整的代码结构,opencv没有GPU加速,采用多进程的方式,也可以加快代码运行,使用多进程,大大提高了代码运行速度,也加速了图片检测的速度,我们观看实时生成的视频时,看着也比较流畅,没有了先前的卡顿感
多核,多任务
微&&信搜索:启示AI科技
体验不一样的AI工具
预训练模型地址可参考往期文章