有所更改,参数不求完备,但求实用。源码参考D:\source\opencv-3.4.9\samples\cpp\train_HOG.cpp
【解读参考】https://blog.csdn.net/xiao__run/article/details/82902267
【HOG原理】https://livezingy.com/hogdescriptor-in-opencv3-1/
https://cloud.tencent.com/developer/article/1434949
#include<opencv2\opencv.hpp> #include <iostream> using namespace cv; using namespace cv::ml; using namespace std; //自定义函数 vector< float > get_svm_detector(const Ptr< SVM >& svm); void convert_to_ml(const std::vector< Mat > & train_samples, Mat& trainData); void load_images(const String & dirname, vector< Mat > & img_lst, bool showImages); void sample_neg(const vector< Mat > & full_neg_lst, vector< Mat > & neg_lst, const Size & size); void computeHOGs(const Size wsize, const vector< Mat > & img_lst, vector< Mat > & gradient_lst, bool use_flip); void test_trained_detector(String obj_det_filename, String test_dir, String videofilename); int main(int argc, char** argv) { String pos_dir = "./Data/Ng";//正样本路径 String neg_dir = "./Data/Ok";//负样本路径 String test_dir = "./Data/test";//测试样本路径 String obj_det_filename = "HOGpedestrian64x128.xml";//训练后生成的模型 String videofilename = ""; //视频文件,无 int detector_width = 64;//检测窗大小 int detector_height = 128; bool test_detector = "HOGpedestrian64x128.xml";//已有模型 bool visualization = false;//可视化训练步骤 bool flip_samples = false; //样本是否旋转180° //if (test_detector)//如果已有模型,则直接用于测试 //{ // test_trained_detector(obj_det_filename, test_dir, videofilename); // exit(0); //} vector< Mat > pos_lst, full_neg_lst, neg_lst, gradient_lst; vector< int > labels; load_images(pos_dir, pos_lst, visualization);//读取正样本 load_images(neg_dir, full_neg_lst, false);//读取负样本 sample_neg(full_neg_lst, neg_lst, Size(detector_width, detector_height));//制作负样本 //提取正样本HOG特征 computeHOGs(Size(detector_width, detector_height), pos_lst, gradient_lst, flip_samples); size_t positive_count = gradient_lst.size(); labels.assign(positive_count, +1);///////////////////////////////////////// clog << "...[done] ( positive count : " << positive_count << " )" << endl; //提取负样本HOG特征 computeHOGs(Size(detector_width, detector_height), neg_lst, gradient_lst, flip_samples);//此时gradient_lst包含了正、负样本集的HOG特征 size_t negative_count = gradient_lst.size() - positive_count; labels.insert(labels.end(), negative_count, -1);///////////////////////////////////////// CV_Assert(positive_count < labels.size()); clog << "...[done] ( negative count : " << negative_count << " )" << endl; Mat train_data; convert_to_ml(gradient_lst, train_data);//正、负样本集转换为用于训练的数据 //设定SVM参数 Ptr< SVM > svm = SVM::create(); svm->setCoef0(0.0);//都是默认值 svm->setDegree(3); svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 1000, 1e-3)); svm->setGamma(0); svm->setKernel(SVM::LINEAR);//LINEAR svm->setNu(0.5); svm->setP(0.1); // for EPS_SVR svm->setC(0.01); svm->setType(SVM::EPS_SVR); // EPS_SVR回归问题;C_SVC分类问题; //训练 svm->train(train_data, ROW_SAMPLE, labels); //生成模型 HOGDescriptor hog; hog.winSize = Size(detector_width, detector_height); hog.setSVMDetector(get_svm_detector(svm)); hog.save(obj_det_filename);//保存模型 //测试 test_trained_detector(obj_det_filename, test_dir, videofilename); return 0; } vector< float > get_svm_detector(const Ptr< SVM >& svm) { // get the support vectors Mat sv = svm->getSupportVectors(); const int sv_total = sv.rows; // get the decision function Mat alpha, svidx; double rho = svm->getDecisionFunction(0, alpha, svidx); //*括号中的条件不满足时,返回错误 CV_Assert(alpha.total() == 1 && svidx.total() == 1 && sv_total == 1); CV_Assert((alpha.type() == CV_64F && alpha.at<double>(0) == 1.) || (alpha.type() == CV_32F && alpha.at<float>(0) == 1.f)); CV_Assert(sv.type() == CV_32F); vector< float > hog_detector(sv.cols + 1); memcpy(&hog_detector[0], sv.ptr(), sv.cols*sizeof(hog_detector[0]));//内存拷贝函数,从源src所指的内存地址的起始位置开始拷贝n个字节到目标dst所指的内存地址的起始位置中。 hog_detector[sv.cols] = (float)-rho; return hog_detector; } //把训练样本集train_samples转换为可用于机器学习的数据trainData(其实就是保证:每行为1个样本,列是HOG特征) void convert_to_ml(const vector< Mat > & train_samples, Mat& trainData) { const int rows = (int)train_samples.size();//行数等于训练样本个数 const int cols = (int)std::max(train_samples[0].cols, train_samples[0].rows);//列数取样本图片中宽度与高度中较大的那一个 Mat tmp(1, cols, CV_32FC1); trainData = Mat(rows, cols, CV_32FC1); for (size_t i = 0; i < train_samples.size(); ++i) { CV_Assert(train_samples[i].cols == 1 || train_samples[i].rows == 1); if (train_samples[i].cols == 1)//如果是列向量(HOG特征组成的),则转置为行,保证1行1个样本 { transpose(train_samples[i], tmp);//转置 tmp.copyTo(trainData.row((int)i)); } else if (train_samples[i].rows == 1)//如果是行向量,则保持不变 { train_samples[i].copyTo(trainData.row((int)i)); } } } //读取图像,dirname路径下的图像保存到img_lst容器中,showImages是否显示每一张图 void load_images(const String & dirname, vector< Mat > & img_lst, bool showImages = false) { vector< String > files; glob(dirname, files);//获取路径中的所有图像 for (size_t i = 0; i < files.size(); ++i) { Mat img = imread(files[i]); if (img.empty()) { cout << files[i] << " is invalid!" << endl; continue; } if (showImages) { imshow("image", img); waitKey(1); } img_lst.push_back(img); } } //负样本制作,从full_neg_lst负样本容器中裁剪出size大小的图,保存到neg_lst容器中 void sample_neg(const vector< Mat > & full_neg_lst, vector< Mat > & neg_lst, const Size & size) { Rect box; box.width = size.width; box.height = size.height; const int size_x = box.width; const int size_y = box.height; for (size_t i = 0; i < full_neg_lst.size(); i++) if (full_neg_lst[i].cols > box.width && full_neg_lst[i].rows > box.height) { box.x = rand() % (full_neg_lst[i].cols - size_x); box.y = rand() % (full_neg_lst[i].rows - size_y); Mat roi = full_neg_lst[i](box); neg_lst.push_back(roi.clone()); } } //计算HOG特征,wsize尺寸的移动窗提取img_lst容器中图像的HOG特征,保存到gradient_lst容器中,use_flip是否对img_lst翻转180° void computeHOGs(const Size wsize, const vector< Mat > & img_lst, vector< Mat > & gradient_lst, bool use_flip) { HOGDescriptor hog; hog.winSize = wsize; Mat gray; vector< float > descriptors; for (size_t i = 0; i < img_lst.size(); i++) { if (img_lst[i].cols >= wsize.width && img_lst[i].rows >= wsize.height) { Rect r = Rect((img_lst[i].cols - wsize.width) / 2, (img_lst[i].rows - wsize.height) / 2, wsize.width, wsize.height); cvtColor(img_lst[i](r), gray, COLOR_BGR2GRAY); hog.compute(gray, descriptors, Size(8, 8));//Size(8,8)为窗口移动步长 gradient_lst.push_back(Mat(descriptors).clone()); if (use_flip) { flip(gray, gray, 1);//旋转180度 hog.compute(gray, descriptors, Size(8, 8)); gradient_lst.push_back(Mat(descriptors).clone()); } } } } //测试模型,用训练好的模型obj_det_filename,测试test_dir目录中的图片或者测试videofilename视频帧(两者都有,则测试后者) void test_trained_detector(String obj_det_filename, String test_dir, String videofilename) { HOGDescriptor hog; hog.load(obj_det_filename); vector< String > files; glob(test_dir, files); int delay = 0; VideoCapture cap; if (videofilename != "") { if (videofilename.size() == 1 && isdigit(videofilename[0])) cap.open(videofilename[0] - ‘0‘); else cap.open(videofilename); } obj_det_filename = "testing " + obj_det_filename; namedWindow(obj_det_filename, WINDOW_NORMAL); for (size_t i = 0;; i++) { Mat img; if (cap.isOpened()) { cap >> img; delay = 1; } else if (i < files.size()) { img = imread(files[i]); } if (img.empty()) { return; } vector< Rect > detections; vector< double > foundWeights; hog.detectMultiScale(img, detections, foundWeights); for (size_t j = 0; j < detections.size(); j++) { Scalar color = Scalar(0, foundWeights[j] * foundWeights[j] * 200, 0); rectangle(img, detections[j], color, img.cols / 400 + 1); } imshow(obj_det_filename, img); if (waitKey(delay) == 27) { return; } } }
原文地址:https://www.cnblogs.com/xixixing/p/12411292.html
时间: 2024-10-09 16:26:27