版权声明:本文为博主原创文章,转载请附上博文链接! https://blog.csdn.net/Daker_Huang/article/details/86740019
首先我们要搞明白图像分类和对象检测的区别:
1、图像分类:
图像分类就是将这幅图像归为某一类,图像中的对象尽量要单一。
2、对象检测:
对象检测就是将不同的对象用框圈出来并规定为某一类,对象可以多个。
首先需要下载SSD模型,可以去github上下载,也可以使用自己训练的模型,这里我使用官网训练的caffe模型。
一个是二进制模型文件,一个是描述文件。这两个文件可以在这里下载:https://download.csdn.net/download/daker_huang/10960936
我们来看看描述文件,它的开头是这样的:
可以看到,该模型所需要的图像文件的维度是3通道,300*300的大小,所以我们先来定义一下宽高:
const size_t width = 300;
const size_t height = 300;//定义图像文件宽高
从上面的文件中我们可以知道ssd模型接受的图像文件是3通道的,这里需要将图像通道数转成3通道的:
Mat frame;
src.copyTo(frame);
if (frame.channels() == 4)
{
cvtColor(frame, frame, COLOR_BGRA2BGR);//将4通道转为3通道
}
if (frame.channels() == 1)
{
cvtColor(frame, frame,COLOR_GRAY2BGR );//将单通道转为3通道
}
在OpenCv-C++ -深度神经网络(DNN)模块-使用goolenet模型实现图像分类中读caffe模型使用的是dnn模块中的"readNet",在这篇文章中,依旧使用dnn模块中的"readNet"读取caffe模型。
string deploy_file = "D:/test/ssd/deploy.prototxt"; //读取描述文件
string model_file = "D:/test/ssd/VGG_VOC0712Plus_SSD_300x300_iter_240000.caffemodel";//模型文件
......
Net net = readNetFromCaffe(deploy_file, model_file);
图片中检测输出的结果需要标注,而我们有标签文件,在一开始就已经导入:
这里放上标签文件:
0,0,background
5,1,aeroplane
2,2,bicycle
15,3,bird
9,4,boat
40,5,bottle
6,6,bus
3,7,car
16,8,cat
57,9,chair
20,10,cow
61,11,diningtable
17,12,dog
18,13,horse
4,14,motorbike
1,15,person
59,16,pottedplant
19,17,sheep
58,18,sofa
7,19,train
63,20,tvmonitor
这里总共21种分类物体,不多,其实我们可以把每行英文名称前面的两个数字去掉,但是如果比较多就行不通了。这里我们用定义一个函数读取每行的英文名称:
vector<String> labels()
{
vector<String>objNames;
ifstream fp(label_file);//打开输入流,读入文件
if (!fp.is_open())
{
printf("文件读入失败!!!\n");
exit(-1);//直接退出
}
string name;//标签文件中都有对应的名字
while (!fp.eof())//当文件没有读到结尾
{
getline(fp, name);//读取每一行
if (name.length())
{
string temp1 = name.substr(name.find(",") + 1); //找到每行第一个逗号,从逗号后面开始取数据
string temp2 = temp1.substr(temp1.find(",") + 1);//找到新的(第二个)逗号,从逗号后面开始取数据
objNames.push_back(temp2);
}
}
/*for (vector<string>::iterator iter = objNames.begin(); iter != objNames.end(); ++iter)
{
//输出*iter才是输出那些字符串
cout << *iter << endl;
}*/
return objNames;
}
对象检测并画框:
float confidence_threshold = 0.2;
for (int i = 0; i < detectionMat.rows; i++)
{
float confidence = detectionMat.at<float>(i, 2); //置信度
if (confidence > confidence_threshold) {
size_t objIndex = (size_t)(detectionMat.at<float>(i, 1)); //标签文件的索引号
float tl_x = detectionMat.at<float>(i, 3) * frame.cols; //矩形框左上横坐标点
float tl_y = detectionMat.at<float>(i, 4) * frame.rows; //矩形框左上纵坐标点
float br_x = detectionMat.at<float>(i, 5) * frame.cols; //矩形框右下横坐标点
float br_y = detectionMat.at<float>(i, 6) * frame.rows; //矩形框右下纵坐标点
Rect object_box((int)tl_x, (int)tl_y, (int)(br_x - tl_x), (int)(br_y - tl_y));
rectangle(frame, object_box, Scalar(0, 0, 255), 2, 8, 0);
putText(frame, format("%s", objnames[objIndex].c_str()), Point(tl_x+1, tl_y+1),FONT_HERSHEY_PLAIN, 2.0, Scalar(255, 0, 0), 2);
// cout << objIndex << endl;
完整代码:
#include<opencv2/opencv.hpp>
#include<opencv2/dnn.hpp>
#include<iostream>
using namespace cv;
using namespace std;
using namespace cv::dnn;
const size_t width = 300;
const size_t height = 300;//定义图像文件宽高
vector<String> labels();
string label_file = "D:/test/ssd/labelmap.txt";
string deploy_file = "D:/test/ssd/deploy.prototxt";
string model_file = "D:/test/ssd/VGG_VOC0712Plus_SSD_300x300_iter_240000.caffemodel";
int main(int argc, char **argv)
{
Mat src = imread("D:/test/dnn_ssd.jpeg.jpg");
if (src.empty())
{
cout << "图像未找到!!!" << endl;
return -1;
}
vector<String> objnames = labels();
namedWindow("input image", WINDOW_AUTOSIZE);
imshow("input image",src);
Net net = readNetFromCaffe(deploy_file, model_file);
Mat frame;
src.copyTo(frame);
if (frame.channels() == 4)
{
cvtColor(frame, frame, COLOR_BGRA2BGR);//将4通道转为3通道
}
if (frame.channels() == 1)
{
cvtColor(frame, frame,COLOR_GRAY2BGR );//将单通道转为3通道
}
Mat blob_Img = blobFromImage(frame, 1.0f, Size(width, height), Scalar(104, 117, 123), false, false);
net.setInput(blob_Img, "data");//开始层
Mat detection=net.forward("detection_out");//最后一层
Mat detectionMat(detection.size[2], detection.size[3], CV_32F, detection.ptr<float>());
/*
detection.size[2]表示宽度
detection.size[3]表示高度
*/
float confidence_threshold = 0.2;
for (int i = 0; i < detectionMat.rows; i++)
{
float confidence = detectionMat.at<float>(i, 2); //置信度
if (confidence > confidence_threshold) {
size_t objIndex = (size_t)(detectionMat.at<float>(i, 1)); //标签文件的索引号
float tl_x = detectionMat.at<float>(i, 3) * frame.cols; //矩形框左上横坐标点
float tl_y = detectionMat.at<float>(i, 4) * frame.rows; //矩形框左上纵坐标点
float br_x = detectionMat.at<float>(i, 5) * frame.cols; //矩形框右下横坐标点
float br_y = detectionMat.at<float>(i, 6) * frame.rows; //矩形框右下纵坐标点
Rect object_box((int)tl_x, (int)tl_y, (int)(br_x - tl_x), (int)(br_y - tl_y));
rectangle(frame, object_box, Scalar(0, 0, 255), 2, 8, 0);
putText(frame, format("%s", objnames[objIndex].c_str()), Point(tl_x+1, tl_y+1),FONT_HERSHEY_PLAIN, 2.0, Scalar(255, 0, 0), 2);
//cout << objIndex << endl;
}
}
imshow("output image", frame);
waitKey(0);
return 0;
}
vector<String> labels()
{
vector<String>objNames;
ifstream fp(label_file);//打开输入流,读入文件
if (!fp.is_open())
{
printf("文件读入失败!!!\n");
exit(-1);//直接退出
}
string name;//标签文件中都有对应的名字
while (!fp.eof())//当文件没有读到结尾
{
getline(fp, name);//读取每一行
if (name.length())
{
string temp1 = name.substr(name.find(",") + 1); //找到每行第一个逗号,从逗号后面开始取数据
string temp2 = temp1.substr(temp1.find(",") + 1);//找到新的(第二个)逗号,从逗号后面开始取数据
objNames.push_back(temp2);
}
}
/*for (vector<string>::iterator iter = objNames.begin(); iter != objNames.end(); ++iter)
{
//输出*iter才是输出那些字符串
cout << *iter << endl;
}*/
return objNames;
}
运行结果: