基于OpenCV的对象检测学习

HOG+SVM进行行人检测的方法是法国研究人员Dalal在2005的CVPR上提出的,已经被广泛应用于图像识别中。

一、HOG特征

HOG(Histograms of Oriented Gradients, HOG)定向梯度柱状图 算法的工作原理是创建图像中梯度方向分布的柱状图,然后以一种非常特殊的方式对其进行归一化。这种特殊的归一化使得Hog 能够有效地检测物体的边缘,即使在对比度很低的情况下也是如此。这些标准化的柱状图被放在一个特征向量(称为 HOG 描述符)中,可以用来训练机器学习算法,例如支持向量机(SVM),以根据图像中的边界(边)检测对象。由于它的巨大成功和可靠性,HOG 已成为计算机视觉中应用最广泛的目标检测算法之一。1

Hog特征提取的过程可大致理解为:将输入样本缩放为:64 x 128,把样本图像分割为8 x 8像素的单元(cellsize),把梯度方向平均划分为9个区间(bin),在每个单元里面对所有像素的梯度方向在各个方向区间进行直方图统计,得到一个9维的特征向量,每相邻的4个单元构成一个块(blocksize),把一个块内的特征向量联起来得到36维的特征向量,用块(blocksize)对样本图像进行扫描,扫描步长(blockstride)为一个单元(cellsize)。最后将所有块的特征串联起来,就得到了人体的特征。对于64x128的图像而言,每16x16的像素组成一个cell,每2x2个cellsize组成一个blocksize,因为每个cellsize有9个特征,所以每个块内有4 x 9 = 36个特征,以8个像素为步长(blockstride=(8,8)),那么,水平方向将有7个扫描窗口,垂直方向将有15个扫描窗口。也就是说,64x128的图片,总共有36 x 7 x 15 = 3780个特征。2

二、SVM算法

支持向量机(Support Vector Machines, SVM)是一种二分类模型,它的基本模型是定义在特征空间上的间隔最大的线性分类器,间隔最大使它有别于感知机;SVM还包括核技巧,这使它成为实质上的非线性分类器。SVM的的学习策略就是间隔最大化,可形式化为一个求解凸二次规划的问题,也等价于正则化的合页损失函数的最小化问题。SVM的学习算法就是求解凸二次规划的最优化算法。3

三、OpenCV代码

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace cv::ml;
using namespace std;

string positive_dir = "F:/study/code/elec_watch/positive";//正样本图像数据路径
string negative_dir = "F:/study/code/elec_watch/negative";//负样本图像数据路径

void get_hog_descriptor(const Mat& image, vector<float>& desc);//从图像image中获取特征描述子desc的方法
void generate_dataset(Mat& trainData, Mat& labels);//用来调用get_hog_descriptor方法,计算所有图像的特征描述子
void svm_train(Mat& trainData, Mat& labels);//SVM训练计算的特征描述子

int main(int argc, char** argv) {
	// read data and generate dataset
	Mat trainData = Mat::zeros(Size(3780, 26), CV_32FC1);//定义存放训练数据trainData的大小、类型
	Mat labels = Mat::zeros(Size(1, 26), CV_32SC1);//定义存放标签的大小、类型
	generate_dataset(trainData, labels);//调用generate_dataset获取trainData、labels

	// SVM train, and save model
	svm_train(trainData, labels);//调用svm训练特征描述子的方法

	// load model 
	Ptr<SVM> svm = SVM::load("F:/study/code/elec_watch/hog_elec1.xml");

	//  detect custom object
	Mat test = imread("F:/study/code/elec_watch/test/scene_02.jpg");
	resize(test, test, Size(0, 0), 0.2, 0.2);
	imshow("input", test);
	Rect winRect;
	winRect.width = 64;
	winRect.height = 128;
	int sum_x = 0;
	int sum_y = 0;
	int count = 0;

	// 开窗检测....
	for (int row = 64; row < test.rows - 64; row += 4) {
		for (int col = 32; col < test.cols - 32; col += 4) {
			winRect.x = col - 32;
			winRect.y = row - 64;
			vector<float> fv;
			get_hog_descriptor(test(winRect), fv);
			Mat one_row = Mat::zeros(Size(fv.size(), 1), CV_32FC1);
			for (int i = 0; i < fv.size(); i++) {
				one_row.at<float>(0, i) = fv[i];//一幅测试图像一行描述子
			}
			float result = svm->predict(one_row);
			if (result > 0) {
				// rectangle(test, winRect, Scalar(0, 0, 255), 1, 8, 0);
				count += 1;
				sum_x += winRect.x;
				sum_y += winRect.y;
			}
		}
	}

	// 显示box
	winRect.x = sum_x / count;
	winRect.y = sum_y / count;
	rectangle(test, winRect, Scalar(255, 0, 0), 2, 8, 0);
	imshow("object detection result", test);
	imwrite("F:/study/code/elec_watch/case02.png", test);
	waitKey(0);
	return 0;
}

