前言

  级联分类器的效果并不是很好,准确度相对深度学习较低,上一章节使用了dnn中的tensorflow,本章使用yolov3模型,识别出具体的分类。

 

Demo

  320x320,置信度0.6

  608x608,置信度0.6(.cfg里面是608)

 

yolov3模型下载

OpenCV深度识别基本流程

  opencv3.4.x支持了各种模型。

支持的模型

  opencv3.4.x支持一下深度学习的模型:
- caffe:.caffemodel
  官网:http://caffe.berkeleyvision.org
- tensorflow:.pb
  官网:https://www.tensorflow.org
- torch:.t7 | .net
  官网:http://torch.ch
- darknet:.weights
  官网:https://pjreddie.com/darknet
- DLDT:.bin
  官网:https://software.intel.com/openvino-toolkit

操作步骤:yolov3

  不同深度学习框架产生的模型,在操作上和数据输出上有一些区别。梳理下opencv使用tensorflow训练好的模型的使用步骤。

步骤一:读取分类文件

  模型文件对应了不同的分类文件,分类文件是以行为标识,所在的行数(0开始),就是最终识别出的分类号的第几个分类。

std::string classesFile = "E:/qtProject/openCVDemo/dnnData/" \
"yolov3/coco.names";
// 读入分类名称,存入缓存
std::ifstream ifs(classesFile);
std::vector<std::string> classes;
std::string classLine;
while(std::getline(ifs, classLine))
{
classes.push_back(classLine);
}

步骤二:加载模型和配置文件,建立神经网络。

  根据不同的模型,使用cv::dnn::readNetFromXXX系列函数进行读取,opencv3.4.x系列支持的dnn模型(支持模型往上看)。
  yolov3模型如下:

std::string modelWeights = "E:/qtProject/openCVDemo/dnnData/" \
"yolov3/yolov3.weights";
std::string modelCfg = "E:/qtProject/openCVDemo/dnnData/" \
"yolov3/yolov3.cfg";
// 加载yolov3模型
cv::dnn::Net net = cv::dnn::readNetFromDarknet(modelCfg, modelWeights);
if(net.empty())
{
qDebug() << __FILE__ << __LINE__ << "net is empty!!!";
return;
}

步骤三:将要预测的图片加入到神经网络中

  加入之后,需要识别图片,那么需要把图片输入到神经网络当中去,使用yolov3模型特别注意,要先进行归一化,然后变成指定大小的图片,如下:

// 读取图片识别
mat = cv::imread("E:/testFile/15.jpg");
if(!mat.data)
{
qDebug() << __FILE__ << __LINE__ << "Failed to read image!!!";
return;
}
// cv::dnn::blobFromImage(mat, blob);
// 必须要设置,否则会跑飞
cv::dnn::blobFromImage(mat,
blob,
1.0f/255,
cv::Size(320, 320),
cv::Scalar(0, 0, 0),
true,
false);
net.setInput(blob);

  宽度高度增加可以提升检测的准确度,最好是根据cfg文件进行修改,本Demo是320x320,实际.cfg文件中的是608x608,并且经过测试,这个是识别效果最好的像素,大于608则会跑飞。
  

步骤四:分类预测,获取识别的结果

  输入之后,就进行识别,识别是向前预测(分类预测),并且拿到结果,对于yolov3模型,规定了有3个输出层,所以需要先获取3个输出层,然后预测的时候就需要指定预测这3个输出层,否则会跑飞。

// 获取输出的层
std::vector<cv::String> outPutNames;
std::vector<int> outLayers = net.getUnconnectedOutLayers();
for(int index = 0; index < outLayers.size(); index++)
{
outPutNames.push_back(layerNames[outLayers[index] - 1]);
qDebug() << __FILE__ << __LINE__
<< QString(layerNames[outLayers[index] - 1].c_str());
}
// 推理预测:可以输入预测的图层名称
std::vector<cv::Mat> probs;
net.forward(probs, outPutNames);

  对于预测的结果,存于std::vectorcv::Mat类型的probs,每一个元素指定为cv::Mat类型的prob,每一行代表一个检测到的分类,具体列信息如下表:
  
  (注意:具体的使用,请参照“步骤五”)

步骤五:对达到置信度的可以通过输出的mat进行分类和框选

  关键的输出结果步骤,不同的识别有区别,yolov3如下图:

