本帖最后由 395876134 于 2019-8-9 16:25 编辑
本教程视频直播回看:
本教程基于RK3399pro开发板,使用fedora 28系统,另需usb鼠标、usb键盘、hdmi显示器、usb摄像头、串口线连接开发板
一. 搭建开发环境
安装rknn-toolkit及其依赖库,[url=http://t.rock-chips.com/wiki.php?mod=view&id=36]按照wiki教程安装环境
二、代码路径
原始代码:https://github.com/DuinoDu/mtcnn.git
本教程代码:https://github.com/chenshiqin/mtcnn.git
三、MTCNN算法流程
1、构建图像金字塔:首先将图像进行不同尺度的变换,构建图像金字塔,以适应不同大小的人脸的进行检测。 2、P-Net:全称为ProposalNetwork,通过浅层的CNN快速生成候选窗口,该网络全部由卷积层实现,获取到候选人脸窗和人脸窗的回归向量 (bounding box regression vectors),基于人脸窗的回归向量对人脸窗进行校正,然后对所有人脸窗进行NMS(非极大值抑制),合并高度重叠的人脸窗。 3、R-Net:全称为Refine Network,其基本的构造是一个卷积神经网络,相对于第一层的P-Net来说,增加了一个全连接层,因此对于输入数据的筛选会更加严格。在图片经过P-Net后,会留下许多预测窗口,我们将所有的预测窗口送入R-Net,这个网络会滤除大量效果比较差的候选框,最后对选定的候选框进行校正和NMS,进一步优化预测结果 4、O-Net:全称为OutputNetwork,基本结构是一个较为复杂的卷积神经网络,相对于R-Net来说多了一个卷积层。O-Net的效果与R-Net的区别在于这一层结构会通过更多的监督来识别面部的区域,而且会对人的面部特征点进行回归,最终输出五个人脸面部特征点。 四、运用的主要公式 1、人脸检测这就是一个分类任务,使用交叉熵损失函数即可:
2、Bounding box regression这是一个回归问题,使用平方和损失函数: 3、人脸特征点定位这也是一个回归问题,目标是5个特征点与标定好的数据的平方和损失: 4、多任务训练不是每个sample都要使用这三种损失函数的,比如对于背景只需要计算交叉熵损失,不需要计算别的损失,这样就需要引入一个指示值指示样本是否需要计算某一项损失。最终的训练目标函数是:
5、非极大抑制NMS 五、问题点介绍 1、MTCNN算法可以接受任意尺度的图片吗?为什么需要金字塔转换? 因为第一阶段的P-NET是一个全卷积网络(Fully Convolutional Networks) 卷积、池化、非线性激活都是一些可以接受任意尺度矩阵的运算,而全连接运算是需要规定输入。我们的P-net网络结构没有全连接层,因此图片尺度可以是任意的; 由于图片中的人脸的尺度有大有小,目标检测本质上来说是目标区域内特征与模板权重的点乘操作;那么如果模板尺度与目标尺度匹配,自然会有很高的检测效果。 2、设置合适的最小人脸尺寸和缩放因子为什么可以优化计算效率? minsize是指你认为图片中需要识别的人脸的最小尺寸(单位:px)。factor是指每次对边缩放的倍数 我们已经知道,第一阶段会多次缩放原图得到图片金字塔,目的是为了让缩放后图片中的人脸与P-NET训练时候的图片尺度(12px * 12px)接近。怎么实现呢?先把原图等比缩放`12/minsize`,再按缩放因子`factor`(例如0.5)用上一次的缩放结果不断缩放,直至最短边接近12。根据上述算法,minsize越大,生成的“金字塔”层数越少,resize和pnet的计算量越小。 3、为什么缩放因子factor官方选择0.709? 图片金字塔缩放时,默认把宽,高都变为原来的1/2,缩放后面积变为原来的1/4;如果认为1/4的缩放幅度太大,你会怎么办?把面积缩放为原来的1/2。对的,这是很直观的想法,所以这里的缩放因子0.709 ≈ sqrt(2)/2,这样宽高变为原来的sqrt(2)/2,面积就变为原来的1/2。 4、 为什么把图片输入模型的时候要对每个像素做(x – 127.5)/128的操作? 归一化操作,加快收敛速度。由于图片每个像素点上是[0, 255]的数,都是非负数,对每个像素点做(x – 127.5)/128,可以把[0,255]映射为(-1, 1)。有正有负的输入,收敛速度更快。训练时候输入的图片需要先做这样的预处理,推断的时候也需要做这样的预处理才行。 5、有什么优化方案? 如图所示,三个模型中,P-NET所占用的时间最多,MTCNN的推断是CPU密集型运算,如果图片超过1080,生成图像金字塔的过程可能是流程中最耗时的过程。因为金字塔结构,第一阶段需要计算很多尺度的图片。但超过1080的图片中,你需要识别的最小人脸真的需要12*12吗?minsize是不是也变大了?另外,如果你事先已经知道图片中人脸的大小,是不是可以调整一下minsize?结合你的实践场景可以思考下。 以耗时最大的第一阶段为主要优化的关键点: ①、第一阶段受输入图片尺寸影响较大,可以让minsize随图片尺寸而改变,大图用大minsize ②、图片金字塔的生成过程,对上一次的resize结果进行resize而不是对全图resize ③、网络越复杂,GPU相比CPU的提升越明显。MTCNN的三阶段都是很弱的网络,GPU的提升不太大。另外,MTCNN的第一阶段,图像金字塔会反反复复地很多次调用一个很浅层的P-NET网络,导致数据会反反复复地从内存COPY到显存,又从显存COPY到内存,而这个复制操作消耗很大,甚至比计算本身更耗时,可以考虑将第一个阶段用其它算法实现。 六、结果展示 七、通过RKNN转换时注意点 1、 rknn.config(channel_mean_value=‘127.5 127.5 127.5 128’, reorder_channel=‘2 1 0’,quantized_dtype='dynamic_fixed_point-8') ①、127.5 127.5 127.5 128是将rgb的数据归一化为[-1,1],此时通过rknn进行归一化处理,所以数据进入inference前不需要手动做归一化处理,dataset.txt里的图像也不需要归一化处理 ②、如果rknn.config未做归一化处理,需要在inference前将数据进行归一化处理( im_data =(img-127.5)*0.0078125 ),且需要将模型中需要的不同尺寸的图片进行归一化处理,并保存。 2、 rknn.build(do_quantization=True, dataset='./dataset.txt') ①、do_quantization=True,是rknn进行量化处理,实际运行时速度更快;如果设置为false,几乎不损失精度,但是速度偏慢。 ②、进行量化处理的时候, dataset.txt最好多点相片,覆盖模型可能的输入类型。 3、 rknn.inference(inputs=[im_data], data_type=‘uint8’,data_format='nchw') ①、data_type默认为uint8,如果此时传入的数据是float类型,结果反而更差 ②、 caffe 数据类型是data_format=‘nchw’,channel first,其他的模型一般是’nhwc’,channel last 4、因为目前rknn不支持不固定维度的模型(不能在运行过程中改变模型维度),而pnet网络需要构建图片金字塔,所以根据图片大小,构建了9个P-Net的rknn;R-Net和O-Net中需要将前一个模型的数据导入,rknn 目前不支持多图片输入,所以目前是循环输入,将结果拼接到数组中。 八、项目展望 1、深入研究RKNN函数对模型精度的影响 2、优化代码:通过rga进行图像转换;并行运算,加快运行速度。(新的github代码已经使用多线程处理,解决卡顿的问题) 3、优化模型:减少图片进出npu次数(新的github代码已经使用图像拼接,加快速度)。 4、根据对比前后图片相似度,解决框图会闪的问题(详见github代码)。 5、提高nms门限,删除rnet(详见github代码)。
|