OpenCV实战【2】HOG+SVM实现行人检测

HOG是什么?

方向梯度直方图( Histogram of Oriented Gradient, HOG )特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子。它通过
计算和统计图像局部区域的梯度方向直方图来构成特征。Hog特征结合SVM分类器已经被广泛应用于图像识别中。

HOG vs SIFT

SIFT :对特征点的描述方法
HOG :对一定区域的特征量的描述方法
1、可以表现较大的形状
2、非常适合行人及车辆检测
假设我们在智能驾驶中要检测行人:
正样本:
行人
负样本:
负样本
识别的本质是要找到正样本和负样本最本质的区别。例如行人在肩部具有横向边缘、两臂具有竖向边缘。而非行人样本中的边缘是杂乱无章的。因此可以通过构建梯度的直方图来检测形状。由于直方图损失了空间信息,所以HOG将图像分割为一个一个小的区域(联系阈值处理中的分块处理法),对小的区域分别构建直方图,然后拼接得到一个大的直方图。
HOG的缺点: 速度慢,实时性差;难以处理遮挡问题。
HOG特征不具有旋转鲁棒性,以及尺度鲁棒性

HOG步骤

1、Gamma矫正(增强图像的对比度)
2、计算梯度信息
3、以cell(一个像素块)为单位计算梯度直方图
4、以block(几个cell为一个block)为单位,对特征量进行归一化
具体步骤:
在这里插入图片描述
一般来说对梯度方向进行九等分量化。
一般以3* 3的像素组成一个cell,这样每个cell就可以得到一个9维的直方图。
每 3*3个cell组成一个block,在每个block进行归一化:
block
归一化的目的:增强对亮度的鲁棒性。

HOG在检测行人中的方式

通常采用滑窗的方式:
1
计算滑窗中包含的像素的梯度直方图,然后与行人模板中的直方图进行对比(如利用各种矩),当两者十分相似时,我们就认为这个区域是行人区域。
从而延生出的问题:
由于模板是固定大小的,因此只能检测固定大小的行人。当图像中的行人尺寸发生变化时,如何使用一个单一的模板检测?

Opencv实现

OpenCV实现了两种类型的基于HOG特征的行人检测,分别是SVM和Cascade,OpenCV自带的级联分类器的文件的位置在“XX\opencv\sources\data\hogcascades”(OpenCV4.x版本可用)。
opencv自带的人数检测文件,所在位置在opencv的安装目录下(下面是我的安装位置):
D:\Program Files\opencv\sources\samples\cpp

HOGDescriptor的构造函数:

 CV_WRAP HOGDescriptor() : winSize(64,128), blockSize(16,16), blockStride(8,8),
        cellSize(8,8), nbins(9), derivAperture(1), winSigma(-1),
        histogramNormType(HOGDescriptor::L2Hys), L2HysThreshold(0.2), gammaCorrection(true),
        free_coef(-1.f), nlevels(HOGDescriptor::DEFAULT_NLEVELS), signedGradient(false)
    {}

窗口大小 winSize(64,128), 块大小blockSize(16,16), 块滑动增量blockStride(8,8), 胞元大小cellSize(8,8), 梯度方向数nbins(9)。
上面这些都是HOGDescriptor的成员变量,括号里的数值是它们的默认值,它们反应了HOG描述子的参数。

nBins表示在一个胞元(cell)中统计梯度的方向数目,例如nBins=9时,在一个胞元内统计9个方向的梯度直方图,每个方向为180/9=20度。

HOGDescriptor中有两种Detector分别是:getDaimlerPeopleDetector、getDefaultPeopleDetector

行人检测HOG+SVM步骤

参考的代码:

#include <opencv2/objdetect.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/videoio.hpp>
#include <iostream>
#include <iomanip>

using namespace cv;
using namespace std;


class Detector
{
	//enum Mode { Default, Daimler } m;
	enum { Default, Daimler };//定义枚举类型
	int m;
	HOGDescriptor hog, hog_d;
public:
	Detector(int a) : m(a), hog(), hog_d(Size(48, 96), Size(16, 16), Size(8, 8), Size(8, 8), 9)//构造函数,初始化对象时自动调用,m,hog,hog_d是数据成员,后跟一个放在圆括号中的初始化形式
	{
		hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
		hog_d.setSVMDetector(HOGDescriptor::getDaimlerPeopleDetector());
	}
	void toggleMode() { m = (m == Default ? Daimler : Default); }
	string modeName() const { return (m == Default ? "Default" : "Daimler"); }
	vector<Rect> detect(InputArray img)
	{
		// Run the detector with default parameters. to get a higher hit-rate
		// (and more false alarms, respectively), decrease the hitThreshold and
		// groupThreshold (set groupThreshold to 0 to turn off the grouping completely).
		vector<Rect> found;
		if (m == Default)
			hog.detectMultiScale(img, found, 0, Size(8, 8), Size(32, 32), 1.05, 2, false);
		else if (m == Daimler)
			hog_d.detectMultiScale(img, found, 0.5, Size(8, 8), Size(32, 32), 1.05, 2, true);
		return found;
	}
	void adjustRect(Rect& r) const
	{
		// The HOG detector returns slightly larger rectangles than the real objects,
		// so we slightly shrink the rectangles to get a nicer output.
		r.x += cvRound(r.width * 0.1);
		r.width = cvRound(r.width * 0.8);
		r.y += cvRound(r.height * 0.07);
		r.height = cvRound(r.height * 0.8);
	}
};
//修改参数区域
static const string keys = "{ help h   |   | print help message }"
"{ camera c | 0 | capture video from camera (device index starting from 0) }"
"{ video v  | D:/opencv/opencv4.0/opencv4.0.0/sources/samples/data/vtest.avi| use video as input }";