// 置信度预制,大于执行度的将其使用rect框出来
for(int index = 0; index < probs.size(); index++)
{
for (int row = 0; row < probs[index].rows; row++)
{
// 获取probs中一个元素里面匹配对的所有对象中得分最高的
cv::Mat scores = probs[index].row(row).colRange(5, probs[index].cols);
cv::Point classIdPoint;
double confidence;
// Get the value and location of the maximum score
cv::minMaxLoc(scores, 0, &confidence, 0, &classIdPoint);
if(confidence > 0.6)
{
qDebug() << __FILE__ << __LINE__ << confidence << classIdPoint.x;
int centerX = (int)(probs.at(index).at<float>(row, 0) * mat.cols);
int centerY = (int)(probs.at(index).at<float>(row, 1) * mat.rows);
int width = (int)(probs.at(index).at<float>(row, 2) * mat.cols);
int height = (int)(probs.at(index).at<float>(row, 3) * mat.rows);
int left = centerX - width / 2;
int top = centerY - height / 2;
cv::Rect objectRect(left, top, width, height);
cv::rectangle(mat, objectRect, cv::Scalar(255, 0, 0), 2);
cv::String label = cv::format("%s:%.4f",
classes[classIdPoint.x].data(),
confidence);
cv::putText(mat,
label,
cv::Point(left, top - 10),
cv::FONT_HERSHEY_SIMPLEX,
0.4,
cv::Scalar(0, 0, 255));
qDebug() << __FILE__ << __LINE__
<< centerX << centerY << width << height;
}
}
}
 

函数原型

读取yolov3模型与配置文件函数原型

Net readNetFromDarknet(const String &cfgFile,
const String &darknetModel = String());

  从文件中读取。

  • 参数一:带有网络体系结构文本描述的.cfg文件的路径;
  • 参数二:已学习网络的.weights文件的路径;

读取图片(需要识别的)函数原型

void blobFromImage(InputArray image,
OutputArray blob,
double scalefactor=1.0,
const Size& size = Size(),
const Scalar& mean = Scalar(),
bool swapRB=false,
bool crop=false,
int ddepth=CV_32F);.

  从图像创建区域。可选择从中心调整和裁剪图像。

  • 参数一:图像输入图像(1、3或4通道);
  • 参数二:输出的图像空间;
  • 参数三:图像值的缩放因子乘数;
  • 参数四:大小输出图像的空间大小;
  • 参数五:从通道中减去平均值的平均标量。价值是有意的,如果image有BGR顺序,swapRB为真,则按(mean-R,mean-G,mean-B)顺序排列;
  • 参数六:swapRB标志,指示交换第一个和最后一个通道,在三通道图像是必要的;
  • 参数七:裁剪标志,指示调整大小后是否裁剪图像;
  • 参数八:输出blob的深度,选择CV_32F或CV_8U;

设置神经网络输入函数原型

void cv::dnn::Net::setInput(InputArray blob,
const String& name = "",
double scalefactor = 1.0,
const Scalar& mean = Scalar());

  设置网络的新输入值。

  • 参数一:一个新的blob。应具有CV_32F或CV_8U深度。
  • 参数二:输入层的名称。
  • 参数三:可选的标准化刻度。
  • 参数四:可选的平均减去值。

返回所有层的名称(按照本身的索引循序排列)

std::vector<String> getLayerNames() const;

返回具有未连接输出的层的索引。

std::vector<int> getUnconnectedOutLayers() const;

深度检测识别(向前预测)函数原型

void cv::dnn::Net::Mat forward(const String& outputName = String());

  向前预测,返回指定层的第一个输出的blob,一般是返回最后一层,可使用cv::Net::getLayarNames()获取所有的层名称。

  • 参数一:outputName需要获取输出的层的名称
 

Demo

