超详细!使用OpenCV深度学习模块在图像分类下的应用实践

2020 年 7 月 31 日 极市平台

加入极市专业CV交流群,与 10000+来自港科大、北大、清华、中科院、CMU、腾讯、百度 等名校名企视觉开发者互动交流!

同时提供每月大咖直播分享、真实项目需求对接、干货资讯汇总,行业技术交流。关注 极市平台 公众号 ,回复 加群,立刻申请入群~

极市导读:本文来自6月份出版的新书OpenCV深度学习应用与性能优化实践,由Intel与阿里巴巴高级图形图像专家联合撰写,系统地介绍了OpenCV DNN 推理模块原理和实践。极市为大家争取到5本赠书福利,详见文末。

深度学习理论的广泛研究促进了其在不同场景的应用。在计算机视觉领域,图像分类、目标检测、语义分割和视觉风格变换等基础任务的性能也因为采用了深度学习的方法而有了飞跃性的提升。本章将为读者梳理深度学习方法在这些基本应用场景的应用情况,并结合OpenCV深度学习模块的示例程序,从源代码和实际运行两个层面进行讲解。 下文对书中图像分类部分内容进行摘录:
图像分类是计算机视觉领域的基础任务之一,在各种基于视觉的人工智能应用中,图像分类都扮演着重要的角色。例如,在智能机器人应用中,我们需要对所采集的视频中的每一帧进行主要物体的检测和分类,并以此作为进一步决策的基础。
近些年,图像分类与深度学习的飞速发展有着密不可分的关系。在2012年的ILSVRC (ImageNet Large Scale Visual Recognition Competition,ImageNet大规模视觉识别挑战赛)大赛上,AlexNet横空出世,以压倒性优势战胜了传统图像分类算法而夺得冠军,开启了计算机视觉领域的深度学习革命。2015年,ResNet首次在图像分类准确度上战胜人类。2017年,随着SENet的夺冠,最后一届ILSVRC大赛落下帷幕。下面为大家梳理一下历届ILSVRC大赛中出现的经典网络结构。
图像分类经典网络结构
自2012年ILSVRC大赛AlexNet夺冠以来,直至2017年最后一届SENet夺冠,所有冠军都被各种深度神经网络所摘得。历届ILSVRC大赛的经典网络结构及其特点如表9-1所示。
这些网络结构不仅可应用在图像分类中,而且可作为其他计算机视觉任务(如目标检测、语义分割和视觉风格变换)的骨干(backbone)网络,用来提取图像特征。因此,它们对整个计算机视觉技术的发展有着深远的影响。
下面我们摘录OpenCV官方Wiki上的DNN模块运行效率统计表,看一下AlexNet、GoogLeNet和ResNet-50在OpenCV DNN模块中的运行效率。
测试系统软硬件配置如下:
各软件组件的版本信息如表9-2所示。
CPU实现的运行时间如表9-3所示,该时间取的是50次运行的中位数时间,中位数时间可以排除多次推理运算中某些过于异常的值对平均值的干扰。另外,所有网络模型都采用32位浮点数据格式进行计算。在神经网络的推理计算中,可以采用量化方法把32位浮点精度的模型参数降低到16位浮点精度以节省数据读取带宽提高运算效率,但是并不是所有算法都支持针对16位浮点精度的实现,为了便于比较,测试都采用32位浮点精度。
GPU实现的运行时间如表9-4所示。
从上面的数据可以看到,DNN模块的OpenCL实现跟原生C++实现性能相近,而Intel-Caffe MKLDNN的加速性能最好,原因是多方面的。首先MKLDNN是针对Intel CPU进行高度优化的神经网络计算库,能够充分发挥Intel CPU的性能。其次,该测试使用的CPU硬件性能比较强劲(8核心,4.0GHz运行频率),而集成的GPU是中低配置。最后,测试的3种网络模型的运算量不算太大,未能充分发挥GPU的并发特性。
接下来,我们以GoogLeNet(Inception-v1)为例,详细讲解其网络结构和设计原理,然后结合OpenCV中的图像分类示例程序讲解GoogLeNet模型的实际使用。
GoogLeNet
GoogLeNet自2014年提出以来,总共演进了4个版本,由于第1版是后续几个版本的基础,本节主要介绍2014年的第1版,即GoogLeNet v1。
GoogLeNet v1是2014年ILSVRC大赛的冠军模型,它延续了自LeNet以来的典型卷积网络结构,即多个卷积层前后堆叠,然后通过全连接层输出最终的特征值。GoogLeNet的结构如图9-1所示。
下面对图9-1中各列进行解释。type列表示层或者模块的类型,其中inception代表一个Inception模块,GoogLeNet中总共堆叠了9个Inception模块,convolution表示卷积层,max pool表示最大池化层,avg pool表示平均池化层,dropout代表随机裁剪操作,linear是全连接层,softmax表示最后对输出特征值进行sotfmax操作。patch size列表示卷积核大小,stride表示卷积运算的步进值。output size列表示输出特征图的长、宽和通道数。Depth列表示该层或者模块重复连接的次数。#1×1,#3×3,#5×5列分别表示Inception模块中的1×1,3×3,5×5卷积核大小的卷积分支的输出通道数。pool proj列表示池化投影的输出通道数。#3×3 reduce和#5×5 reduce列表示Inception结构中3×3和5×5卷积核卷积之前的1×1卷积的输出通道数。params列表示参数数目。ops列表示运算量。Inception模块是GoogLeNet的最大创新点,它的初衷是增加卷积核尺寸种类的同时降低训练参数数量,下面对Inception v1模块进行讲解,它的结构如图9-1所示。
Inception模块使用1×1卷积对前层数据进行降维处理并分成多路,然后用3×3,5×5卷积对降维后的分支进行卷积运算,同时将各个卷积结果和3×3最大池化的结果按通道进行连接。这种创新的结构使得网络参数大大降低的同时保留了很好的特征表达能力,达到了深度和参数数量的双赢。
为什么使用多种尺寸的卷积核有助于提高特征表达能力呢?我们以图9-3为例,最左边的狗占据了图的大部分,中间的狗占了图的一部分,而最右边的狗占了图的很小一部分。采用多种尺寸的卷积核可以学习到不同尺度的特征,使网络具有更好的特征适应性。
接下来,我们结合DNN模块图像分类示例程序看一下图像分类应用的具体实现。
图像分类程序源码分析
我们借助OpenCV的示例程序来介绍图像分类应用的主要步骤。OpenCV DNN模块示例程序囊括了各种不同应用场景,它们有着相似的代码结构和流程,如图9-4所示。各种示例应用源代码的区别主要体现在最后一步:推理结果的解析和可视化。本节将详细讲解代码的每个步骤,之后各节的源码分析将重点聚焦于应用特定的参数及推理结果的解析和可视化。
下面分析图像分类示例程序源码。
首先引入必要的头文件,参见代码清单9-1。其中,fstream和sstream是C++标准库头文件,用于文件读取和文本处理。dnn.hpp、imgproc.hpp、highgui.hpp提供OpenCV API声明,common.hpp提供了一些DNN示例程序通用的函数,例如,查找输入文件位置,从模型配置文件中读取默认的运行时参数等。
9-1 引入必要的头文件
#include <fstream>#include <sstream>#include <opencv2/dnn.hpp>#include <opencv2/imgproc.hpp>#include <opencv2/highgui.hpp>#include "common.hpp"
代码清单9-2定义了命令行参数,下面逐一讲解。

