基于opencv的行人檢測(支持圖片視頻)
基于方向梯度直方圖(HOG)/線性支持向量機(SVM)算法的行人檢測方法中存在檢測速度慢的問題,如下圖所示,對一張400*490像素的圖片進行檢測要接近800毫秒,所以hog+svm的方法放在視頻中進行行人檢測時,每秒只能檢測1幀圖片,1幀/s根本不能達(dá)到視頻播放的流暢性。
本文采用先從視頻每幀的圖像中提取出物體的輪廓(也可以對前后兩針圖片做差,只對有變化的部分進行檢測,其目的一樣,都是減少運算的面積),再對每個輪廓進行HOG+SVM檢測,判斷是否為行人。可以大大的縮減HOG+SVM的面積,經(jīng)實測,檢測速度可以達(dá)到10幀/S,可以勉強達(dá)到視頻流暢的要求。
輪廓的提取用的是cv::findContours的API,感興趣的可以自己去查看相關(guān)資料
首先介紹下方向梯度直方圖。
在圖像或者視頻幀中,邊緣方向密度分布可以很好地描述局部目標(biāo)的形狀和表象,也就是說通過 HOG特征,可以有效地將人體和復(fù)雜背景區(qū)分出來。對于行人檢測中 HOG 特征提取的具體實現(xiàn)方法是: 將視頻中的每一幀通過滑動窗口切割成很小的區(qū)域( Cell) ,通過計算每個區(qū)域面的方向梯度特征,得到每個區(qū)域的方向特征直方圖,小區(qū)域再組成更大的區(qū)域,通過將區(qū)域的方向梯度特征組合起來并在塊單元中進行歸一化處理,就形成了一個 Block 內(nèi) HOG 描述子,遍歷搜索所有的方向特征從而最終構(gòu)成一個幀的 HOG 描述特征向量。算法的過程[3]分為:
①將一個視頻的每一幀進行灰度化處理。把視頻的每一幀彩色空間變成灰度空間,因為 HOG 中不需要彩色信息的幫助。
②對輸入的視頻的每一幀進行顏色空間的歸一化。由于視頻中人信息的復(fù)雜性,背影的灰暗程度以及光照的影響都對檢測器的魯棒性有一定的影響,歸一化可以很大程度上降低這些的影響。這 里 使 用gamma 校正: 對像素值求其平方根( 降低數(shù)值大小) 。
③計算像素梯度。這是 HOG 特征檢測中最重要的一個環(huán)節(jié),通過像素的梯度方向直方圖來描述像素的特征。特別注意的是我們不需要做平滑處理,因為平滑處理的本質(zhì)就是迷糊圖像,降低了像素邊緣信息,因而就不能很好地提取邊緣信息來表達(dá)特征。
④將圖像劃分成小 cell。這一步我們需要為計算梯度,建立梯度方向直方圖定義一個載體,因此這里把圖像分割成很小的區(qū)域,這里稱為細(xì)胞單元,實驗表明6 × 6 像素的細(xì)胞單元效果最佳。接著采用 9 個直方圖來統(tǒng)計一個細(xì)胞單元里面的特征信息。360°不考慮
正負(fù)方向,把方向分成 9 份,如圖 1 所示,稱為 bin,也就是每一個 bin 對應(yīng) 20°,這樣就把梯度方向映射到直方圖里面,9 個方向特征向量代表 9 個 bin,增幅就代表每一個 bin 的權(quán)值。
⑤統(tǒng)計每個 cell 的梯度直方圖即可形成每個 cell的 descriptor。
⑥將每幾個 cell 組成一個 block,一個 block 內(nèi)所有 cell 的特征串聯(lián)起來,便得到該 block 的 HOG 特征descriptor。
⑦將圖像內(nèi)所有 block 的 HOG 特征收集起來就可得到該圖像特征向量。
支持向量機
支持向量機( Support Vector Machine) 就是風(fēng)險降低到最小程度上,尋找最優(yōu)的解決方案。視頻檢測特征分類中,就是針對低維空間的線性不可分問題,通過核函數(shù)映射到高維空間達(dá)到線性可分,再進行線性分割實現(xiàn)特征分類。
SVM 具有以下幾個特點:
( 1) 小樣本。
( 2) 非 線 性 問 題。即針對線性的不可分問題,SVM 通過松弛變量以及核函數(shù)進行解決。
( 3) 高維模式識別。在某些樣本,例如密集型特征,可以達(dá)到幾萬甚至十幾萬的維數(shù),如果不對樣本進行降維,SVM 也能夠找出支持向量樣本,對這些特征訓(xùn)練出優(yōu)秀的分類器。
視頻檢測代碼:
void video_test() { void display(Mat, vector<Rect>&); //void Crop_picture(); //void train(); //void save_hard_example(); //Crop_picture(); //裁切負(fù)樣本圖片,每張負(fù)樣本圖片隨機裁成10張 //train(); //訓(xùn)練正負(fù)樣本 //save_hardexample() //根據(jù)正負(fù)樣本得到的檢測子,對INRIAPerson/Train/neg/中的圖片進行測試,并將錯檢的樣本保存 //train(); //訓(xùn)練正負(fù)樣本及難例樣本 //加載svm分類器的系數(shù) HOGDescriptor hog; string str; vector<float> detector; /*ifstream fin("HOGDetectorForOpenCV.txt"); while (getline(fin, str)) { detector.push_back(stringToNum<float>(str)); } */ vector<Rect> people; VideoCapture capture(VideotestPath); /*if (!capture.isOpened()) return -1;*/ Mat frame, foreground; int num = 0; Ptr<BackgroundSubtractorMOG2> mod = createBackgroundSubtractorMOG2(); while (true) { vector<Rect> rect6; if (!capture.read(frame)) break; mod->apply(frame, foreground, 0.01); hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector()); //hog.setSVMDetector(detector); vector<Rect> rect5; display(foreground, rect5); vector<Rect> ret = rect5; for (auto i = 0; i != ret.size(); i++) { Mat a = frame; if (ret[i].x > 50 && ret[i].y > 50 && ret[i].x + ret[i].width <670 && ret[i].y + ret[i].height < 520) { ret[i].x = ret[i].x - 50; ret[i].y = ret[i].y - 50; ret[i].width = ret[i].width + 100; ret[i].height = ret[i].height + 100; } Mat src(a(ret[i])); cout << ret[i].x << " " << ret[i].y << " " << ret[i].width << " " << ret[i].height << endl; // imshow("aa", src); waitKey(0); // cv::namedWindow("src", CV_WINDOW_NORMAL); if (ret[i].width >= 64 && ret[i].height >= 128) hog.detectMultiScale(src, people, 0, Size(4, 4), Size(0, 0), 1.07, 2); //cout << people.size()<<endl; for (size_t j = 0; j < people.size(); j++) { people[j].x += ret[i].x; people[j].y += ret[i].y; rect6.push_back(people[j]); //rectangle(frame, people[j], cv::Scalar(0, 0, 255), 2); } //imshow(" ", frame); waitKey(0); } //因為多尺度檢測得到的結(jié)果矩形框較大,按比例縮減矩形框 for (auto h = 0; h != rect6.size(); h++) { rect6[h].x += cvRound(rect6[h].width*0.1); rect6[h].width = cvRound(rect6[h].width*0.8); rect6[h].y += cvRound(rect6[h].height*0.07); rect6[h].height = cvRound(rect6[h].height*0.8); rectangle(frame, rect6[h], cv::Scalar(0, 0, 255), 1); //rect2[h] = boundingRect(frame); } imshow(" ", frame); waitKey(1); } waitKey(); }
提取輪廓的代碼:
void display(Mat gray_diff, vector<Rect>& rect) { //Mat res = src.clone(); vector<vector<Point>> cts; //定義輪廓數(shù)組 findContours(gray_diff, cts, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); //查找輪廓,,模式為只檢測外輪廓,并存儲所有的輪廓點 //vector<Rect> rect; //定義矩形邊框 for (int i = 0; i < cts.size(); i++) { if (contourArea(cts[i])>th_area) //計算輪廓的面積,排除小的干擾輪廓 //查找外部矩形邊界 rect.push_back(boundingRect(cts[i])); //計算輪廓的垂直邊界最小矩形 } cout << rect.size() << endl; //輸出輪廓個數(shù) }
檢測效果:
進行HOG+SVM的四個頂點像素坐標(biāo)??梢钥吹矫看芜\算的面積小了很多。
當(dāng)然 ,是可以優(yōu)化,比如每兩幀圖片檢測一次,下一幀圖片保持上一幀的檢測結(jié)果。比如輪廓區(qū)域的面積怎么去合適的框起來,如何設(shè)定合適的閾值去濾掉小框,兩個框重疊時,怎么去優(yōu)化,減小進行運算的面積。本文只是給個思路,具體讀者可以自己去實現(xiàn)。
貼下github 有興趣的可以去讀下 ,樣本集用的INRIA行人檢測數(shù)據(jù)集,訓(xùn)練過程就不詳述了。
github:https://github.com/ttttthub/pedestrian-detection.git
到此這篇關(guān)于基于opencv的行人檢測(支持圖片視頻)的文章就介紹到這了,更多相關(guān)opencv 行人檢測內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
對比C語言中execv相關(guān)的執(zhí)行文件的三個函數(shù)
這篇文章主要介紹了對比C語言中execv相關(guān)的執(zhí)行文件的三個函數(shù),分別為execv()函數(shù)和execve()函數(shù)以及execvp()函數(shù),需要的朋友可以參考下2015-08-08如何通過wrap malloc定位C/C++的內(nèi)存泄漏問題
用C/C++開發(fā)的程序執(zhí)行效率很高,但卻經(jīng)常受到內(nèi)存泄漏的困擾。本文提供一種通過wrap malloc查找memory leak的思路。2021-05-05C++循環(huán)鏈表之約瑟夫環(huán)的實現(xiàn)方法
這篇文章主要介紹了C++循環(huán)鏈表之約瑟夫環(huán)的實現(xiàn)方法,對于學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)與算法有一定的借鑒價值,需要的朋友可以參考下2014-09-09C語言關(guān)鍵字之a(chǎn)uto register詳解
這篇文章主要為大家介紹了C語言關(guān)鍵字之a(chǎn)uto register,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-01-01簡單了解設(shè)計模式中的裝飾者模式及C++版代碼實現(xiàn)
這篇文章主要介紹了簡單了解設(shè)計模式中的裝飾者模式及C++版代碼實現(xiàn),ConcreteComponent的引用(指針)也可以達(dá)到修飾的功能,需要的朋友可以參考下2016-03-03