void OpenCVManager::testYoloV3()
{
std::string classesFile = "E:/qtProject/openCVDemo/dnnData/" \
"yolov3/coco.names";
std::string modelWeights = "E:/qtProject/openCVDemo/dnnData/" \
"yolov3/yolov3.weights";
std::string modelCfg = "E:/qtProject/openCVDemo/dnnData/" \
"yolov3/yolov3.cfg"; // 读入分类名称,存入缓存
std::ifstream ifs(classesFile);
std::vector<std::string> classes;
std::string classLine;
while(std::getline(ifs, classLine))
{
classes.push_back(classLine);
} // 加载yolov3模型
cv::dnn::Net net = cv::dnn::readNetFromDarknet(modelCfg, modelWeights);
if(net.empty())
{
qDebug() << __FILE__ << __LINE__ << "net is empty!!!";
return;
} cv::Mat mat;
cv::Mat blob; // 获得所有层的名称和索引
std::vector<cv::String> layerNames = net.getLayerNames();
int lastLayerId = net.getLayerId(layerNames[layerNames.size() - 1]);
cv::Ptr<cv::dnn::Layer> lastLayer = net.getLayer(cv::dnn::DictValue(lastLayerId));
qDebug() << __FILE__ << __LINE__
<< QString(lastLayer->type.c_str())
<< QString(lastLayer->getDefaultName().c_str())
<< QString(layerNames[layerNames.size()-1].c_str()); // 获取输出的层
std::vector<cv::String> outPutNames;
std::vector<int> outLayers = net.getUnconnectedOutLayers();
for(int index = 0; index < outLayers.size(); index++)
{
outPutNames.push_back(layerNames[outLayers[index] - 1]);
qDebug() << __FILE__ << __LINE__
<< QString(layerNames[outLayers[index] - 1].c_str());
} while(true)
{
// 读取图片识别
mat = cv::imread("E:/testFile/15.jpg");
if(!mat.data)
{
qDebug() << __FILE__ << __LINE__ << "Failed to read image!!!";
return;
} // cv::dnn::blobFromImage(mat, blob);
// 必须要设置,否则会跑飞
cv::dnn::blobFromImage(mat,
blob,
1.0f/255,
cv::Size(320, 320),
cv::Scalar(0, 0, 0),
true,
false);
net.setInput(blob);
// 推理预测:可以输入预测的图层名称
std::vector<cv::Mat> probs;
net.forward(probs, outPutNames); // 显示识别花费的时间
std::vector<double> layersTimes;
double freq = cv::getTickFrequency() / 1000;
double t = net.getPerfProfile(layersTimes) / freq;
std::string label = cv::format("Inference time: %.2f ms", t);
cv::putText(mat,
label,
cv::Point(0, 15),
cv::FONT_HERSHEY_SIMPLEX,
0.5,
cv::Scalar(255, 0, 0));
// 置信度预制,大于执行度的将其使用rect框出来
for(int index = 0; index < probs.size(); index++)
{
for (int row = 0; row < probs[index].rows; row++)
{
// 获取probs中一个元素里面匹配对的所有对象中得分最高的
cv::Mat scores = probs[index].row(row).colRange(5, probs[index].cols);
cv::Point classIdPoint;
double confidence;
// Get the value and location of the maximum score
cv::minMaxLoc(scores, 0, &confidence, 0, &classIdPoint);
if(confidence > 0.6)
{
qDebug() << __FILE__ << __LINE__ << confidence << classIdPoint.x;
int centerX = (int)(probs.at(index).at<float>(row, 0) * mat.cols);
int centerY = (int)(probs.at(index).at<float>(row, 1) * mat.rows);
int width = (int)(probs.at(index).at<float>(row, 2) * mat.cols);
int height = (int)(probs.at(index).at<float>(row, 3) * mat.rows);
int left = centerX - width / 2;
int top = centerY - height / 2;
cv::Rect objectRect(left, top, width, height);
cv::rectangle(mat, objectRect, cv::Scalar(255, 0, 0), 2);
cv::String label = cv::format("%s:%.4f",
classes[classIdPoint.x].data(),
confidence);
cv::putText(mat,
label,
cv::Point(left, top - 10),
cv::FONT_HERSHEY_SIMPLEX,
0.4,
cv::Scalar(0, 0, 255));
qDebug() << __FILE__ << __LINE__
<< centerX << centerY << width << height;
}
}
} cv::imshow(_windowTitle.toStdString(), mat);
cv::waitKey(0);
}
}
 

对应工程模板v1.65.0

  openCVDemo_v1.65.0_基础模板_yolov3分类检测.rar。

 

入坑

入坑一:加载模型时候错误

错误
  

原因
  模型文件加载错误。
解决
  检查文件是否存在,路径是否正确,模型文件是否能对应上。

入坑二:输入blob时错误

错误
  

原因
  预测的时候未输入参数,需要输入参数(注意:tensorflow未输入没有问题)。
解决
  

 

