前言

  级联分类器的效果并不是很好,准确度相对深度学习较低,上一章节使用了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. Keepalived服务详解

    1. VRRP协议 1.1 VRRP协议概述 VRRP协议的出现是为了解决静态路由的单点故障,它是通过一种竞选机制来将路由任务交给某个vrrp路由器的 在VRRP物理结构中,有多个物理的VRRP路由器 ...

  2. Dubbo工作流程

    一.dubbo整体架构 其中Service 和 Config 层为 API,对应服务提供方来说是使用ServiceConfig来代表一个要发布的服务配置对象,对应服务消费方来说ReferenceCon ...

  3. 使用redis来调用iptables,封禁恶意IP

    话不多说,通常大多数站点都会有被薅羊毛的情况,防护无非也就是业务层做处理,短时内不再响应恶意请求啦.虽然不响应了,可还是会消耗资源的,比如我要从数据库(当然也可能是内存数据库)去查询下,你是不是恶意的 ...

  4. vue显示后端传递的图片流

    一.显示部分(组件我使用的vuetify) <template> <v-container fluid> <v-card width="100%" m ...

  5. Tensorflow学习笔记No.4.1

    使用CNN卷积神经网络(1) 简单介绍CNN卷积神经网络的概念和原理. 已经了解的小伙伴可以跳转到Tensorflow学习笔记No.4.2学习如和用Tensorflow实现简单的卷积神经网络. 1.C ...

  6. Vue3实战系列:结合 Ant-Design-of-Vue 实践 Composition API

    Vue 3 出来之后,很多人有如下想法,"又要学新东西啦"."我学不动了"等等. 但是事物总有它的两面性,前端知识更新的很快,利好勤奋好学的同学.计算机行业的迭 ...

  7. Java防止文件被篡改之文件校验和

    Java防止文件被篡改之文件校验和转载:请注明出处,谢谢! 1.为什么要防止文件被篡改?  答案是显然的,为了保证版权,系统安全性等.之前公司开发一个系统,技术核心是一个科学院院士的研究成果,作为一款 ...

  8. JVM 第六篇:极致优化 IDEA 启动速度

    本文内容过于硬核,建议有 Java 相关经验人士阅读. 1. 引言 相信做 Java 开发的同学,对 IDEA 这个工具应该都不陌生,即使不使用 IDEA 做开发,那么对 Eclipse 这个工具应该 ...

  9. 两大IT培训巨头,达内和传智播客哪个更好?

    多年来,从财报收入及培训规模角度来看,达内和传智播客分别在IT培训领域占据第一和第二的位置已经是不争的事实,但是从培训学员的角度来讲,选择达内和传智播客哪个更好呢,这两家机构在学员心目中的排名和营收的 ...

  10. MeteoInfoLab脚本示例:风场矢量图

    读取风场U/V变量数据,可以从U/V计算出风速:speed = sqrt(u*u+v*v).quiverm函数用来绘制风场矢量图,参数中包括U/V变量,如果要绘制彩色风场还需要第三个变量,这里是风速s ...