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.李竹版》

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