OpenCV开发笔记(七十三):红胖子8分钟带你使用opencv+dnn+yolov3识别物体的更多相关文章

  1. OpenCV开发笔记(七十二):红胖子8分钟带你使用opencv+dnn+tensorFlow识别物体

    前言   级联分类器的效果并不是很好,准确度相对深度学习较低,本章使用opencv通过tensorflow深度学习,检测已有模型的分类.   Demo       可以猜测,1其实是人,18序号类是狗 ...

  2. OpenCV开发笔记(七十一):红胖子8分钟带你深入级联分类器训练

    前言   红胖子,来也!  做图像处理,经常头痛的是明明分离出来了(非颜色的),分为几块区域,那怎么知道这几块区域到底哪一块是我们需要的,那么这部分就涉及到需要识别了.  识别可以自己写模板匹配.特征 ...

  3. OpenCV开发笔记(六十五):红胖子8分钟带你深入了解ORB特征点(图文并茂+浅显易懂+程序源码)

    若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...

  4. OpenCV开发笔记(六十九):红胖子8分钟带你使用传统方法识别已知物体(图文并茂+浅显易懂+程序源码)

    若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...

  5. OpenCV开发笔记(六十四):红胖子8分钟带你深入了解SURF特征点(图文并茂+浅显易懂+程序源码)

    若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...

  6. OpenCV开发笔记(五十五):红胖子8分钟带你深入了解Haar、LBP特征以及级联分类器识别过程(图文并茂+浅显易懂+程序源码)

    若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...

  7. OpenCV开发笔记(五十六):红胖子8分钟带你深入了解多种图形拟合逼近轮廓(图文并茂+浅显易懂+程序源码)

    若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...

  8. OpenCV开发笔记(七十四):OpenCV3.4.1+ffmpeg3.4.8交叉编译移植到海思平台Hi35xx平台

    前言   移植opencv到海思平台,opencv支持对视频进行解码,需要对应的ffmpeg支持.   Ffmpeg的移植   Ffmpeg的移植请参考之前的文章:<FFmpeg开发笔记(十): ...

  9. 【opencv学习笔记七】访问图像中的像素与图像亮度对比度调整

    今天我们来看一下如何访问图像的像素,以及如何改变图像的亮度与对比度. 在之前我们先来看一下图像矩阵数据的排列方式.我们以一个简单的矩阵来说明: 对单通道图像排列如下: 对于双通道图像排列如下: 那么对 ...

随机推荐

  1. pytest封神之路第四步 内置和自定义marker

    可以通过命令行查看所有marker,包括内置和自定义的 pytest --markers 内置marker 内置marker本文先讲usefixtures .filterwarnings .skip ...

  2. Jmeter(二十四) - 从入门到精通 - JMeter函数 - 中篇(详解教程)

    1.简介 在性能测试中为了真实模拟用户请求,往往我们需要让提交的表单内容每次都发生变化,这个过程叫做参数化.JMeter配置元件与前置处理器都能帮助我们进行参数化,但是都有局限性,为了帮助我们能够更好 ...

  3. 关于KeePass实现mstsc远程桌面(rdp协议)的自动登录

    本文的Keepass版本:KeePass Password Safe Version 2.45 首先介绍一下Keepass,引用官网的解释如下: KeePass is a free open sour ...

  4. Spring Boot学习(一)初识Spring Boot

    Spring Boot 概述 Spring Boot 是所有基于 Spring 开发的项目的起点.Spring Boot 的设计是为了让你尽可能快的跑起来 Spring 应用程序并且尽可能减少你的配置 ...

  5. minium-微信小程序自动化框架-python,官方文档

    minium文档 个人将其部署到了自己的服务器上,如有需要可以访问共同学习这个minium 用python来实现小程序自动化测试... 文档地址 http://49.232.203.244:3000/ ...

  6. 刷题[CISCN2019 华东南赛区]Web4

    解题思路 打开有一个链接,那先点击一下 发现url处,很像命令执行,试一试.发现无论是什么都是no response,又是各种尝试 发现直接传?url=/etc/passwwd可以爆出回显,难道不是命 ...

  7. 有没有异常处理翻车过的,绩效还被打了C

    絮叨 因为程序异常处理问题,就在前几天龙叔的服务挂了几秒钟. 完了,马上季度末打绩效,竟然在这里翻车了,心如刀绞啊. 虽然没有影响到用户体验,但是找到问题并解决掉问题是工程师日常追求之一. 作为一个优 ...

  8. Spring属性注入(set方式、构造函数方式、p名称空间、spel、复杂类型)

    1.set注入方式 (1)注入的为值类型(八大数据类型)的数据 配置文件: <?xml version="1.0" encoding="UTF-8"?&g ...

  9. 神奇的字符串匹配:扩展KMP算法

    引言 一个算是冷门的算法(在竞赛上),不过其算法思想值得深究. 前置知识 kmp的算法思想,具体可以参考 → Click here trie树(字典树). 正文 问题定义:给定两个字符串 S 和 T( ...

  10. 基于COCA词频表的文本词汇分布测试工具v0.1

    美国语言协会对美国人日常使用的英语单词做了一份详细的统计,按照日常使用的频率做成了一张表,称为COCA词频表.排名越低的单词使用频率越高,该表可以用来统计词汇量. 如果你的词汇量约为6000,那么这张 ...