yolo.h
#pragma once
#pragma once
#include<iostream>
#include<opencv2/opencv.hpp>
#define YOLO_P6 false //是否使用P6模型
struct Output {
int id; //结果类别id
float confidence; //结果置信度
cv::Rect box; //矩形框
};
class Yolo {
public:
Yolo() {
}
~Yolo() {}
bool readModel(cv::dnn::Net& net, std::string& netPath, bool isCuda); //读取模型
bool Detect(cv::Mat& SrcImg, cv::dnn::Net& net, std::vector<Output>& output); //前向传播
void drawPred(cv::Mat& img, std::vector<Output> result, std::vector<cv::Scalar> color); //绘制矩形框
void PathDB(std::string image_path, std::string imwrite_path, std::string xmlpath); //读取以及存放路径
void CreateXml(const char* img_name, const char* img_path, int img_width, int img_heigh, int check_number, std::string xmlpath); //, const char* object_name, int xmin, int ymin, int xmax, int ymax,std::string xmlpath
private:
#if(defined YOLO_P6 && YOLO_P6==true)
const float netAnchors[4][6] = { { 19,27, 44,40, 38,94 },{ 96,68, 86,152, 180,137 },{ 140,301, 303,264, 238,542 },{ 436,615, 739,380, 925,792 } };
const int netWidth = 1280; //ONNX图片输入宽度
const int netHeight = 1280; //ONNX图片输入高度
const int strideSize = 4; //stride size
#else
const float netAnchors[3][6] = { { 10,13, 16,30, 33,23 },{ 30,61, 62,45, 59,119 },{ 116,90, 156,198, 373,326 } };
const int netWidth = 640; //ONNX图片输入宽度
const int netHeight = 640; //ONNX图片输入高度
const int strideSize = 3; //stride size
#endif // YOLO_P6
const float netStride[4] = { 8, 16.0,32,64 };
float boxThreshold = 0.7;
float classThreshold = 0.7;
float nmsThreshold = 0.40;
float nmsScoreThreshold = boxThreshold * classThreshold;
//std::vector<std::string> className = { "person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light",
// "fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow",
// "elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee",
// "skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard",
// "tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple",
// "sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch",
// "potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone",
// "microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear",
// "hair drier", "toothbrush" }; //COCO数据集类别
std::vector<std::string> className = { "worter","open","unslot","broken","people","openwin","nocap" }; //铁路数据集类别
/*std::vector<std::string> className = { "element"};*/
};
yolo.cpp
#include <iostream>
#include"yolo.h"
#include "tinystr.h";
#include "tinyxml.h";
#include <list>
using namespace std;
using namespace cv;
using namespace cv::dnn;
bool Yolo::readModel(Net& net, string& netPath, bool isCuda = true) {
try {
net = readNet(netPath);
}
catch (const std::exception&) {
return false;
}
//cuda
if (isCuda) {
net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA_FP16);
}
//cpu
else {
net.setPreferableBackend(cv::dnn::DNN_BACKEND_DEFAULT);
net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);
}
return true;
}
bool Yolo::Detect(Mat& SrcImg, Net& net, vector<Output>& output) {
Mat blob;
int col = SrcImg.cols;
int row = SrcImg.rows;
int maxLen = MAX(col, row);
Mat netInputImg = SrcImg.clone();
if (maxLen > 1.2 * col || maxLen > 1.2 * row) {
Mat resizeImg = Mat::zeros(maxLen, maxLen, CV_8UC3);
SrcImg.copyTo(resizeImg(Rect(0, 0, col, row)));
netInputImg = resizeImg;
}
blobFromImage(netInputImg, blob, 1 / 255.0, cv::Size(netWidth, netHeight), cv::Scalar(0, 0, 0), true, false);
//如果在其他设置没有问题的情况下但是结果偏差很大,可以尝试下用下面两句语句
//blobFromImage(netInputImg, blob, 1 / 255.0, cv::Size(netWidth, netHeight), cv::Scalar(104, 117, 123), true, false);
//blobFromImage(netInputImg, blob, 1 / 255.0, cv::Size(netWidth, netHeight), cv::Scalar(114, 114,114), true, false);
net.setInput(blob);
std::vector<cv::Mat> netOutputImg;
net.forward(netOutputImg, net.getUnconnectedOutLayersNames());
std::vector<int> classIds;//结果id数组
std::vector<float> confidences;//结果每个id对应置信度数组
std::vector<cv::Rect> boxes;//每个id矩形框
float ratio_h = (float)netInputImg.rows / netHeight;
float ratio_w = (float)netInputImg.cols / netWidth;
int net_width = className.size() + 5; //输出的网络宽度是类别数+5
float* pdata = (float*)netOutputImg[0].data;
for (int stride = 0; stride < strideSize; stride++) { //stride
int grid_x = (int)(netWidth / netStride[stride]);
int grid_y = (int)(netHeight / netStride[stride]);
for (int anchor = 0; anchor < 3; anchor++) { //anchors
const float anchor_w = netAnchors[stride][anchor * 2];
const float anchor_h = netAnchors[stride][anchor * 2 + 1];
for (int i = 0; i < grid_y; i++) {
for (int j = 0; j < grid_x; j++) {
float box_score = pdata[4]; ;//获取每一行的box框中含有某个物体的概率
if (box_score >= boxThreshold) {
cv::Mat scores(1, className.size(), CV_32FC1, pdata + 5);
Point classIdPoint;
double max_class_socre;
minMaxLoc(scores, 0, &max_class_socre, 0, &classIdPoint);
max_class_socre = (float)max_class_socre;
if (max_class_socre >= classThreshold) {
//rect [x,y,w,h]
float x = pdata[0]; //x
float y = pdata[1]; //y
float w = pdata[2]; //w
float h = pdata[3]; //h
int left = (x - 0.5 * w) * ratio_w;
int top = (y - 0.5 * h) * ratio_h;
classIds.push_back(classIdPoint.x);
confidences.push_back(max_class_socre * box_score);
boxes.push_back(Rect(left, top, int(w * ratio_w), int(h * ratio_h)));
}
}
pdata += net_width;//下一行
}
}
}
}
//执行非最大抑制以消除具有较低置信度的冗余重叠框(NMS)
vector<int> nms_result;
NMSBoxes(boxes, confidences, nmsScoreThreshold, nmsThreshold, nms_result);
for (int i = 0; i < nms_result.size(); i++) {
int idx = nms_result[i];
Output result;
result.id = classIds[idx];
result.confidence = confidences[idx];
result.box = boxes[idx];
output.push_back(result);
}
} // 图片名字 绝对路径 宽度 高度 物体类别 x小坐标 y小坐标 x大坐标 y大坐标 保存xml路径
void Yolo::CreateXml(const char* img_name, const char* img_path, int img_width, int img_heigh, int check_number, std::string xmlpath)//const char* object_name, int xmin, int ymin, int xmax, int ymax,
{
std::string a = "E:/DeployYoloV5/DeployYoloV5/1.jpg";
const char* p = a.data();
TiXmlDocument doc; //创建一个文档类
//创建一个描述,构造参数(version,encoding,standalone)
TiXmlElement* rootElement = new TiXmlElement("annotation"); //创建一个根element root
TiXmlElement* PersonElement = new TiXmlElement("image");
TiXmlElement* PersonElement2 = new TiXmlElement("img_size");
rootElement->LinkEndChild(PersonElement);
rootElement->LinkEndChild(PersonElement2);
PersonElement->SetAttribute("img_name", img_name);
PersonElement->SetAttribute("img_path", img_path);
PersonElement2->SetAttribute("width", img_width);
PersonElement2->SetAttribute("height", img_heigh);
TiXmlElement* PersonElement3 = new TiXmlElement("object");
rootElement->LinkEndChild(PersonElement3);
PersonElement3->SetAttribute("Check_objectnumber", check_number);
/*PersonElement3->SetAttribute("name", object_name);
PersonElement3->SetAttribute("xmin", xmin);
PersonElement3->SetAttribute("ymin", ymin);
PersonElement3->SetAttribute("xmax", xmax);
PersonElement3->SetAttribute("ymax", ymax);*/
doc.LinkEndChild(rootElement);//文档添加root element
string filenamexml = ".xml";
//cout << xmlpath;
string totalpath = xmlpath + filenamexml;
const char* path = totalpath.data();
doc.SaveFile(path);//保存到文件new.xml
}
void Yolo::drawPred(Mat& img, vector<Output> result, vector<Scalar> color) {
for (int i = 0; i < result.size(); i++) {
//cout << result.size(); //查看检测到物体的数量
int left, top;
left = result[i].box.x;
top = result[i].box.y;
int color_num = i;
rectangle(img, result[i].box, color[result[i].id], 10, 8); //在图片中划出矩形框
string label = className[result[i].id] + ":" + to_string(result[i].confidence);
int baseLine;
Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
top = max(top, labelSize.height);
putText(img, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 5, color[result[i].id], 3);
}
Mat dst;
//resize(img, dst, Size(), 0.2, 0.3); //图片进行比例缩放
//imshow("1", dst); //展示窗口
//waitKey(); //窗口等待
//destroyAllWindows(); // 关闭所有窗口
//system("pause"); //让控制台暂停,等待用户信号
}
const char* object_name[100]; //声明一个char类型用于保存类别名字
void Yolo::PathDB(string image_path, string imwrite_path, std::string xmlpath) { // 读取模型路径
Yolo test;
Net net;
string model_path = "E:/yolov5-master/runs/train/exp/weights/best.onnx";
if (test.readModel(net, model_path, false)) {
cout << "read net ok!" << endl;
}
else {
cout << "No found path of onnx!";
}
//生成随机颜色
vector<Scalar> color;
srand(time(0));
for (int i = 0; i < 80; i++) {
int b = rand() % 256;
int g = rand() % 256;
int r = rand() % 256;
color.push_back(Scalar(b, g, r));
}
vector<String>src_test;
vector<Output> result;
Mat img = imread(image_path);
glob(image_path, src_test, false);//将文件夹路径下的所有图片路径保存到src_test中
if (src_test.size() == 0) {//判断文件夹里面是否有图片
printf("No image in file ! Please check the folder for pictures\n");
exit(1);
}
//aaa();
for (int i = 0; i < src_test.size(); i++)
{
Mat img = imread(src_test[i]);
int pos = src_test[i].find_last_of("\\"); // 切分找到最后一个名字
string imagepatht = src_test[i];
const char* image_pathcc = imagepatht.data(); //传入图片绝对路径
//cout << image_pathcc;
string save_name = src_test[i].erase(0, pos);
const char* image_name = save_name.data(); //传入图片名字
cv::Mat dst;
if (test.Detect(img, net, result))
{
test.drawPred(img, result, color);
if (result.size() > 0)
{
string name_xml_path = xmlpath + save_name;
cout << name_xml_path;
CreateXml(image_name, image_pathcc, img.rows, img.cols, result.size(), name_xml_path); //xml生成
imwrite(imwrite_path + save_name, img); //把有问题的图片进行保存 // label xm ym xmax ymax
}
result.clear(); // 对每次循环检测到图片信息的结果进行一次清除
img.release(); //释放资源
}
else
{
cout << "Detect Failed!" << endl;
}
continue;
}
}
YoloSave.cpp
// YoloSave.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include "yolo.h";
#include <iostream>
#include<opencv2//opencv.hpp>
#include<math.h>
using namespace std;
using namespace cv;
using namespace dnn;
int main(int argc, char* argv[]) //argc 总参数个数 argv[]是arcg个参数
{
Yolo path;
/*cout << "参数个数:" << argc << endl;
cout << "参数内容:";*/
for (int i = 0; i < argc; ++i)
{
cout << argv[i] << endl;
}
cout << "=============="<<endl;
string str1 = argv[1];
string str2 = argv[2];
string str3 = argv[3];
//cout << str1 << endl;
//cout << str2 << endl;
//cout << str3 << endl;
path.PathDB(str1,str2,str3); //image_path, imwrite_path, svacexml_path
//system("pause");
return 0;
}
// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单
// 入门使用技巧:
// 1. 使用解决方案资源管理器窗口添加/管理文件
// 2. 使用团队资源管理器窗口连接到源代码管理
// 3. 使用输出窗口查看生成输出和其他消息
// 4. 使用错误列表窗口查看错误
// 5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
// 6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件
#############################################################################
其余的头文件与源文件为xml提供的
需要传3个string类型的参数:
cmd命令行输入:
YoloSave.exe E:/yolov5-master/image_test E:/DeployYoloV5/YoloSave/outputimg E:/DeployYoloV5/YoloSave\outxml
查看结果:
这两个文件夹中生成了对应的检测到有问题的图片以及对应的xml