9-2 命令行参数定义

std::string keys=    "{ help  h | | Print help message. }"    "{ @alias  | | An alias name of model to extract preprocessing parameters from models.yml file. }"    "{ zoo  | models.yml | An optional path to file with preprocessing parameters }"    "{ input i  | | Path to input image or video file. Skip this argument to capture frames from a camera.}"    "{ framework f | | Optional name of an origin framework of the model. Detect it automatically if it does not set. }"    "{ classes  | Optional path to a text file with names of classes. }"    "{ backend  | 0 | Choose one of computation backends: "                     "0: automatically (by default), "                     "1: Halide language (http://halide-lang.org/), "                     "2: Intel's Deep Learning Inference Engine (https://software.intel.com/openvino-toolkit), "                    "3: OpenCV implementation }"    "{ target  | 0 | Choose one of target computation devices: "                     "0: CPU target (by default), "                     "1: OpenCL, "                     "2: OpenCL fp16 (half-float precision), "                     "3: VPU }";

接下来引用命名空间,参见代码清单9-3。我们的代码用到了cv和dnn命名空间 中的API,通过显式声明命名空间,方便后续的API调用。

9-3 声明命名空间及定义全局变量

using namespace cv;using namespace dnn;
接下来定义用于存放类别名称的变量classes:
std::vector<std::string> classes;
下面进入主函数。
首先,解析命令行参数,参见代码清单9-4。

9-4 主函数(解析命令行参数)

int main(int argc, char** argv){    CommandLineParser parser(argc, argv, keys);    const std::string modelName=parser.get<String>("@alias");    const std::string zooFile=parser.get<String>("zoo");    keys +=genPreprocArguments(modelName, zooFile);    parser=CommandLineParser(argc, argv, keys);    parser.about("Use this script to run classification deep learning networks using OpenCV.");    if (argc==1 || parser.has("help"))    {        parser.printMessage();        return 0;    }    float scale=parser.get<float>("scale");    Scalar mean=parser.get<Scalar>("mean");    bool swapRB=parser.get<bool>("rgb");    int inpWidth=parser.get<int>("width");    int inpHeight=parser.get<int>("height");    String model=findFile(parser.get<String>("model"));    String config=findFile(parser.get<String>("config"));    String framework=parser.get<String>("framework");    int backendId=parser.get<int>("backend");    int targetId=parser.get<int>("target");
如果命令行参数提供了类别文件路径,则解析类别文件并将类别名称存储到全局变量classes,参见代码清单9-5。

9-5 主函数(类别文件解析)

if (parser.has("classes")){    std::string file=parser.get<String>("classes");    std::ifstream ifs(file.c_str());    if (!ifs.is_open())        CV_Error(Error::StsError, "File " + file + " not found");    std::string line;    while (std::getline(ifs, line))    {        classes.push_back(line);    }}
接下来进行异常情况检查,包括命令行参数异常,以及缺失模型文件异常,参加代码清单9-6。

9-6 主函数(异常情况检查)

if (!parser.check())    {        parser.printErrors();        return 1;    }    CV_Assert(!model.empty());
加载网络模型,创建DNN模块网络对象,并设置加速后端和目标运算设备,参加代码清单9-7。

9-7 主函数(初始化网络并创建显示窗口)

Net net=readNet(model, config, framework);net.setPreferableBackend(backendId);net.setPreferableTarget(targetId);
接下来,创建用于显示结果的窗口对象。代码如下:
static const std::string kWinName="Deep learning image classification in OpenCV";namedWindow(kWinName, WINDOW_NORMAL);
然后,创建图像输入对象cap,用于读取指定的图片、视频文件,参见代码清单9-8。如果没有指定图片或视频文件,则从摄像头读取视频帧。

9-8 主函数(创建图像输入对象)

VideoCapture cap;if (parser.has("input"))cap.open(parser.get<String>("input"));else    cap.open(0);
接下来进入图像处理循环,循环起始部分通过cap对象读取一帧图像,参见代码清单9-9。

9-9 图像处理循环(读取一帧图像)

Mat frame, blob;while (waitKey(1) < 0){    cap >> frame;    if (frame.empty())    {        waitKey();        break;    }
然后调用blobFromImage()函数将读入的图像转换成网络模型的输入(blob),并设置网络对象,参见代码清单9-10。blobFromImage()函数会对图像进行一系列的预处理,包括调整大小、减均值、交换红蓝颜色通道等,最终返回一个一维数组(N、C、H、W)。其中,N代表批大小,实时应用中通常为1,即一次处理一帧图像数据;C代表图像通道数,一般为3,即R、G、B三种颜色;H、W分别代表图像的高度和宽度。

9-10 图像处理循环(设置网络输入)

blobFromImage(frame, blob, scale, Size(inpWidth, inpHeight),mean, swapRB, false);net.setInput(blob);
接下来运行网络模型推理,代码如下:
Mat prob=net.forward();
网络推理的输出数据对象prob包含1000个概率值,分别对应1000个图像类别。
至此,网络推理运算部分结束,接下来进行推理结果的解析和可视化,参见代码清单9-11和代码清单9-12。

9-11 图像处理循环(解析网络推理输出)

Point classIdPoint;double confidence;// 找到概率值最大的类别id,该类别为图像所属分类minMaxLoc(prob.reshape(1, 1), 0, &confidence, 0, &classIdPoint);int classId=classIdPoint.x;

9-12 图像处理循环(可视化推理结果)

// 获取网络推理运算耗时,并叠加到原始图像上std::vector<double> layersTimes;double freq=getTickFrequency() / 1000;double t=net.getPerfProfile(layersTimes) / freq;std::string label=format("Inference time: %.2f ms", t);putText(frame, label, Point(0, 15), FONT_HERSHEY_SIMPLEX,          0.5, Scalar(0, 255, 0));// 将图像类别标签和概率值叠加到原始图像上label=format("%s: %.4f", (classes.empty() ?                                 format("Class #%d", classId).c_str() :                                 classes[classId].c_str()),confidence);putText(frame, label, Point(0, 40), FONT_HERSHEY_SIMPLEX,           0.5, Scalar(0, 255, 0));// 显示图像imshow(kWinName, frame);    }    // 循环结束,退出主函数    return 0;}
以上内容摘自 《OpenCV深度学习应用与性能优化实践》一书,经出版方授权发布。

福利时间

本次联合机械工业出版社华章公司为大家带来 5本 正版新书。点击 在看并留言,写下自己深度学习的心得感悟 ,极小东将精选 符合条件的留言 进行展示,并向点赞最高的 前5名 读者赠送此书(截至 8月3日20点 )。没有被抽到的开发者可以通过 扫描下方二维码或点击阅读原文 进行购买。
今天京东购书 5折优惠(8月1日0点结束 ,这本书原价89元,今天扫描下方海报的二维码购买,只要 44.05元,非常划算。无论你是想要入门OpenCV的学生或初学者,还是想要进阶提升技术水平的算法工程师或图像视频开发人员,都推荐你购买阅读!
《OpenCV深度学习应用与性能优化实践》
Intel与阿里巴巴高级图形图像专家联合撰写!深入解析OpenCV DNN 模块、基于GPU/CPU的加速实现、性能优化技巧与可视化工具,以及人脸活体检测等应用,涵盖Intel推理引擎加速等鲜见一手深度信息。知名专家傅文庆、邹复好、Vadim Pisarevsky、周强联袂推荐!
点击“ 阅读原文”,查看更多五折AI好书!


推荐阅读

添加极市小助手微信 (ID : cv-mart) ,备注: 研究方向-姓名-学校/公司-城市 (如:目标检测-小极-北大-深圳),即可申请加入 极市技术交流群 ,更有 每月大咖直播分享、真实项目需求对接、求职内推、算法竞赛、 干货资讯汇总、行业技术交流 一起来让思想之光照的更远吧~

△长按添加极市小助手

△长按关注极市平台,获取 最新CV干货

觉得有用麻烦给个在看啦~   
登录查看更多
0

相关内容

图像分类是指给定一组各自被标记为单一类别的图像,然后对一组新的测试图像的类别进行预测,并测量预测的准确性结果。
最新《深度学习人脸识别》综述论文,
专知会员服务
68+阅读 · 2020年8月10日
深度学习目标检测方法综述
专知会员服务
279+阅读 · 2020年8月1日
【干货书】高级应用深度学习,294页pdf
专知会员服务
154+阅读 · 2020年6月20日
专知会员服务
201+阅读 · 2020年3月6日
计算机视觉最佳实践、代码示例和相关文档
专知会员服务
19+阅读 · 2019年10月9日
【机器视觉】图像分类问题详细介绍
产业智能官
10+阅读 · 2018年12月26日
【干货】李沐等人:CNN图像分类Trick合集(附详细代码)
GAN生成式对抗网络
58+阅读 · 2018年12月11日
计算机视觉方向简介 | 目标检测最新进展总结与展望
计算机视觉life
9+阅读 · 2018年10月28日
TensorFlow图像分类教程
机器学习研究会
34+阅读 · 2017年12月29日
资源 |《解析卷积神经网络—深度学习实践手册》
黑龙江大学自然语言处理实验室
4+阅读 · 2017年12月21日
深度学习目标检测概览
AI研习社
46+阅读 · 2017年10月13日
Local Relation Networks for Image Recognition
Arxiv
4+阅读 · 2019年4月25日
Arxiv
4+阅读 · 2018年6月14日
Arxiv
8+阅读 · 2018年5月1日
Arxiv
6+阅读 · 2018年4月23日
Arxiv
11+阅读 · 2018年4月8日
Arxiv
3+阅读 · 2018年3月27日
Arxiv
5+阅读 · 2016年12月29日
VIP会员
相关VIP内容
最新《深度学习人脸识别》综述论文,
专知会员服务
68+阅读 · 2020年8月10日
深度学习目标检测方法综述
专知会员服务
279+阅读 · 2020年8月1日
【干货书】高级应用深度学习,294页pdf
专知会员服务
154+阅读 · 2020年6月20日
专知会员服务
201+阅读 · 2020年3月6日
计算机视觉最佳实践、代码示例和相关文档
专知会员服务
19+阅读 · 2019年10月9日
相关资讯
【机器视觉】图像分类问题详细介绍
产业智能官
10+阅读 · 2018年12月26日
【干货】李沐等人:CNN图像分类Trick合集(附详细代码)
GAN生成式对抗网络
58+阅读 · 2018年12月11日
计算机视觉方向简介 | 目标检测最新进展总结与展望
计算机视觉life
9+阅读 · 2018年10月28日
TensorFlow图像分类教程
机器学习研究会
34+阅读 · 2017年12月29日
资源 |《解析卷积神经网络—深度学习实践手册》
黑龙江大学自然语言处理实验室
4+阅读 · 2017年12月21日
深度学习目标检测概览
AI研习社
46+阅读 · 2017年10月13日
相关论文
Local Relation Networks for Image Recognition
Arxiv
4+阅读 · 2019年4月25日
Arxiv
4+阅读 · 2018年6月14日
Arxiv
8+阅读 · 2018年5月1日
Arxiv
6+阅读 · 2018年4月23日
Arxiv
11+阅读 · 2018年4月8日
Arxiv
3+阅读 · 2018年3月27日
Arxiv
5+阅读 · 2016年12月29日
Top
微信扫码咨询专知VIP会员