提交 33f8eebe 编写于 作者: M MaoXianxin

删除OpenCV栏目

上级 8bede382
单目标跟踪是计算机视觉中的一个基本问题。在如今深度学习时代也涌现出多种基于深度学习的单目标跟踪算法,其中基于SiamFC(双流网络)的单目标跟踪算法占据了半壁江山。DaSiamRPN就是典型的基于SiamFC的方法。
GSoC 2020为OpenCV增加了Python版本的DaSiamRPN,后来发现大家对C++版本的需求挺强烈的,于是我就为OpenCV贡献了一个C++版本的DaSiamRPN。
对于DaSiamRPN,在inference阶段,模型的主要结构如下图所示:
![](./imgs/24.jpg)
其中**黄色的CNN网络****橙色的Conv网络**是模型文件,DaSiamRPN最后的输出有两个分别为分类分支:17 x 17 x 2k 和 回归分支:17 x 17 x 4k。
在实际代码中我们将上图中的模型整合成三个模型:siamRPN,siamKernelCL1,siamKernelR1。其中siamRPN是主要模型,会在每一帧的跟踪中用到,而siamKernelCL1和siamKernelR1仅会在设定模板参数时用到。注意:图中的两个黄色的CNN是公用参数的,详细介绍请看原文[1]。
C++版本使用了与Python版本同样的逻辑和模型文件,下面简单介绍一下实现的主要逻辑。
在单目标跟踪中,首先需要设定模板参数,如以下代码所示:
```
siamRPN.setInput(blob); // blob 为输入的template
Mat out1;
siamRPN.forward(out1, "63"); // 63层的输出为文中描述的黄色CNN的输出
siamKernelCL1.setInput(out1); // 分别作为回归和分类分支的输入
siamKernelR1.setInput(out1);
Mat cls1 = siamKernelCL1.forward(); // 获取模板分类分支的特征
Mat r1 = siamKernelR1.forward(); // 获取模板回归分支的特征
std::vector<int> r1_shape = { 20, 256, 4, 4 }, cls1_shape = { 10, 256, 4, 4 };
siamRPN.setParam(siamRPN.getLayerId("65"), 0, r1.reshape(0, r1_shape)); // 将获取到的参数写入主模型
siamRPN.setParam(siamRPN.getLayerId("68"), 0, cls1.reshape(0, cls1_shape));
```
设定模板参数之后进入跟踪主循环:
```
// 主循环
for (int count = 0; ; ++count)
{
cap >> image;
// ...
float score = trackerEval(image, trackState, siamRPN); // 每一帧的跟踪计算
// 绘制图像
Rect rect = {
int(trackState.targetBox.x - int(trackState.targetBox.width / 2)),
int(trackState.targetBox.y - int(trackState.targetBox.height / 2)),
int(trackState.targetBox.width),
int(trackState.targetBox.height)
};
Mat render_image = image.clone();
rectangle(render_image, rect, Scalar(0, 255, 0), 2);
// ...
imshow(winName, render_image);
int c = waitKey(1);
if (c == 27 /*ESC*/)
break;
}
```
其中上述的`trackerEval`函数即为跟踪目标的计算,主体如下所示:
```
float trackerEval(Mat img, trackerConfig& trackState, Net& siamRPN)
{
// 第一步:确认搜索区域。跟踪算法根据前一帧中目标位置确定搜索区域
float searchSize = float((trackState.instanceSize - trackState.exemplarSize) / 2);
float pad = searchSize / scaleZ;
float sx = sz + 2 * pad;
Mat xCrop = getSubwindow(img, targetBox, (float)cvRound(sx), trackState.avgChans);
static Mat blob;
std::vector<Mat> outs;
std::vector<String> outNames;
Mat delta, score;
Mat sc, rc, penalty, pscore;
// 第二步:用siamRPN网络推理
blobFromImage(xCrop, blob, 1.0, Size(trackState.instanceSize, trackState.instanceSize), Scalar(), trackState.swapRB, false, CV_32F);
siamRPN.setInput(blob);
outNames = siamRPN.getUnconnectedOutLayersNames();
siamRPN.forward(outs, outNames);
delta = outs[0];
score = outs[1];
// score 和 delta即为文章开头结构图中的两个输出矩阵
score = score.reshape(0, { 2, trackState.anchorNum, trackState.scoreSize, trackState.scoreSize });
delta = delta.reshape(0, { 4, trackState.anchorNum, trackState.scoreSize, trackState.scoreSize });
// 第三步:后处理
// ...太长,这里省略
return score.at<float>(bestID); // 返回最好的跟踪框
}
```
运行效果:
![](./imgs/25.png)
算法存在的问题:模型训练集中包含的小物体较少,该算法在目标为小物体情况下的性能较弱,只能重新训练解决这个问题。
全部代码请参考:
[https://github.com/opencv/opencv/blob/master/samples/dnn/dasiamrpn_tracker.cpp](https://github.com/opencv/opencv/blob/master/samples/dnn/dasiamrpn_tracker.cpp)
参考资料:
1. 原始论文:[https://arxiv.org/abs/1808.06048](https://arxiv.org/abs/1808.06048)
2. 原始PyTorch实现:[https://github.com/foolwood/DaSiamRPN](https://github.com/foolwood/DaSiamRPN)
3. OpenCV中Python实现:[https://github.com/opencv/opencv/blob/master/samples/dnn/dasiamrpn_tracker.py](https://github.com/opencv/opencv/blob/master/samples/dnn/dasiamrpn_tracker.py)
\ No newline at end of file
2011年12月微信3.5版本正式上线“扫一扫”二维码,历经9年蜕变,“扫一扫”从二维码名片到扫码支付、从小程序码到健康码,二维码已经成为一种生活方式,连接着数字与现实。
现在,在腾讯WeChatCV团队的贡献下,微信扫码引擎正式加入OpenCV开源了!只需3行代码,你便轻松拥有微信的扫码能力:
```
import cv2
detector = cv2.wechat_qrcode_WeChatQRCode("detect.prototxt", "detect.caffemodel", "sr.prototxt", "sr.caffemodel")
img = cv2.imread("img.jpg")
res, points = detector.detectAndDecode(img)
print(res, points)
```
微信扫码引擎收录于:
opencv_contrib/modules/wechat_qrcode
开发文档:
[https://docs.opencv.org/master/d5/d04/classcv_1_1wechat__qrcode_1_1WeChatQRCode.html](https://docs.opencv.org/master/d5/d04/classcv_1_1wechat__qrcode_1_1WeChatQRCode.html)
微信扫码引擎是一款基于开源引擎ZXing,并高度优化和深度改造的高性能轻量二维码识别器。
# 基于CNN的二维码检测
**“一图多码**”是扫码支付经常遇到的线下场景。早在2016年,微信扫码引擎在业内率先支持远距离二维码检测、自动调焦定位、多码检测识别。然而,传统方法需要牺牲40%以上的性能来支持多码的检测与识别。伴随着深度学习技术的成熟和移动端计算能力的提升,微信扫码引擎引入基于CNN的二维码检测器解决上述问题。
![](./imgs/26.jpg)
图1 二维码检测器
我们以SSD框架为基础,构造了短小精干的二维码检测器(图1),采用残差连接(Residual Concat)、深度卷积(Depthwise Convolution)、空洞卷积(Dilated Convolution)、卷积投影(Convolution Projection)等技术进行了针对性的优化。**整个模型大小仅943KB**,iPhone7(A10)单CPU的推理时间仅需20ms,很好地满足“低延时、小体积、高召回”的业务需求。
检测代码:
[https://github.com/opencv/opencv_contrib/blob/master/modules/wechat_qrcode/src/wechat_qrcode.cpp#L156](https://github.com/opencv/opencv_contrib/blob/master/modules/wechat_qrcode/src/wechat_qrcode.cpp#L156)
模型文件:
[https://github.com/WeChatCV/opencv_3rdparty/blob/wechat_qrcode/detect.prototxt](https://github.com/WeChatCV/opencv_3rdparty/blob/wechat_qrcode/detect.prototxt)
参数文件:
[https://github.com/WeChatCV/opencv_3rdparty/blob/wechat_qrcode/detect.caffemodel](https://github.com/WeChatCV/opencv_3rdparty/blob/wechat_qrcode/detect.caffemodel)
# 基于CNN的二维码增强
**大图小码**”是远距扫码和长按识码经常面临的难点,二维码增强技术可以让小码更加清晰。2014年,微信率先在对话中上线“识别图中二维码”能力,离不开增强技术的加持。在长按识别的场景中,二维码图像经过用户的裁剪、压缩、转发,图像质量严重受损,分辨率急剧下降,边缘变得模糊不清,这给扫码引擎的识别带来了极大的挑战。
传统图像增强算法很难完美地解决以上问题,因此微信扫码引擎率先在识别流程中引入了基于深度学习的超分辨率技术(图2)。在网络结构上,密集连接(Dense Concat)、深度卷积(Depthwise Convolution)、反向卷积(Deconvolution)、残差学习(Residual Learning)等技术改善模型的性能;在目标函数上,针对二维码强边缘和二值化的特点,结合L2/L1损失、边界加权、二值约束设计了针对二维码的目标函数。经过精心的调优,**超分辨率模型大小仅23KB**,在iPhone7(A10)单CPU的推理时间仅需6ms(100x100超分200x200),完全满足移动端的应用需求。
![](./imgs/27.jpg)
图2 二维码超分辨率
超分代码:[https://github.com/opencv/opencv_contrib/blob/master/modules/wechat_qrcode/src/wechat_qrcode.cpp#L68](https://github.com/opencv/opencv_contrib/blob/master/modules/wechat_qrcode/src/wechat_qrcode.cpp#L68)
模型文件:
[https://github.com/WeChatCV/opencv_3rdparty/blob/wechat_qrcode/sr.prototxt](https://github.com/WeChatCV/opencv_3rdparty/blob/wechat_qrcode/sr.prototxt)
参数文件:
[https://github.com/WeChatCV/opencv_3rdparty/blob/wechat_qrcode/sr.caffemodel](https://github.com/WeChatCV/opencv_3rdparty/blob/wechat_qrcode/sr.caffemodel)
# 更鲁棒的定位点检测
二维码在识别的时候,通常需要根据扫描像素行/列匹配对应比例来寻找定位点(图3)。
![](./imgs/28.jpg)
图3 二维码定位点
在定位点检测上,我们提出面积法的定位点检测方法,相比于传统扫描线方法更为鲁棒和高效,有效地避免了误召回和漏召回;在定位点匹配上,特征聚类方法使得扫码引擎可以高效和准确地匹配多个定位点;在图像二值化上,引入多种更为鲁棒的二值化方法,有效地提高解码的成功率。
核心代码:
[https://github.com/opencv/opencv_contrib/blob/master/modules/wechat_qrcode/src/wechat_qrcode.cpp#L117](https://github.com/opencv/opencv_contrib/blob/master/modules/wechat_qrcode/src/wechat_qrcode.cpp#L117)
结合传统计算机视觉和深度学习技术,微信扫码引擎解决了一图多码、大图小码、鲁棒解码等业务痛点和技术难点。如今,该引擎在OpenCV上正式开源,期待广大开发者与我们一同打造业内领先的扫码引擎!
\ No newline at end of file
作者:梁峻豪,王天麒,孙中夏 (南方科技大学计算机科学与工程系)
最近,我们为OpenCV贡献了一维条形码识别模块,代码收录在:[https://github.com/opencv/opencv_contrib/tree/master/modules/barcode](https://github.com/opencv/opencv_contrib/tree/master/modules/barcode)
我们收集的数据集(数据集地址:[https://github.com/SUSTech-OpenCV/BarcodeTestDataset](https://github.com/SUSTech-OpenCV/BarcodeTestDataset),共250张条码图片)上进行了测试,我们的识别算法正确率达到了**96%**,速度为**20ms**每张图像。作为对比,我们也测试了ZXing在该数据集上的表现,其正确率为**64.4%**,速度为**90ms**每张图像。
注:测试速度不包含初始化以及读图时间。同时,我们的算法是C++实现,ZXing是Java实现。另外,对于用图片数据集进行的测试,ZXing99%的时间是在做彩色图到灰度图的转换。
本文将对此模块的原理和使用方式进行介绍。
# 条形码介绍
条形码是将宽度不等的多个黑条和空白,按照一定的编码规则排列,用以表达一组信息的图形标识符,如下图所示:
![](./imgs/1.png)
条码区域与其他图像相比有如下两个重要特点:第一,条码区域内的条空是平行排列的,方向趋于一致;第二,为了条码的可识读性,条码在制作时条和空之间有着较大的反射率差,从而条码区域内的灰度对比度较大,而且边缘信息丰富。
# 基于方向一致性的条码定位算法
根据条形码方向趋于一致的特点,我们可以将图像分块,通过计算每个块内**梯度方向的一致性**,来滤除那些**低一致性**的块。下图是筛选过后剩下的块:
![](./imgs/2.png)
由于包含条码区域的块**一定连续存在**的特性,我们可以通过对这些图像块再进行一个改进的**腐蚀**操作过滤掉部分背景图像块。下图是滤除部分背景图像块后剩余的块:
![](./imgs/3.png)
得到这些块之后,我们再根据每个图像块内的**平均梯度方向进行连通**。因为如果是相邻的图像块都属于同一个条码的话,那么他们的平均梯度方向也一定相同。
得到连通区域之后我们再根据条码图像的特性进行筛选,比如连通区域内的梯度大于阈值的点的比例,组成连通区域的图像块数量等。
最后,用**最小外接矩形**去拟合每个连通区域,并计算外界矩形的方向是否和连通区域内的平均梯度方向一致,过滤掉差距较大的连通区域。将平均梯度方向作为矩形的方向,并将矩形作为最终的定位框。
![](./imgs/4.png)
# 条形码解码
目前我们支持了三种类型的条码解码,它们分别是EAN13、 EAN8 和UPC-A。(下图为EAN13 条码示例)
![](./imgs/5.png)
条码的识别主要流程如下图:
![](./imgs/6.png)
其中:
1. 优化的超分辨率策略指的是对较小的条码进行**超分辨率放大**,不同大小条码做不同处理。
2. 解码算法的核心是基于条码编码方式的**向量距离计算**。因为条码的编码格式为固定的数个"条空",所以可以在约定好"条空"间隔之后。将固定的条空读取为一个向量,接下来与约定好的编码格式相匹配,取匹配程度最高的编码为结果。
3. 在解码步骤中,解码的单位为一条线,由于噪点,条空的粘连等原因,单独条码的解码结果存在较大的不确定性,因此我们加入了对**多条线的扫码**,通过对均匀分布的扫描与解码,能够将二值化过程中的一些不完美之处加以抹除。
**具体实现为**:首先在检测线上寻找起始符,寻找到起始符之后,对前半部分进行读取与解码,接着寻找中间分割符,接着对后半部分进行读取与解码,最后寻找终结符,并对整个条码进行首位生成与校验(此处以EAN13格式举例,不同格式不尽相同)。最后,每条线都会存在一个解码结果,所以对其进行投票,只将最高且总比例在有效结果50%以上的结果返回。这一部分我们基于ZXing的算法实现做了一些改进(投票等)。
4. **更换二值化和解码器**指的是在为解码成功遍历使用每种解码器和二值化尝试解码。
# 使用方式
C++
```
#include "opencv2/barcode.hpp"
#include "opencv2/imgproc.hpp"
using namespace cv;
Ptr<barcode::BarcodeDetector> bardet = makePtr<barcode::BarcodeDetector>("sr.prototxt", "sr.caffemodel"); //如果不使用超分辨率则可以不指定模型路径
Mat input = imread("your file path");
Mat corners; //返回的检测框的四个角点坐标,如果检测到N个条码,那么维度应该是[N][4][2]
std::vector<std::string> decoded_info; //返回的解码结果,如果解码失败,则为空string
std::vector<barcode::BarcodeType> decoded_format; //返回的条码类型,如果解码失败,则为BarcodeType::NONE
bool ok = bardet->detectAndDecode(input, decoded_info, decoded_format, corners);
```
Python
```
import cv2
bardet = cv2.barcode_BarcodeDetector()
img = cv2.imread("your file path")
ok, decoded_info, decoded_type, corners = bardet.detectAndDecode(img)
```
更多使用方式请参考文档:
[https://docs.opencv.org/master/dc/df7/classcv_1_1barcode_1_1BarcodeDetector.html](https://docs.opencv.org/master/dc/df7/classcv_1_1barcode_1_1BarcodeDetector.html)
# 参考文献
王祥敏,汪国有. 一种基于方向一致性的条码定位算法[EB/OL]. 北京:中国科技论文在线 [2015-04-22]. [http://www.paper.edu.cn/releasepaper/content/201504-338](http://www.paper.edu.cn/releasepaper/content/201504-338).
\ No newline at end of file
本文用OpenCV部署了超轻量目标检测模型NanoDet,实现了C++和Python两个版本,并对此进行了解析。
2020年,在深度学习目标检测领域诞生了yolov4,yolov5和nanodet这些优秀的检测模型,有许多的微信公众号报道这些算法模型。深度学习目标检测方法可划分为 Anchor-base 和 Anchor-free 两大类,nanodet是一个速度超快和轻量级的移动端 Anchor-free 目标检测模型,并且它的精度不亚于yolo系列的。
nanodet通过一些论文里的trick组合起来得到了一个兼顾精度、速度和体积的检测模型。作者用到的一些trick,主要参考自:(1)参考FCOS 式的单阶段 anchor-free 目标检测模型,FCOS特点是让模型学习feature map中每个位置到检测框的四条边的距离,如下图所示。
![](./imgs/8.jpg)
(2)使用 ATSS 进行目标采样,该方法提出了自适应训练样本选择方法,该方法根据目标的统计特征(方差和均值)自动划分正训练样本和负训练样本,弥合了基于锚的探测器与无锚探测器之间的差距。(3)使用 Generalized Focal Loss 损失函数执行分类和边框回归(box regression),该函数能够去掉 FCOS 的 Centerness 分支,省去这一分支上的大量卷积,从而减少检测头的计算开销。
为了达到轻量化的目的,作者在设计网络结构时,使用 ShuffleNetV2 1.0x 作为骨干网络,他去掉了该网络的最后一层卷积,并且抽取 8、16、32 倍下采样的特征输入到 PAN 中做多尺度的特征融合。
在FPN模块里,去掉所有卷积,只保留从骨干网络特征提取后的 1x1 卷积来进行特征通道维度的对齐,上采样和下采样均使用插值来完成。与 YOLO 使用的 concat操作不同,项目作者选择将多尺度的 Feature Map 直接相加,使整个特征融合模块的计算量变得非常小。
在检测头模块里,使用了共享权重的检测头,即对 FPN 出来的多尺度 Feature Map 使用同一组卷积预测检测框,然后每一层使用一个可学习的 Scale 值作为系数,对预测出来的框进行缩放。与此同时,使用了 Group Normalization(GN)作为归一化方式.FCOS 的检测头使用了 4 个 256 通道的卷积作为一个分支,也就是说在边框回归和分类两个分支上一共有 8 个 c=256 的卷积,计算量非常大。为了将其轻量化,项目作者首先选择用深度可分离卷积替换普通卷积,并且将卷积堆叠的数量从 4 个减少为 2 组。在通道数上,将 256 维压缩至 96 维,之所以选择 96,是因为需要将通道数保持为 8 或 16 的倍数,能够享受到大部分推理框架的并行加速。
最后,项目作者借鉴了 YOLO 系列的做法,将边框回归和分类使用同一组卷积进行计算,然后 split 成两份。最后,项目作者借鉴了 YOLO 系列的做法,将边框回归和分类使用同一组卷积进行计算,然后 split 成两份,这样就组成了nanodet网络。
作者把nanodet发布在github上,项目地址: [https://github.com/RangiLyu/nanodet](https://github.com/RangiLyu/nanodet),下载代码和模型文件到本地,按照README文档运行一个前向推理程序。接下来,我阅读前向推理主程序demo.py文件,尝试理解在运行这个主程序时需要调用哪些函数和.py文件。在前向推理主程序demo.py文件,对一幅图片做目标检测是在Predictor类的成员函数inference里实现的,它里面包含了对输入图片做预处理preprocess,前向计算forward和后处理postprocess这三个步骤。Predictor类的定义如下图所示
![](./imgs/9.jpg)
对输入原图做预处理,预处理模块是使用Pipeline类实现,对应的代码是
![](./imgs/10.jpg)
看到这段代码时,我有些懵逼了。第一次见到functools.partial这个模块,我百度查了一下它的作用是包装函数,接着看warp_resize函数,这个函数对应的代码很复杂,里面有多个if条件判断,调用了多个自定义函数。限于篇幅,在这里展示部分截图代码,如下图所示
![](./imgs/11.jpg)
从代码不难猜测出warp_resize函数的作用是对原图做resize,于是我把warp_resize函数返回的图像做可视化并打印出图像的尺寸是高宽:320x320,可视化结果如下图所示。
![](./imgs/12.jpg)
从图中可以看到,warp_resize函数是保持原图高宽比的resize,短边剩下的部分用黑色像素填充。这种功能在ultralytics的yolov3和yolov5代码仓库里有一个letterbox函数实现的,在letterbox函数使用opencv库里的resize和copyMakeBorder就可以实现保持高宽比的resize,这种方法简洁明了。接着我对warp_resize函数和letterbox函数对同一幅图片做保持原图高宽比的resize的结果比较。可视化结果如下,从视觉上看不出它们有何差异。把这两幅图的像素矩阵做减法比较,发现它们并不等于0,也是是说它们的像素值还是有差异的。
![](./imgs/13.jpg)
接着看预处理模块Pipeline类里的第二个函数color_aug_and_norm,代码截图如下。可以看出,这个函数的作用是对输入图片的RGB三通道分别做减均值除以标准差的操作,不过在最开始对img除以255,在最后对均值和标准差分别除以255,这三次除以255是完全没必要的,因为在最后一步 (img - mean) / std,分子分母可以约掉1/255,这和img,mean,std不做除以255这一步计算,直接(img - mean) / std是等价的。
![](./imgs/14.jpg)
综上所述,在预处理模块Pipeline类包含了很多冗余的计算,图像预处理本身是一个简单问题,但是在官方代码里却把简单问题搞复杂化了。
官方代码仓库([https://github.com/RangiLyu/nanodet](https://github.com/RangiLyu/nanodet))里提供了基于 ncnn 推理框架的实现,基于mnn,libtorch,openvino的实现,但是没有基于Opencv的dnn模块的实现。于是我就编写一套基于Opencv的dnn模块的实现,程序里包含Python和C++两个版本的代码。
**地址是**[ **https://github.com/hpc203/nanodet-opncv-dnn-cpp-python**](https://github.com/hpc203/nanodet-opncv-dnn-cpp-python)
在这套程序里,图像预处理模块沿用了ultralytics代码仓库里的letterbox函数使用opencv库里的resize和copyMakeBorder就可以实现保持高宽比的resize。此外,在网上有很多介绍nanodet网络结构的文章,但是在文章里没有对nanodet后处理模块做详细介绍的。因此,在编写这套程序时,我最关注的是nanodet的后处理模块,在nanodet网络输出之后,经过怎样的矩阵计算之后得到检测框的左上和右下两个顶点的坐标(x,y)的值的。接下来,我结合代码来理解后处理模块的运行原理。首先,原图经过预处理之后形成一个320x320的图片作为nanodet网络的输入,经过forward前向计算后会得到40x40,20x20,10x10这三种尺度的特征图(换言之就是原图缩小8倍,16倍,32倍),在程序代码里设断点调试,查看中间变量,截图如下:
![](./imgs/23.jpg)
![](./imgs/15.jpg)
从上图可以看到,经过forward前向计算后,有6个输出矩阵。第1个输出的维度是(1600,80),它对应的是40x40的特征图(拉平后是长度为1600的向量,也就是说一共有1600个像素点)里的每个像素点在coco数据集的80个类别里的每个类的置信度。第2个输出的维度是(1600,32),它对应的是40x40的特征图(拉平后是长度为1600的向量,也就是说一共有1600个像素点)里的每个像素点的检测框的预测偏移量,可以看到这个预测偏移量是一个长度为32的向量,它可以分成4份,每份向量的长度为8,接下来的第3,4,5,6个输出矩阵的意义以此类推。
前面讲到过nanodet的特点是让神经网络学习feature map中每个位置到检测框的四条边的距离,接下来我们继续在程序里设断点调试,来理解这4份长度为8的预测偏移量是如何经过一系列的矩阵计算后得到到检测框的四条边的距离。代码截图如下:
![](./imgs/16.jpg)
从上图可以看到,把形状为(1600,32)的矩阵reshape成(6400,8)的矩阵bbox_pred,其实就等同于把每一行切分成4份组成新的矩阵,然后做softmax变换,把数值归一化到0至1的区间内。继续调试接下来的一步,代码截图如下:
![](./imgs/17.jpg)
可以看到project是一个长度8的向量,元素值是从0到7。形状为(6400,8)的矩阵bbox_pred与向量project做乘法得到6400的列向量,然后reshape为(1600,4)的矩阵,接下来乘以缩放步长。这时候就得到的形状为(1600,4)的矩阵bbox_pred,它的几何意义就是40x40的特征图里的每个像素点到检测框的四条边的距离。有了这个值之后,接下来的计算就简单了,在此不做详细讲述,可以参阅我的代码。简单来说就是计算特征图的每个像素点在coco数据集里的80类里的最大score值作为类别置信度,然后把特征图的所有像素点的类别置信度从高到低排序,取前topk个像素点,然后根据上一步计算出的到检测框四条边的距离换算出检测框的左上和右下两个顶点的(x,y)值,最后做NMS去除重叠的检测框。为了更好的理解从nanodet输出特征图到最终计算出目标检测框的左上和右下顶点坐标(x,y)值的这个过程,我在草稿纸上画图演示,如下所示:
![](./imgs/18.jpg)
![](./imgs/19.jpg)
![](./imgs/20.jpg)
在编写完调用opencv的做nanodet目标检测的程序后,为了验证程序的有效性,从COCO数据集里选取几张图片测试并且与官方代码做比较,官方代码是用python编写的调用pytorch框架做目标检测的。结果如下,左边的图是官方代码的可视化结果,右边的图是opencv做nanodet目标检测的可视化结果。
![](./imgs/21.jpg)
把官方代码和我编写的代码做了一个性能比较的实验,实验环境是ubuntu系统,8G显存的gpu机器。在实验中读取一个视频文件,对视频里的每一帧做目标检测,分别运行官方的调用pytorch框架做目标检测的python代码和我编写的调用opencv做目标检测的python代码,在terminal终端输入top查看这两个程序运行时占用的内存,截图如下。第一行是opencv做nanodet目标检测程序运行时占用的内存,第二行是官方代码运行时占用的内存,可以看到使用opencv做nanodet目标检测对内存的消耗明显要低于官方代码的pytorch框架做nanodet目标检测的。
![](./imgs/22.jpg)
**Github传送门**[**https://github.com/hpc203/nanodet-opncv-dnn-cpp-python**](https://github.com/hpc203/nanodet-opncv-dnn-cpp-python)
\ No newline at end of file
......@@ -30,20 +30,4 @@
往期精彩链接: [点击这里](./PaddlePaddle)
## OpenCV
### 2021-05-26
[微信二维码引擎OpenCV开源!3行代码让你拥有微信扫码能力-2021-01-31](./OpenCV/微信二维码引擎OpenCV开源!3行代码让你拥有微信扫码能力-2021-01-31.md)
### 2021-05-25
[用OpenCV实现条形码识别-2021-04-22](./OpenCV/用OpenCV实现条形码识别-2021-04-22.md)
[用OpenCV实现超轻量的NanoDet目标检测模型-2021-03-17](./OpenCV/用OpenCV实现超轻量的NanoDet目标检测模型-2021-03-17.md)
[OpenCV中单目标跟踪算法DaSiamRPN的C++实现-2021-03-09](./OpenCV/OpenCV中单目标跟踪算法DaSiamRPN的C++实现-2021-03-09.md)
往期精彩链接: [点击这里](./OpenCV)
![](1.png)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册