標籤:
閾值法:
對每一幀進行閾值處理,取較低的一個閾值進行二值化處理。假設以下為視頻流中的任意一幀
代表任意一點處的亮度值(灰階空間),代表一個固定的閾值,對當前幀做以下二值化處理:
該演算法比較適合運動物體的亮度大於周圍環境的情況,如夜晚的汽車前燈、尾燈等。
下面基於閾值法的前景檢測,完成夜晚視頻中車輛的檢測、跟蹤和計數:
【演算法的步驟】
1、首先畫出感興趣地區,步驟再此博文已詳細描述:視頻中畫出感興趣地區
2、對進入感興趣地區的車輛進行前燈的檢測,跟蹤和計數
代碼如下:
#include<iostream>using namespace std;#include<string>#include<opencv2\core\core.hpp>#include<opencv2\highgui\highgui.hpp>#include<opencv2\imgproc\imgproc.hpp>using namespace cv;//Trackbar控制的變數及該值的最大值int thresh = 200;const int MAX_THRESH = 255;//Trackbar控制的函數void thresh_callback(int,void*);int w_h = 25;const int MAX_W_H = 100;int vehicleFrequency = 2;//這個值不能過於大,否則拍到的車輛裡,沒有車牌const int MAX_VEHICLEFREQUENCY = 15;//全域標量Mat frame;//原視頻流的幀Mat grayFrame;//色彩空間轉化Mat binaryFrame;//二值化Mat kernel;//進行形態學處理的核函數Rect box_vehicle;bool vehicleBool;//判斷是否有車輛//儲存邊緣vector<vector<Point>> contours;vector<Vec4i> hierarchy;Rect vehicleRect;//通過前燈檢測,定位出來的車輛/*----定義滑鼠事件--畫矩形地區:作用當兩個車燈----*///第一步:全域變數bool drawing_box = false;bool gotBox = false;Rect box;Point downPoint;void mouseRectHandler(int event, int x, int y, int flags, void *param){switch (event){case CV_EVENT_MOUSEMOVE:if (drawing_box){//滑鼠的移動到downPoint的右下角if( x >=downPoint.x && y >= downPoint.y){box.x = downPoint.x;box.y = downPoint.y;box.width = x - downPoint.x;box.height = y - downPoint.y;}//滑鼠的移動到downPoint的右上方if( x >= downPoint.x && y <= downPoint.y){box.x = downPoint.x;box.y = y;box.width = x - downPoint.x;box.height = downPoint.y - y;}//滑鼠的移動到downPoint的左上方if( x <= downPoint.x && y <= downPoint.y){box.x = x;box.y = y;box.width = downPoint.x - x;box.height = downPoint.y - y;}//滑鼠的移動到downPoint的左下角if( x <= downPoint.x && y >= downPoint.y){box.x = x;box.y = downPoint.y;box.width = downPoint.x -x;box.height = y - downPoint.y;}}break;case CV_EVENT_LBUTTONDOWN://按下滑鼠,代表可以可以開始畫矩形drawing_box = true;//記錄起點downPoint = Point(x,y);break;case CV_EVENT_LBUTTONUP://鬆開滑鼠,代表結束畫矩形drawing_box = false;gotBox = true;break;default:break;}}int main(int argc,char*argv[]){//視頻流的輸入VideoCapture video(argv[1]);if( !video.isOpened())return -1;//得到形態學處理的kernelkernel = getStructuringElement(MORPH_CROSS,Size(3,3),Point(-1,-1));//register,註冊滑鼠事件namedWindow("video",CV_WINDOW_AUTOSIZE);setMouseCallback("video",mouseRectHandler,NULL);//輸出視頻流,並同時畫出感興趣地區for(;;){video>>frame;if(!frame.data)break;//控制閾值createTrackbar("前燈檢測:","video",&thresh,MAX_THRESH,thresh_callback);thresh_callback( 0, 0 );//這一行很重要//去除面積過小的createTrackbar("面積:","video",&w_h,MAX_W_H,thresh_callback);thresh_callback( 0, 0 );//代表一輛車,如果出現的頻數大於createTrackbar("頻數:","video",&vehicleFrequency,MAX_VEHICLEFREQUENCY,NULL);//當得到box時,去除setMouseCallbackif(gotBox){setMouseCallback("video",NULL,NULL);break;}rectangle(frame,box,Scalar(255,0,0),2);//畫出感興趣地區imshow("video",frame);if(waitKey(33) == ‘q‘)break;}//用於跟蹤演算法和計數bool currentVehicleBool = false; bool previousVehicleBool = false;int numberVehicle = 0;//用於記錄每一輛車的的幀數,避免是一閃而過的,可能不是車輛int numberVehicleFrame = 1; int currentVehicleNumber = 0;int previousVehicleNumber = 0;//以上畫出感興趣地區,以下可以判斷是否為車輛for(;;){vehicleRect.width = 0;vehicleRect.height = 0;video >> frame;if(!frame.data)break;//色彩空間轉換CV_Assert(frame.channels() == 3);cvtColor(frame,grayFrame,COLOR_BGR2GRAY);//控制閾值createTrackbar("前燈檢測:","video",&thresh,MAX_THRESH,thresh_callback);thresh_callback( 0, 0 );//這一行很重要//去除面積過小的createTrackbar("面積:","video",&w_h,MAX_W_H,thresh_callback);thresh_callback( 0, 0 );createTrackbar("頻數:","video",&vehicleFrequency,MAX_VEHICLEFREQUENCY,NULL);//畫出感興趣地區rectangle(frame,box,Scalar(255,255,0),2);//畫出檢測到的前燈rectangle(frame,vehicleRect,Scalar(0,255,0),2);//判斷vehicleRect和box是否相交,來判斷是否有車輛box_vehicle = box & vehicleRect;//顯示二值化imshow("二值化",binaryFrame);if(box_vehicle.width >0 && vehicleRect.height > 0){currentVehicleBool = true;if( previousVehicleBool ==false ){//代表這是第幾輛車numberVehicle++;//記錄當前幀的車輛的標號currentVehicleNumber = numberVehicle;}//用於計數,該輛車出現的幀數if( currentVehicleNumber == previousVehicleNumber ){numberVehicleFrame ++;if( numberVehicleFrame == vehicleFrequency+1){cout << "抓拍" << endl;//imshow("抓拍",frame);//儲存圖片}}}else{currentVehicleBool = false;//將歸為1,重新計數numberVehicleFrame = 1;}//記錄上一幀的是否有車輛previousVehicleBool = currentVehicleBool;//記錄上一幀,車輛的標號(number)previousVehicleNumber = currentVehicleNumber;//顯示圖片imshow("video",frame);if(waitKey(33) == ‘q‘)break;}return 0;}void thresh_callback(int,void*)//得到新的vehicleRect{if(!grayFrame.data)return;//閾值處理threshold(grayFrame,binaryFrame,double(thresh),255.0,THRESH_BINARY);//均值濾波medianBlur(binaryFrame,binaryFrame,5);//形態學處理morphologyEx(binaryFrame,binaryFrame,MORPH_OPEN,kernel,Point(-1,-1),7,BORDER_REPLICATE);//儲存邊緣vector<vector<Point>> contours;vector<Vec4i> hierarchy;//找到邊緣,注意為什麼先把binaryFrame複製一遍Mat tempBinaryFrame = binaryFrame.clone();findContours( tempBinaryFrame,contours,hierarchy, CV_RETR_TREE , CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );//注意這個findContours中binaryImage既是輸入又是輸出vector<vector<Point>> contours_poly( contours.size() );//儲存vector<Rect> boundRect;boundRect.clear();for(int index = 0;index < contours.size(); index++){approxPolyDP( Mat(contours[index]), contours_poly[index], 3, true );Rect rect = boundingRect( Mat(contours_poly[index]) );//做篩選(去除較小的w_h)if( rect.width < w_h || rect.height < w_h)continue;boundRect.push_back(rect);}//得到整個汽車前燈輪廓for(int index = 0;index < boundRect.size() ;index++){if(index ==0)vehicleRect = boundRect[0];elsevehicleRect = vehicleRect | boundRect[index];//得到最大的矩形}}
【運行結果】:
opencv-視頻處理-即時前景檢測-閾值法