HOG+SVM(OpenCV案例源码train_HOG.cpp解读)

有所更改,参数不求完备,但求实用。源码参考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

HOG+SVM(OpenCV案例源码train_HOG.cpp解读)的相关文章

Canny,先Scharr得梯度再Canny,三通道黑色背景展示结果(OpenCV案例源码edge.cpp)

有所更改,参数不求完备,但求实用.源码参考D:\source\opencv-3.4.9\samples\cpp\edge.cpp #include<opencv2\opencv.hpp> #include<iostream> using namespace cv; using namespace std; int edgeThresh = 1; int edgeThreshScharr = 1; Mat image, gray, blurImage, edge1, edge2, c

JEECG社区 一个微信教育网站案例源码分享

微信教育网站案例演示: http://t.cn/RvPgLcb 源码分享: http://pan.baidu.com/s/1cUImy 截图演示: JEECG社区 一个微信教育网站案例源码分享,布布扣,bubuko.com

使用jqplot创建报表(一) 初学后写的第一个案例源码

一.初学后写的第一个案例源码 效果图: 代码如下: <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ include file="common.jsp"%> <html> <head> <script type="text/javascript" src=&q

能把opencv的源码也进行调试吗?(需要pdb文件才行)

能把opencv的源码也进行调试吗?(需要pdb文件才行)1.我是用的Qt Creator,然后"工具\选项\调试器\概要\源码路径映射"中,选择"添加Qt源码",目标路径是Qt的源码路径,比如是"C:\Qt\Qt5.7.0\5.7\Src", 源路径是Qt Creator自动补充的.2.按照这种方法也可以将VC CRT源码给添加进来,目标路径是VC CRT的源码路径,比如是"C:\Program Files (x86)\Microso

RPG游戏案例源码

再发一份我的另外一个作品吧,源码OzgGameRPG,本游戏主要用于技术的研究和积累,目前只部署了ios平台.游戏第一次启动的时候会把数据库复制到可写文件夹,若数据库结构因为一些改动发生了变化,需要把旧的数据库删除. 编译时需要将本游戏的目录复制到cocos2d-x-2.2.3/projects/. 游戏中所用的一部分图片来自 http://www.66rpg.com/type.php?t=17 另一部分图片来自http://usui.moo.jp/rpg_tukuru.html 声音文件来自 

java实现 swing模仿金山打字 案例源码

java实现 swing模仿金山打字 案例源码,更多Java技术就去Java教程网.http://java.662p.com 代码: <font size="3">import Java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Image; import java.awt.Toolkit; import java.io.File; import java.io.IO

使用SBT开发Akka第一个案例源码解析MapActor、ReduceActor、AggregateActor

学习了使用SBT开发Akka第一个案例源码解析MapActor.ReduceActor.AggregateActor,应用MapActor对单词计数,发信息给ReduceActor,对信息进行local级的汇总,然后交给AggregateActor. 案例如下: class MapActor(reduceActor: ActorRef) extend Actor{ val STOP_WORDS_LIST=List("a","is") deg receive: Rec

Java利用webservice创建接口案例源码

环境要求: JDK1.7,并配置Java的环境变量 BaseDao  接口: /** * 要使得成为一个可供访问的接口,必须添加:@WebService * */ @WebServicepublic interface BaseDao {    //添加一个加法的接口        @WebResult(name="addddddre")    public int add(@WebParam(name="add01")int a,@WebParam(name=&q

SVM与C++源码实现

1. 推导出函数间隔最小 2. 约束优化函数变形至如下形式 /* min 1/2*||w||^2 s.t.  (w[i]*x[i] + b[i] - y[i]) >= 0; */ 3. 对偶函数 /* min(para alpha) 1/2*sum(i)sum(j)(alpha[i]*alpha[j]*y[i]*y[j]*x[i]*x[j]) - sum(alpha[i]) s.t. sum(alpha[i] * y[i]) = 0 C>= alpha[i] >= 0 * 4. 根据KK