//由一幅图像生成3780个描述子的实现方法
void get_hog_descriptor(const Mat& image, vector<float>& desc) {
	HOGDescriptor hog;
	int h = image.rows;
	int w = image.cols;
	float rate = 64.0 / w;
	Mat img, gray;
	resize(image, img, Size(64, int(rate * h)));//对传入的图像尺寸重置
	cvtColor(img, gray, COLOR_BGR2GRAY);
	Mat result = Mat::zeros(Size(64, 128), CV_8UC1);
	result = Scalar(127);
	Rect roi;
	roi.x = 0;
	roi.width = 64;
	roi.y = (128 - gray.rows) / 2;
	roi.height = gray.rows;
	gray.copyTo(result(roi));
	hog.compute(result, desc, Size(8, 8), Size(0, 0));//调用计算hog特征描述子的方法
	// printf("desc len : %d \n", desc.size());
}

//对正、负样本图像分别生成特征描述子,一起保存为trainData
void generate_dataset(Mat& trainData, Mat& labels) {
	vector<string> images;
	glob(positive_dir, images);//正样本路径下的图像数据存放到images中
	int pos_num = images.size();//获取正样本所有图像数据的数量
	for (int i = 0; i < images.size(); i++) {//循环正样本图像
		Mat image = imread(images[i].c_str());//通过[i]依次读取
		vector<float> fv;//定义存放描述子的参数fv
		get_hog_descriptor(image, fv);//调用生成描述子的方法(每一幅图像生成3780个描述子)
		for (int j = 0; j < fv.size(); j++) {//循环存放描述子
			trainData.at<float>(i, j) = fv[j];//每个正样本图像(i=10行)对应一行(j=3780列)特征描述子,共10*3780
		}
		labels.at<int>(i, 0) = 1;//正样本图像标签设为1
	}
	images.clear();
	glob(negative_dir, images);//清理images已经存放的正样本图像数据,改为存放负样本数据
	for (int i = 0; i < images.size(); i++) {
		Mat image = imread(images[i].c_str());
		vector<float> fv;
		get_hog_descriptor(image, fv);
		for (int j = 0; j < fv.size(); j++) {
			trainData.at<float>(i + pos_num, j) = fv[j];//i+pos_num表示在正样本描述子后接着保存负样本描述子
		}
		labels.at<int>(i + pos_num, 0) = -1;//负样本图像标签设为1
	}
}

//把计算得到的正、负样本特征描述子以及标签用SVM监督训练分类
void svm_train(Mat& trainData, Mat& labels) {
	printf("\n start SVM training... \n");
	Ptr<SVM> svm = SVM::create();
	svm->setC(2.67);
	svm->setType(SVM::C_SVC);
	svm->setKernel(SVM::LINEAR);
	svm->setGamma(5.383);
	svm->train(trainData, ROW_SAMPLE, labels);
	clog << "....[Done]" << endl;
	printf("end train...\n");

	// save xml
	svm->save("F:/study/code/elec_watch/hog_elec1.xml");
}

四、检测结果

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述


  1. HOG 特征提取算法(实践篇) ↩︎

  2. HOG特征提取 ↩︎

  3. 经典分类算法——SVM算法 ↩︎

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值