int main(int argc, char** argv)
{
	CommandLineParser parser(argc, argv, keys);		//keys:描述可接受的命令行参数的字符串
	parser.about("This sample demonstrates the use ot the HoG descriptor.");//设置相关信息。相关信息会在 printMessage 被调用时显示。
	if (parser.has("help"))
	{
		parser.printMessage();
		return 0;
	}
	int camera = parser.get<int>("camera");
	string file = parser.get<string>("video");
	if (!parser.check())//检查解析错误。当错误发生时返回true。错误可能是转换错误、丢失参数等。
	{
		parser.printErrors();
		return 1;
	}

	VideoCapture cap;
	if (file.empty())
		cap.open(camera);
	else
		cap.open(file.c_str());
	if (!cap.isOpened())
	{
		cout << "Can not open video stream: '" << (file.empty() ? "<camera>" : file) << "'" << endl;
		return 2;
	}

	cout << "Press 'q' or <ESC> to quit." << endl;
	cout << "Press <space> to toggle between Default and Daimler detector" << endl;
	//Default and Daimler detector
	Detector detector(1);		//初始化使用Daimler detector

	Mat frame;
	for (;;)
	{
		cap >> frame;
		if (frame.empty())
		{
			cout << "Finished reading: empty frame" << endl;
			break;
		}
		int64 t = getTickCount();
		vector<Rect> found = detector.detect(frame);
		t = getTickCount() - t;

		// show the window
		{
			ostringstream buf;
			buf << "Mode: " << detector.modeName() << " ||| "
				<< "FPS: " << fixed << setprecision(1) << (getTickFrequency() / (double)t);
			putText(frame, buf.str(), Point(10, 30), FONT_HERSHEY_PLAIN, 2.0, Scalar(0, 0, 255), 2, LINE_AA);
		}
		for (vector<Rect>::iterator i = found.begin(); i != found.end(); ++i)
		{
			Rect& r = *i;
			detector.adjustRect(r);
			rectangle(frame, r.tl(), r.br(), cv::Scalar(0, 255, 0), 2);
		}
		imshow("People detector", frame);

		// interact with user
		const char key = (char)waitKey(30);
		if (key == 27 || key == 'q') // ESC
		{
			cout << "Exit requested" << endl;
			break;
		}
		else if (key == ' ')
		{
			detector.toggleMode();
		}
	}
	return 0;
}

简化后的对单张图片的检测

#include <opencv2/objdetect.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/videoio.hpp>
#include <iostream>
#include <iomanip>

using namespace cv;
using namespace std;


class Detector
{
	//enum Mode { Default, Daimler } m;
	enum { Default, Daimler };//定义枚举类型
	int m;
	HOGDescriptor hog, hog_d;
public:
	Detector(int a) : m(a), hog(), hog_d(Size(48, 96), Size(16, 16), Size(8, 8), Size(8, 8), 9)//构造函数,初始化对象时自动调用,m,hog,hog_d是数据成员,后跟一个放在圆括号中的初始化形式
	{
		hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
		hog_d.setSVMDetector(HOGDescriptor::getDaimlerPeopleDetector());
	}
	void toggleMode() { m = (m == Default ? Daimler : Default); }
	string modeName() const { return (m == Default ? "Default" : "Daimler"); }
	vector<Rect> detect(InputArray img)
	{
		// Run the detector with default parameters. to get a higher hit-rate
		// (and more false alarms, respectively), decrease the hitThreshold and
		// groupThreshold (set groupThreshold to 0 to turn off the grouping completely).
		vector<Rect> found;
		if (m == Default)
			hog.detectMultiScale(img, found, 0, Size(8, 8), Size(32, 32), 1.05, 2, false);
		else if (m == Daimler)
			hog_d.detectMultiScale(img, found, 0.5, Size(8, 8), Size(32, 32), 1.05, 2, true);
		return found;
	}
	void adjustRect(Rect& r) const
	{
		// The HOG detector returns slightly larger rectangles than the real objects,
		// so we slightly shrink the rectangles to get a nicer output.
		r.x += cvRound(r.width * 0.1);
		r.width = cvRound(r.width * 0.8);
		r.y += cvRound(r.height * 0.07);
		r.height = cvRound(r.height * 0.8);
	}
};


int main(int argc, char** argv)
{

	Detector detector(1);		//初始化使用Daimler detector
	Mat img=imread("D:\\opencv_picture_test\\HOG行人检测\\timg.jpg");
	vector<Rect> found = detector.detect(img);
	for (vector<Rect>::iterator i = found.begin(); i != found.end(); ++i)
	{
		Rect& r = *i;
		detector.adjustRect(r);		
		rectangle(img, r.tl(), r.br(), cv::Scalar(0, 255, 0), 2);
	}
	imshow("People detector", img);
	waitKey(0);
	return 0;
}

结果:
显示


Reference:

OpenCV实战4: HOG+SVM实现行人检测
HOG detectMultiScale 参数分析
CommandLineParser类(命令行解析类)
C++语法:构造函数以及析构函数
《数字图像处理PPT.李竹版》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章