生活随笔
收集整理的這篇文章主要介紹了
数字图像处理合集终章——车流量统计(后附源码)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
題目要求: 包括1)基于高斯混合背景建模的運(yùn)動目標(biāo)提取;2)基于矩形度/圓形度/面積的車輛目標(biāo)判別;3)區(qū)域生長法獲取完整的車輛目標(biāo);4)統(tǒng)計不同方向的車流量(單位是輛/分鐘),對于白天場景下車流量能夠有效的統(tǒng)計。
題目分析: 首先明白混合高斯背景建模,其基本思想為:定義每個像素點(diǎn)的分布模型為由多個單高斯模型組成的集合,根據(jù)每一個新的像素值更新模型參數(shù),按照一定的準(zhǔn)則判斷哪些像素點(diǎn)為背景點(diǎn),哪些為前景點(diǎn),從而實(shí)現(xiàn)對運(yùn)動目標(biāo)的檢測。 使用混合高斯背景建模提取出前景之后,對前景進(jìn)行圖像預(yù)處理,進(jìn)行濾波、膨脹、腐蝕等操作得到較為完整的前景運(yùn)動車輛二值圖。在這一操作時,使用While大循環(huán)對每一幀進(jìn)行處理。辨識二值圖像中的車輛目標(biāo)時,可以使用opencv內(nèi)的Rect類,將前景車輛目標(biāo)圈出。在檢測時,將圖像中的一部分劃分出來作為檢測區(qū)域,當(dāng)車輛進(jìn)入這一區(qū)域時對其進(jìn)行計數(shù)。 綜上,重點(diǎn)在于辨識車輛目標(biāo)和進(jìn)行計數(shù)。
算法分析 1.系統(tǒng)模塊劃分 根據(jù)第二部分的分析,將本車流量統(tǒng)計系統(tǒng)分為如下幾個模塊。 1.1混合高斯背景建模 使用opencv自帶的高斯建模函數(shù)createBackgroundSubtractorMOG2()對視頻進(jìn)行背景建模。視頻中還可能存在影子等內(nèi)容,故使用setVarThreshold減少影子對之后測試的影響。使用BackgroundSubtractorMOG2中的操作apply對背景進(jìn)行更新。使用Scalar對獲取到的前景保持像素不變,背景換成零像素。對前景二值化處理即可進(jìn)行圖像預(yù)處理模塊。本模塊主要實(shí)現(xiàn)代碼如下所示。
Ptr
< BackgroundSubtractorMOG2
> pBgmodel
= createBackgroundSubtractorMOG2 ( ) ;
pBgmodel
- > setVarThreshold ( 500 ) ;
pBgmodel
- > apply ( image
, fgMask
) ;
foreGround
= Scalar
:: all ( 0 ) ;
image
. copyTo ( foreGround
, fgMask
) ; pBgmodel
- > getBackgroundImage ( backGround
) ;
1.2圖像預(yù)處理 對獲取到的每一幀二值圖進(jìn)行預(yù)處理,本系統(tǒng)中對獲取到的二值圖進(jìn)行了高斯濾波、膨脹、腐蝕等操作,使用的結(jié)構(gòu)元素為(13*13)大小的。主要實(shí)現(xiàn)代碼如下所示。
Mat element
= getStructuringElement ( MORPH_RECT
, Size ( 13 , 13 ) ) ;
GaussianBlur ( fgMask
, fgMask
, Size ( 5 , 5 ) , 0 ) ; dilate ( fgMask
, fgMask
, element
) ; erode ( fgMask
, fgMask
, element
) ;
1.3分析辨識車輛目標(biāo) 本系統(tǒng)中使用Rect類標(biāo)記每一輛車,通過輪廓檢測將車輛標(biāo)識出來,并畫出矩形類的輪廓。對得到的每一個矩形陣,設(shè)定最小寬度及最大寬度排除一些偽目標(biāo),再對當(dāng)前幀中的所有輪廓進(jìn)行排序,當(dāng)其中某個輪廓(連通分量)的面積小于當(dāng)前幀中最大輪廓(連通分量)面積的1/6時認(rèn)為其是偽目標(biāo)。將真實(shí)車輛目標(biāo)放入一個動態(tài)數(shù)組并將其畫出。通過兩層檢測基本能排除所有不是車輛的偽目標(biāo)并畫出車輛目標(biāo)。主要實(shí)現(xiàn)代碼如下所示。
vector
< vector
< Point
>> contours
;
vector
< vector
< Point
>> Realcontours
;
vector
< Vec4i
> hierarchy
;
findContours ( fgMask
, contours
, hierarchy
, CV_RETR_EXTERNAL
, CV_CHAIN_APPROX_NONE
, cvPoint ( 0 , 0 ) ) ; getSizeContours ( contours
) ;
sort ( contours
. begin ( ) , contours
. end ( ) , descSort
) ;
for ( int i
= 0 ; i
< contours
. size ( ) ; i
++ ) { if ( contourArea ( contours
[ i
] ) < contourArea ( contours
[ 0 ] ) / 5 ) break ; rct
= boundingRect ( contours
[ i
] ) ; Realcontours
. push_back ( contours
[ i
] ) ; } rectangle ( image
, rct
, SCALAR_GREEN
, 2 ) ;
使用的自定義函數(shù):
void getSizeContours ( vector
< vector
< Point
>> & contours
) { int cmin
= 100 ; int cmax
= 1000 ; vector
< vector
< Point
>> :: const_iterator itc
= contours
. begin ( ) ; while ( itc
!= contours
. end ( ) ) { if ( ( itc
- > size ( ) ) < cmin
|| ( itc
- > size ( ) ) > cmax
) { itc
= contours
. erase ( itc
) ; } else ++ itc
; } }
bool descSort ( vector
< Point
> p1
, vector
< Point
> p2
) { return contourArea ( p1
) > contourArea ( p2
) ; }
1.4建立檢測區(qū)域?qū)ζ渲械能囕v進(jìn)行計數(shù) 本模塊中需要建立一個檢測區(qū)域,市面上大多數(shù)車流量檢測使用的都是直線檢測,使用車輛過線對車輛進(jìn)行計數(shù),本系統(tǒng)沒有采用這樣的方法,而是使用一個檢測矩形框進(jìn)行檢測。使用opencv中矩形類的特殊操作輔助。 在屏幕下方畫出一個矩形框作為檢測區(qū)域,每一個進(jìn)入此區(qū)域的車輛對其進(jìn)行計數(shù),但這里涉及到一個輪廓跟蹤的問題。即一個已經(jīng)進(jìn)入框內(nèi)但并未出框的車輛多次計數(shù)的問題。本模塊使用一個標(biāo)志carFlag對該車輛是否已經(jīng)計數(shù)(即該車輛是否為新進(jìn)入檢測區(qū)的車輛)。輪廓跟蹤部分,本系統(tǒng)使用中心點(diǎn)進(jìn)行跟蹤,建立一個存放中心點(diǎn)的動態(tài)數(shù)組 centerPointArray,計算每一幀中出現(xiàn)的車輛的中心點(diǎn),將該中心點(diǎn)與數(shù)組中已存在的中心點(diǎn)使用歐幾里得距離計算公式算出其距離,若該距離大于設(shè)定的一個距離值則認(rèn)為該車輛為一輛新的車輛并將該中心點(diǎn)加入中心點(diǎn)數(shù)組。若小于設(shè)定的距離值則認(rèn)為其是該幀中的某一個車輛的下一個位置,并對所有計算出的距離進(jìn)行排序,距離差值最小的則認(rèn)為該中心點(diǎn)是數(shù)組中這一中心點(diǎn)車輛的下一個位置并將該中心值替換到數(shù)組中。本模塊的不足之處是設(shè)定的距離值需要通過多次統(tǒng)計檢測才能得出,且為人工計算出。 設(shè)定一個全局變量CarCount存儲車輛的數(shù)量,當(dāng)中心點(diǎn)距離小于距離設(shè)定值且carFlag值為True時才使CarCount自增一次。為了增加用戶體驗(yàn)感,車輛在計數(shù)范圍內(nèi)并被計數(shù)時使用紅色進(jìn)行畫框,其余時間使用綠色畫框。本模塊主要實(shí)現(xiàn)代碼如下所示。
vector
< Point
> centerPointArray
;
Rect
Testrct ( 0 , imageheigh
, image
. cols
, 100 ) ; rectangle ( image
, Testrct
, SCALAR_GREEN
, 2 ) ; for ( int i
= 0 ; i
< Realcontours
. size ( ) ; i
++ ) { Rect Realrct
= boundingRect ( Realcontours
[ i
] ) ; Point center
; center
= getCenterPoint ( boundingRect ( Realcontours
[ i
] ) ) ; if ( center
. y
<= imageheigh
+ 100 && center
. y
>= imageheigh
) { double distance
; bool carFlag
= false ; if ( centerPointArray
. size ( ) > 0 ) { for ( auto i
= centerPointArray
. begin ( ) ; i
< centerPointArray
. end ( ) ; i
++ ) { distance
= sqrt ( pow ( ( center
. x
- i
- > x
) , 2 ) + pow ( ( center
. y
- i
- > y
) , 2 ) ) ; if ( distance
<= 30 ) { * i
= center
; carFlag
= true ; } } } if ( carFlag
== false ) { centerPointArray
. push_back ( center
) ; CarCount
++ ; } rectangle ( image
, Realrct
, SCALAR_RED
, 2 ) ; } else { rectangle ( image
, Realrct
, SCALAR_GREEN
, 2 ) ; } }
使用的自定義函數(shù):
Point
getCenterPoint ( Rect rect
) { Point cpt
; cpt
. x
= rect
. x
+ cvRound ( rect
. width
/ 2.0 ) ; cpt
. y
= rect
. y
+ cvRound ( rect
. height
/ 2.0 ) ; return cpt
; }
1.5尾處理 車流量計數(shù)完畢需要對用戶展示一些處理過程的處理結(jié)果如原視頻、視頻背景、視頻運(yùn)動目標(biāo)、運(yùn)動目標(biāo)二值圖、視頻處理結(jié)果并將計數(shù)展示在視頻處理結(jié)果的右上角。右上角顯示計數(shù)結(jié)果部分應(yīng)能根據(jù)視頻的大小進(jìn)行字體大小的變換,在力所能及的部分增加用戶體驗(yàn)感。本模塊主要實(shí)現(xiàn)代碼如下所示。
int intFontFace
= CV_FONT_HERSHEY_SIMPLEX
; double dblFontScale
= ( image
. rows
* image
. cols
) / 300000.0 ; int intFontThickness
= ( int ) std
:: round ( dblFontScale
* 3.0 ) ; Size textSize
= cv
:: getTextSize ( std
:: to_string ( CarCount
) , intFontFace
, dblFontScale
, intFontThickness
, 0 ) ; crossingLine
. x
= image
. cols
- 1 - ( int ) ( ( double ) textSize
. width
* 1.25 ) ; crossingLine
. y
= ( int ) ( ( double ) textSize
. height
* 1.25 ) ; putText ( image
, to_string ( CarCount
) , crossingLine
, intFontFace
, dblFontScale
, SCALAR_GREEN
, intFontThickness
) ; imshow ( "【視頻背景】" , backGround
) ; imshow ( "【視頻運(yùn)動目標(biāo)】" , foreGround
) ; imshow ( "【運(yùn)動目標(biāo)二值圖】" , fgMask
) ;
imshow ( "【視頻】" , image
) ;
2.系統(tǒng)運(yùn)行流程圖 根據(jù)系統(tǒng)模塊劃分,可得出本系統(tǒng)的流程圖如圖所示。 系統(tǒng)運(yùn)行流程圖: 實(shí)現(xiàn)效果: 由于本系統(tǒng)是對視頻進(jìn)行處理,故不能展示所有結(jié)果,在此展示本系統(tǒng)處理過程中某一幀的運(yùn)行結(jié)果,并給出最終的統(tǒng)計結(jié)果,處理視頻來源與網(wǎng)站。 讀取原視頻如圖所示。 原視頻讀取: 混合高斯背景建模背景分離如圖所示,由于運(yùn)行電腦的配置等原因,背景分離出現(xiàn)一些重影,這也是混合高斯背景建模的不足之處,其對運(yùn)動緩慢的目標(biāo)無法很好的對其進(jìn)行提取。 背景分離:
混合高斯背景建模前景提取如圖所示。此處將背景像素值置為0,僅留下前景目標(biāo)。 前景提取: 圖像預(yù)處理結(jié)果如圖所示。此時已對前景進(jìn)行二值化、高斯濾波、膨脹、腐蝕等操作。 圖像預(yù)處理結(jié)果: 圖像計數(shù)過程如圖所示。進(jìn)入此區(qū)域并被計數(shù)的車輛的輪廓最小正外接矩形被畫出。 圖像計數(shù): 圖像計數(shù)結(jié)果如圖所示。計數(shù)結(jié)果為52,針對該視頻,此計數(shù)結(jié)果正確。 圖像計數(shù)結(jié)果: 最終給出車流量統(tǒng)計的實(shí)驗(yàn)源碼:
#include "stdafx.h"
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/video/background_segm.hpp>
using namespace cv
;
using namespace std
;
void getSizeContours ( vector
< vector
< Point
>> & contours
) ;
bool descSort ( vector
< Point
> p1
, vector
< Point
> p2
) ;
Point
getCenterPoint ( Rect rect
) ;
const Scalar SCALAR_GREEN
= Scalar ( 0.0 , 200.0 , 0.0 ) ;
const Scalar SCALAR_RED
= Scalar ( 0.0 , 0.0 , 255.0 ) ;
int CarCount
= 0 ;
int main ( )
{ VideoCapture capture
; Mat frame
, image
, foreGround
, backGround
, fgMask
, findcontours
; Rect rct
; vector
< vector
< Point
>> contours
; vector
< Vec4i
> hierarchy
; vector
< Point
> centerPointArray
; Point crossingLine
; Ptr
< BackgroundSubtractorMOG2
> pBgmodel
= createBackgroundSubtractorMOG2 ( ) ; pBgmodel
- > setVarThreshold ( 500 ) ; capture
. open ( "G:\\opencvdemo\\Guass\\CarsDrivingUnderBridge.mp4" ) ; if ( ! capture
. isOpened ( ) ) { cout
<< "open videp eror!" << endl
; } while ( true ) { vector
< vector
< Point
>> Realcontours
; Mat element
= getStructuringElement ( MORPH_RECT
, Size ( 13 , 13 ) ) ; capture
>> frame
; if ( frame
. empty ( ) ) break ; resize ( frame
, image
, Size ( frame
. cols
/ 2 , frame
. rows
/ 2 ) , INTER_LINEAR
) ; if ( foreGround
. empty ( ) ) foreGround
. create ( image
. size ( ) , image
. type ( ) ) ; GaussianBlur ( fgMask
, fgMask
, Size ( 5 , 5 ) , 0 ) ; dilate ( fgMask
, fgMask
, element
) ; erode ( fgMask
, fgMask
, element
) ; threshold ( fgMask
, fgMask
, 10 , 255 , THRESH_BINARY
) ; foreGround
= Scalar
:: all ( 0 ) ; image
. copyTo ( foreGround
, fgMask
) ; pBgmodel
- > getBackgroundImage ( backGround
) ; int imageheigh
= ( int ) std
:: round ( ( double ) image
. rows
* 0.5 ) ; findContours ( fgMask
, contours
, hierarchy
, CV_RETR_EXTERNAL
, CV_CHAIN_APPROX_NONE
, cvPoint ( 0 , 0 ) ) ; getSizeContours ( contours
) ; imshow ( "【原視頻】" , image
) ; sort ( contours
. begin ( ) , contours
. end ( ) , descSort
) ; for ( int i
= 0 ; i
< contours
. size ( ) ; i
++ ) { if ( contourArea ( contours
[ i
] ) < contourArea ( contours
[ 0 ] ) / 5 ) break ; rct
= boundingRect ( contours
[ i
] ) ; Realcontours
. push_back ( contours
[ i
] ) ; } rectangle ( image
, rct
, SCALAR_GREEN
, 2 ) ; Rect
Testrct ( 0 , imageheigh
, image
. cols
, 100 ) ; rectangle ( image
, Testrct
, SCALAR_GREEN
, 2 ) ; for ( int i
= 0 ; i
< Realcontours
. size ( ) ; i
++ ) { Rect Realrct
= boundingRect ( Realcontours
[ i
] ) ; Point center
; center
= getCenterPoint ( boundingRect ( Realcontours
[ i
] ) ) ; if ( center
. y
<= imageheigh
+ 100 && center
. y
>= imageheigh
) { double distance
; bool carFlag
= false ; if ( centerPointArray
. size ( ) > 0 ) { for ( auto i
= centerPointArray
. begin ( ) ; i
< centerPointArray
. end ( ) ; i
++ ) { distance
= sqrt ( pow ( ( center
. x
- i
- > x
) , 2 ) + pow ( ( center
. y
- i
- > y
) , 2 ) ) ; if ( distance
<= 30 ) { * i
= center
; carFlag
= true ; } } } if ( carFlag
== false ) { centerPointArray
. push_back ( center
) ; CarCount
++ ; } rectangle ( image
, Realrct
, SCALAR_RED
, 2 ) ; } else { rectangle ( image
, Realrct
, SCALAR_GREEN
, 2 ) ; } } int intFontFace
= CV_FONT_HERSHEY_SIMPLEX
; double dblFontScale
= ( image
. rows
* image
. cols
) / 300000.0 ; int intFontThickness
= ( int ) std
:: round ( dblFontScale
* 3.0 ) ; Size textSize
= cv
:: getTextSize ( std
:: to_string ( CarCount
) , intFontFace
, dblFontScale
, intFontThickness
, 0 ) ; crossingLine
. x
= image
. cols
- 1 - ( int ) ( ( double ) textSize
. width
* 1.25 ) ; crossingLine
. y
= ( int ) ( ( double ) textSize
. height
* 1.25 ) ; putText ( image
, to_string ( CarCount
) , crossingLine
, intFontFace
, dblFontScale
, SCALAR_GREEN
, intFontThickness
) ; imshow ( "【視頻背景】" , backGround
) ; imshow ( "【視頻運(yùn)動目標(biāo)】" , foreGround
) ; imshow ( "【運(yùn)動目標(biāo)二值圖】" , fgMask
) ; imshow ( "【視頻】" , image
) ; char key
= waitKey ( 100 ) ; if ( key
== 27 ) break ; }
system ( "pause" ) ; return 0 ;
}
void getSizeContours ( vector
< vector
< Point
>> & contours
)
{ int cmin
= 100 ; int cmax
= 1000 ; vector
< vector
< Point
>> :: const_iterator itc
= contours
. begin ( ) ; while ( itc
!= contours
. end ( ) ) { if ( ( itc
- > size ( ) ) < cmin
|| ( itc
- > size ( ) ) > cmax
) { itc
= contours
. erase ( itc
) ; } else ++ itc
; }
}
bool descSort ( vector
< Point
> p1
, vector
< Point
> p2
) { return contourArea ( p1
) > contourArea ( p2
) ;
} Point
getCenterPoint ( Rect rect
)
{ Point cpt
; cpt
. x
= rect
. x
+ cvRound ( rect
. width
/ 2.0 ) ; cpt
. y
= rect
. y
+ cvRound ( rect
. height
/ 2.0 ) ; return cpt
;
}
數(shù)字圖像處理合集告一段落,希望這一合集對你學(xué)習(xí)數(shù)字圖像處理帶來幫助。 合集入口
最后附上資源下載鏈接,均為本合集的資源 資源下載
測試視頻觀看入口
總結(jié)
以上是生活随笔 為你收集整理的数字图像处理合集终章——车流量统计(后附源码) 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔 推薦給好友。