在opencv的初等應用上,對運動物體的識別主要有幀差或背景差兩種方式。
幀差法主要的原理是當前幀與前一幀作差取絕對值;
背景差主要的原理是當前幀與背景幀作差取絕對值;
在識別運動車輛上主要需要以下9個步驟:
(1)讀取幀(VideoCapture,Mat)
(2)ROI選定(Rect)
(3)平滑處理(GaussianBlur)
(4)灰度處理(cvtColor,CV_RGB2GRAY)
(5)幀差或背景差(absdiff)
(6)二值化(threshold)
(7)膨脹(dilate)
(8)腐蝕(erode)
(9)繪制運動車輛(findContours,rectangle)
(1)讀取幀(外部視頻讀取:Videocapture;幀讀取:Mat)
這個屬於比較基礎的內容這里不做解釋,直接附上源碼:
1 /****************************************************** 2 函數名稱: MyClass 3 函數功能: 初始化 4 傳入參數: 5 返 回 值: 6 建立時間: 2018-05-17 7 修改時間: 8 建 立 人: 9 修 改 人: 10 其它說明: 11 ******************************************************/ 12 MyClass::MyClass() 13 { 14 //capture.open("F:/code/vsc++/Car_Find/Car_Find/交大七公里交通監控.avi"); 15 capture.open("交大七公里交通監控.avi"); 16 //capture = 0; 17 if (!capture.isOpened())//判斷是否打開視頻文件 18 { 19 exit(1); 20 } 21 FPS = capture.get(CV_CAP_PROP_FPS); 22 } 23 /****************************************************** 24 函數名稱: ~MyClass 25 函數功能: 釋放空間 26 傳入參數: 27 返 回 值: 28 建立時間: 2018-05-17 29 修改時間: 30 建 立 人: 31 修 改 人: 32 其它說明: 33 ******************************************************/ 34 MyClass::~MyClass() 35 { 36 capture.release(); 37 } 38 /****************************************************** 39 函數名稱: play 40 函數功能: 播放法 41 傳入參數: 42 返 回 值: 43 建立時間: 2018-05-17 44 修改時間: 45 建 立 人: 46 修 改 人: 47 其它說明: 48 ******************************************************/ 49 void MyClass::play(){ 50 Mat frame; 51 namedWindow("播放界面按Esc退出", 0); 52 cvResizeWindow("播放界面按Esc退出", 600, 500); 53 while (true) 54 { 55 capture >> frame; 56 if (frame.empty())break; 57 imshow("播放界面按Esc退出", frame); 58 if (waitKey(1000.0 / FPS) == 27)//按原FPS顯示 59 { 60 cout << "ESC退出!" << endl; 61 break; 62 } 63 } 64 }
處理結果:
(2)ROI選定(Rect)
這里使用Rect進行划ROI(感興趣區域),畫出一個矩陣的ROI。在這里最好的ROI是梯形(減少除了道路外的不必要的干擾)
這里提下做法:做一個灰度的mask(遮罩層),然后調整mask的形狀大小。ps:感興趣的可以做下。
(3)平滑處理(處理方法:GaussianBlur)
這里采用高斯平滑處理,在拍攝視頻的時候會受到電流的干擾,但這個干擾時均勻存在的,所以采用高斯平滑處理可以去除電流的干擾。
方法很簡單,源碼如下:
1 /****************************************************** 2 函數名稱: getSmooth 3 函數功能: 平滑處理 4 傳入參數: 5 返 回 值: 6 建立時間: 2018-05-17 7 修改時間: 8 建 立 人: 9 修 改 人: 10 其它說明: 11 ******************************************************/ 12 Mat MyClass::getSmooth(Mat frame) 13 { 14 Mat img; 15 GaussianBlur(frame, img, Size(13, 13),2,2); 16 return img; 17 }
處理結果:
(4)灰度處理(處理方法:cvtColor;核:CV_RGB2GRAY)
RGB的圖對我們的識別會造成一定的干擾或者說增加處理的難度,這里將原幀轉換為灰度圖像;
在平滑處理 后,直接調用opencv的cvtColor方法:
1 /****************************************************** 2 函數名稱: getGray 3 函數功能: 灰度處理 4 傳入參數: 5 返 回 值: 6 建立時間: 2018-05-17 7 修改時間: 8 建 立 人: 9 修 改 人: 10 其它說明: 11 ******************************************************/ 12 Mat MyClass::getGray(Mat frame) 13 { 14 Mat img; 15 cvtColor(frame, img, CV_RGB2GRAY); 16 return img; 17 }
處理結果:
(5)幀差或背景差(處理方法:absdiff)
不管是幀差和背景差都是需要獲取當前幀跟對比幀,其中對比幀的獲取和幀的作差是處理的關鍵。
在獲取到處理好的灰度圖在進行幀差處理。
以下為對比幀的獲取 的源碼:
1 /****************************************************** 2 函數名稱: play 3 函數功能: 播放幀差法 4 傳入參數: 5 返 回 值: 6 建立時間: 2018-05-17 7 修改時間: 8 建 立 人: 9 修 改 人: 10 其它說明: 11 ******************************************************/ 12 void MyClass::play(){ 13 Mat frame,preframe,curframe,result; 14 namedWindow("播放界面按Esc退出", 0); 15 //cvResizeWindow("播放界面按Esc退出", 600, 500); 16 while (true) 17 { 18 capture >> frame; 19 if (frame.empty())break; 20 if (preframe.empty())preframe = frame.clone();//首幀處理 21 curframe = frame.clone(); 22 imshow("播放界面按Esc退出", frame); 23 if (waitKey(1000.0 / FPS) == 27)//按原FPS顯示 24 { 25 cout << "ESC退出!" << endl; 26 break; 27 } 28 preframe = frame.clone();//記錄當前幀為下一幀的前幀 29 } 30 }
以下為幀差處理的源碼:
/****************************************************** 函數名稱: getDiff 函數功能: 幀差化處理 傳入參數: 返 回 值: 建立時間: 2018-05-17 修改時間: 建 立 人: 修 改 人: 其它說明: ******************************************************/ Mat MyClass::getDiff(Mat preframe,Mat frame) { Mat img; absdiff(preframe, frame, img); return img; }
處理結果:
這里一個車的灰度輪廓已經可以識別出來了,但是我們的目的是將車輛的輪廓識別處理,為了更加精准,去除不必要的干擾,需要下面的處理。
(6)二值化(處理方法:threshold,閾值類型:CV_THRESH_BINARY)
Threshold函數詳解,其中CV_THRESH_BINARY:當前點值大於閾值時,取Maxval,也就是第四個參數,下面再不說明,否則設置為0
由於這些車輛原畫偏暗,這里設定閾值為30,處理當前點值大於閾值時,取白色255。
源碼如下:
1 /****************************************************** 2 函數名稱: getEz 3 函數功能: 二值化處理 4 傳入參數: 5 返 回 值: 6 建立時間: 2018-05-17 7 修改時間: 8 建 立 人: 9 修 改 人: 10 其它說明: 11 ******************************************************/ 12 Mat MyClass::getEz(Mat frame) 13 { 14 Mat img; 15 threshold(frame, img,30, 255, CV_THRESH_BINARY); 16 return img; 17 }
處理結果:
車的黑白輪廓已經顯示出來了,不過這里還不是一塊整體,所以需要進行膨脹處理。
ps:有些博文的處理方式是先腐蝕處理后膨脹處理,這個在一定程度上會消除干擾點。但是這里考慮到車輛中有摩托車,如果先腐蝕處理的話會將摩托車給消除掉,造成識別精度不高。
(7)膨脹(處理方法:dilate)
膨脹的目的在於將一輛車拼合成一塊完整的個體,以達到標識的目的
opencv提供了dilate的方法進行處理,源碼如下:
1 /****************************************************** 2 函數名稱: getPz 3 函數功能: 膨脹處理 4 傳入參數: 5 返 回 值: 6 建立時間: 2018-05-17 7 修改時間: 8 建 立 人: 9 修 改 人: 10 其它說明: 11 ******************************************************/ 12 Mat MyClass::getPz(Mat frame) 13 { 14 Mat img; 15 Mat element=getStructuringElement(MORPH_RECT, Size(11, 30)); 16 dilate(frame, img, element); 17 return img; 18 }
處理結果:
這時候已經合成一塊了,但是如果旁邊的車靠的太近的話,會導致多輛車黏合成一塊,所以下面做腐蝕處理。
(8)腐蝕(處理方法:erode)
腐蝕的目的在於將因為膨脹而導致的黏合,還有非關鍵點和區域的清除,以達到區分標識的目的。
opencv提供了erode的方法進行處理,源碼如下:
1 /****************************************************** 2 函數名稱: getFs 3 函數功能: 腐蝕處理 4 傳入參數: 5 返 回 值: 6 建立時間: 2018-05-17 7 修改時間: 8 建 立 人: 9 修 改 人: 10 其它說明: 11 ******************************************************/ 12 Mat MyClass::getFs(Mat frame) 13 { 14 Mat img; 15 Mat element = getStructuringElement(MORPH_RECT, Size(10, 16)); 16 erode(frame, img, element); 17 return img; 18 }
處理結果:
ps:這里截的圖不是很好,看不出效果分離的效果。感興趣的可以下載下源碼去調試。
(9)繪制運動車輛(處理方法:findContours,rectangle)
這里需要將車輛的外圍輪廓描繪出來即可,所以findContours采用
mode取值“CV_RETR_EXTERNAL”,method取值“CV_CHAIN_APPROX_NONE”,即只檢測最外層輪廓,並且保存輪廓上所有點;
rectangle的使用並不難,但是這里要注意的是:
a.處理的圖像是原圖像的ROI;
b.findContours輸出的是處理過圖像的坐標位置
所以在使用rectangle的時候,畫區域的時候要加上原來ROI的起始位置的坐標,改成原來圖像的坐標位置。
源碼如下:
1 /****************************************************** 2 函數名稱: Deal 3 函數功能: ROI區域處理 4 傳入參數: 5 返 回 值: 6 建立時間: 2018-05-17 7 修改時間: 8 建 立 人: 9 修 改 人: 10 其它說明: 11 ******************************************************/ 12 Mat MyClass::Deal(Mat preframe, Mat frame) 13 { 14 Mat result = frame.clone(); 15 Mat curimageROI = preframe(Rect(preframe.cols / 5, preframe.rows / 5, preframe.cols / 1.5, preframe.rows / 1.5)); 16 Mat preimageROI = frame(Rect(frame.cols / 5, frame.rows / 5, frame.cols / 1.5, frame.rows / 1.5)); 17 18 Mat sm_pre, sm_cur; 19 sm_pre = getSmooth(preimageROI); 20 sm_cur = getSmooth(curimageROI); 21 imshow("平滑處理", sm_cur); 22 23 Mat gray_pre, gray_cur; 24 gray_pre = getGray(sm_pre); 25 gray_cur = getGray(sm_cur); 26 imshow("灰度處理",gray_cur); 27 28 Mat diff; 29 diff = getDiff(gray_pre, gray_cur); 30 imshow("幀差處理",diff); 31 32 Mat ez; 33 ez = getEz(diff); 34 imshow("二值化處理", ez); 35 36 Mat pz; 37 pz = getPz(ez); 38 imshow("膨脹處理", pz); 39 40 Mat fs; 41 fs = getFs(pz); 42 imshow("腐蝕處理", fs); 43 44 vector<vector<Point> > contours; 45 findContours(fs, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); 46 vector<Rect> boundRect(contours.size()); 47 for (int i = 0; i < contours.size(); i++) 48 { 49 boundRect[i] = boundingRect(contours[i]); 50 rectangle(result, Rect(boundRect[i].x + frame.cols/5, boundRect[i].y + frame.rows/5, boundRect[i].width, boundRect[i].height), Scalar(0, 255, 0), 1);//在result上繪制正外接矩形 51 } 52 return result; 53 }
處理結果:
這里效果不是非常好可以勉強識別出車輛輪廓。但作為初級應用是足夠的。
如需要源碼請轉移至碼雲:https://gitee.com/cjqbaba/MediaTest/tree/Car_Find進行源碼克隆下載
如有問題請留言評論。轉載請注明出處,謝謝。