基于OpenCV的简单人流量统计
生活随笔
收集整理的這篇文章主要介紹了
基于OpenCV的简单人流量统计
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
? ? ? ?學習OpenCV快一年了,最近做了一個簡單的人流量統計的項目,分享給大家。
? ? ? ?本次人流量統計用的是純OpenCV的技術,沒有涉及深度學習的知識,如果大家深度學習做得好的話,效果會更好。
? ? ? ?首先介紹我的環境Windows10+OpenCV3.4.3+VS2017?
OpenCV代碼實現
?? ? ? 首先是頭文件,相關函數以及全局變量的設定。
//頭文件 #include "stdafx.h" #include <opencv2/opencv.hpp> #include <iostream>using namespace cv; using namespace std;//鼠標操作,畫一條紅線,撞線計數 void on_MouseHandle(int event, int x, int y, int flags, void* param); void DrawLine(Mat &img, Rect box);Rect g_box; bool g_DrawingBox = false; Point p1, p2; int Thecount = 0; int precolor = 0; int all = 0;然后我們將進入主程序部分:
int main(int argc, char**) {//全局變量的初始化g_box = Rect(-1, -1, 0, 0);p1 = Point(0, 0);p2 = Point(0, 0);//打開視頻文件VideoCapture capture;capture.open("E:/The Program/OpenCV3.3.4/Video/manflow.mp4");if (!capture.isOpened()) {printf("could not find the video file...\n");return -1;}// create windowsMat frame;Mat bsmaskMOG2;Mat threshimg;//讀取人形掩碼Mat man1, man2, man3;man1 = imread("C:/Users/dlgker/Desktop/man1.png");man2 = imread("C:/Users/dlgker/Desktop/man2.png");man3 = imread("C:/Users/dlgker/Desktop/man3.png");cvtColor(man1, man1, COLOR_BGR2GRAY);//轉化為灰度圖像,便于后面的圖形矩匹配cvtColor(man2, man2, COLOR_BGR2GRAY);cvtColor(man3, man3, COLOR_BGR2GRAY);vector<vector<Point>> contours;//存取輪廓的點vector<Vec4i> hierarchy;//存取輪廓的層級//Mat bsmaskKNN;namedWindow("input video", CV_WINDOW_AUTOSIZE);//namedWindow("MOG2", CV_WINDOW_AUTOSIZE);//namedWindow("KNN Model", CV_WINDOW_AUTOSIZE);capture.read(frame);//因手機像素太高,未來便于觀察,把圖像縮小為以前的0.6倍resize(frame, frame, Size(round(frame.cols*0.6), round(frame.rows*0.6)));//鼠標回調函數setMouseCallback("input video", on_MouseHandle, (void*)&frame);//3×3的矩形核Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));// intialization BS//基于MIG2的背景去除法Ptr<BackgroundSubtractor> pMOG2 = createBackgroundSubtractorMOG2();//基于KNN的背景去除法//Ptr<BackgroundSubtractor> pKNN = createBackgroundSubtractorKNN();bool drawline = false;? ? ? ?進過一段時間的操作,我們的代碼初始化完成了。代碼中man1,man2和man3讀取的是相關彩色的人形掩碼。關于掩碼的提取相信大家不陌生,利用一些簡單的OpenCV處理技術就可以實現,比如說人站在白底的墻前,拍一張照片,然后使用一次OpenCV里的閾值函數就可以提取出人形掩碼,當然,也可以使用背景去除法,直接用QQ截圖去截取掩碼圖像就可以,我當時也是圖方便,也是采用的這種方法,總之,方法有很多種,大家去嘗試。例如這是其中一張人形掩碼,做這個人形掩碼的目的是為了我們后面的輪廓Hu據匹配,以識別出人。
? ? ? ?此處,我們可以用兩種背景剔除的方法,一種是基于BackgroundSubtractorMIG2的背景去除法,BackgroundSubtractorMOG2用于動態目標檢測,用到的是基于自適應混合高斯背景建模的背景減除法,相對于BackgroundSubtractorMOG,其具有更好的抗干擾能力,特別是光照變化。還有一種是基于KNN的背景去除法,這是一種基于深度學習的方法。
while (capture.read(frame)) {//因手機像素太高,未來便于觀察,把圖像縮小為以前的0.6倍resize(frame, frame, Size(round(frame.cols*0.6), round(frame.rows*0.6)));// MOG BS//用MOG方法實現背景去除pMOG2->apply(frame, bsmaskMOG2);//判斷用戶是否劃線if (g_DrawingBox){drawline = true;}if (drawline){DrawLine(frame, g_box);}//開運算,除去相關噪點morphologyEx(bsmaskMOG2, bsmaskMOG2, MORPH_OPEN, kernel, Point(-1, -1));//設置閾值,把圖像變為2值圖像0->0,1->255threshold(bsmaskMOG2, threshimg, 200, 255, THRESH_BINARY);//提取輪廓findContours(threshimg, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);for (size_t i = 0; i < contours.size(); i++){double length,alllength;//獲取輪廓的長度length = arcLength(contours[i],1);alllength = frame.cols + frame.rows;//把輪廓長度小于閾值的輪廓去除掉,相當于再一次去除噪點if (length > alllength) {double match1, match2, match3;//進行Hu矩匹配,識別出視頻中的人match1 = matchShapes(threshimg, man1, CONTOURS_MATCH_I1, 0);match2 = matchShapes(threshimg, man2, CONTOURS_MATCH_I1, 0);match3 = matchShapes(threshimg, man3, CONTOURS_MATCH_I1, 0);//當匹配系數足夠高的時候,開始畫出人的識別框,矩形框的中心點,便于撞線計數if (match1 < 0.1 || match2 < 0.1 || match3 < 0.1){RotatedRect box;//找出最小的外接矩形box = minAreaRect(contours[i]);Point2f vertex[4];Point center;box.points(vertex);center.x = round((vertex[0].x + vertex[2].x + vertex[3].x + vertex[1].x) / 4);center.y = round((vertex[1].y + vertex[2].y + vertex[3].y + vertex[0].y) / 4);//通過畫四條線,畫出矩形框for (int p = 0; p < 4; p++){line(frame, vertex[p], vertex[(p + 1) % 4], Scalar(0, 0, 255), 2, 8, 0);}//畫出矩形的中心點circle(frame, center, 5, Scalar(0, 255, 0), 5, 8, 0);}}}//imshow("MOG2", threshimg);//創建一個直線迭代器對象,//用于檢測用戶所畫的紅線上綠色通道上是否有上跳沿的信號//如果有上跳沿信號,全局變量Thecount開始計數LineIterator it(frame, p1, p2, 8);all = 0;for (int j = 0; j < it.count; ++j, ++it){if (precolor == 0 && (int)(*it)[1] == 255){Thecount++;//precolor = 0;}all = all + (int)(*it)[1];if ((int)(*it)[1] == 255){precolor = 255;break;}}if (all == 0){precolor = 0;}stringstream ss;string str;string str1="The Count: ";ss << Thecount;ss >> str;str = str1 + str;//把記好的數打印到圖像的(20,20)坐標點上putText(frame, str, Point(20, 20), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 255));imshow("input video", frame);//cout << "Count:" << Thecount << endl;// KNN BS mask//pKNN->apply(frame, bsmaskKNN);//imshow("KNN Model", bsmaskKNN);char c = waitKey(3);if (c == 27) {break;}}capture.release();return 0; }然后我們進入讀取視頻的環節,首先我們需要利用背景去除法來做背景去除。完成后的圖像如下所示:
? ? ? ?可見,僅僅通過背景去除的圖形還是不能識別的,當人經過時,人的周圍會產生很大的噪聲造成算法的誤判。但是相對人來說,周圍像素值明顯偏低。因為背景去除后的圖像噪聲有很多,我們需要進行一次開運算才能去除部分細小的噪聲。開運算后的結果:
圖片中的細小噪點得到充分的減少,經過一次閾值后,結果可見如下:
? ? ? ?可見,經過一番的操作,人形提取已基本完成。接下來就是我們的識別環節了,不慌,這個時候,我們還要最后對我們處理的圖像做除去噪點的操作。這個時候,我們需要用arcLength()函數獲取每個輪廓的長度,對于長度比較小的輪廓,我們將其剔除,不讓其參與識別。把符合要求的輪廓與預先讀取的掩碼進行輪廓Hu矩匹配,因為圖像的hu矩是一種具有平移、旋轉和尺度不變性的圖像特征。所以在圖形識別方面具有非常廣泛的應用。當然,現在也有非常多的識別方法,最多的就是基于深度學習的SSD和Yolov2和Yolov3模型。這些深度學習最大的優點就是識別穩定,識別效果好,準確性高。但是這些深度學習方法也有一些缺點,對硬件的要求高,特別是顯卡,不然視頻處理速度就很慢,對于我這種電腦沒有N卡的人,也算是一種傷害。二是深度學習需要大量的訓練集,而對于一些特殊的應用,劣勢就明顯了。好了,我們再往下說,識別出來后,我們可以把人給框起來,然后用綠色的點把人中心畫出來。而對于人流量的計數,就是要利用這個綠色的點。當用戶劃一條線后,我們把線上的值設置為BGR為(0,0,255),我們再在這條直線上設置一個直線的迭代器,就可以把直線上的點的信息統計下來,當人穿過直線時,綠色的點會穿過直線,這時,直線上部分點的綠色通道將由0跳變到255,通過收集這種上跳沿的信號。我們就可以實現人流量的計數了。
? ? ? ?最后,還有兩個函數的代碼,也分享給大家。
//鼠標事件的回調函數 void on_MouseHandle(int event, int x, int y, int flags, void* param) {Mat &image = *(Mat*)param;switch (event){case EVENT_MOUSEMOVE:{if (g_DrawingBox){g_box.width = x - g_box.x;g_box.height = y - g_box.y;}}break;case EVENT_LBUTTONDOWN:{g_DrawingBox = true;g_box = Rect(x, y, 0, 0);}break;case EVENT_LBUTTONUP:{g_DrawingBox = false;if (g_box.width < 0){g_box.x += g_box.width;g_box.width *= -1;}if (g_box.height < 0){g_box.y += g_box.height;g_box.height *= -1;}DrawLine(image, g_box);}break;default:break;} } //劃線函數 void DrawLine(Mat &img, Rect box) {p1 = Point(box.x,box.y);p2 = Point(box.x + box.width, box.y + box.height);line(img, p1, p2, Scalar(0, 0, 255), 3, 8);//rectangle(img, box.tl(), box.br(), Scalar(0, 0, 255)); }? ? ? ?這樣,我們基于OpenCV的簡單人流量統計就完成了。下面是我們做出的最終效果:
? ? ? ?可見,效果還不錯。可能大家會說當多人重疊,一起走過鏡頭的時候,可能會識別不出來。其實大家不要那么緊張,我們可以把攝像頭懸掛起來,從上往下拍,利用同樣的原理可以進行,基于人頭的人流量統計,車流量統計……這樣就不會有重疊了。
總結
以上是生活随笔為你收集整理的基于OpenCV的简单人流量统计的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 前端学习(1933)vue之电商管理系统
- 下一篇: python闯红灯检测斑马线检测红绿灯检