Python基于OpenCV的視頻圖像處理詳解
初識OpenCV
OpenCV是一個開源的,跨平臺的計算機視覺庫,它采用優(yōu)化的C/C++代碼編寫,能夠充分利用多核處理器的優(yōu)勢,提供了Python,Ruby,MATPLOAB以及其他高級語言接口。
OpenCV的設(shè)計目標(biāo)是執(zhí)行速度盡量快,主要面向?qū)崟r應(yīng)用,是視頻信號處理的主要工具之一,它封裝了豐富的視頻處理相關(guān)的工具包。視頻信號是重要的視覺信息來源,其中包含的信息要遠(yuǎn)大于圖像,對視頻的分析也是計算機視覺領(lǐng)域的重要研究方向之一。視頻在本質(zhì)上由連鎖的多幀圖像構(gòu)成,因此,視頻信號處理最終仍歸屬圖像處理范疇。但在視頻中,其時間維度也包含了許多有用的信息。
視頻讀寫處理
視頻一般有兩種來源,一種是從本地磁盤加載,另一種是從攝像頭等設(shè)備實時獲取。上述兩種視頻獲取方式分別對應(yīng)著OpenCV2的兩個函數(shù)CaptureFromFile()和CaptureFromCAM().在OpenCV3中則統(tǒng)一為一個用于處理視頻源載入的函數(shù)VideoCapture()。
下示例代碼展示了如何從本地載入一個視頻文件,然后將其轉(zhuǎn)化為灰度圖像連續(xù)幀并播放:
import cv2 cap=cv2.VideoCapture('D:\Image\Funny.mp4') while(cap.isOpened()): ret,frame=cap.read() #循環(huán)播放視頻中每幀圖像 gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) #將原幀圖像轉(zhuǎn)化為灰度圖像 cv2.imshow('視頻捕捉',gray) #顯示處理后的圖像 if cv2.waitKey(1) & 0xFF==ord('q'): #按q鍵退出程序 break cap.release() #處理完成,釋放視頻捕捉 cv2.destroyAllWindows() #關(guān)閉窗口釋放資源
由于視頻過長,原視頻放在了主頁。
下列代碼則展示了如何從攝像頭獲取視頻:
#攝像頭獲取視頻 import cv2 cap=cv2.VideoCapture(1) #打開攝像頭獲取視頻 while(True): ret,frame=cap.read() #循環(huán)播放視頻中每幀圖像 gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) #顯示結(jié)果 cv2.imshow('攝像頭拍照',gray) if cv2.waitKey(1) & 0xFF==ord('q'): break cap.release()#處理完成,釋放視頻捕捉 cv2.destroyAllWindows()
下列代碼展示了將攝像頭獲得的視頻寫入存儲文件的過程。其中,VideoWriter_fourcc類用于定義視頻文件的寫入格式,其參數(shù)有多種格式可選,如下:
- ①.VideoWriter_fourcc('T','4','2','0'),該選項為一個未壓縮的YUV顏色編碼類型,是4:2:0色度子采樣。改編碼有著很好的兼容性,但是會產(chǎn)生較大的文件,文件拓展名為:“.avi”
- ②.VideoWriter_fourcc('P','T','M','1'):該選項為“MPEG-1”編碼類型,文件拓展名為“.mpeg”
- ③.VideoWrite_fourcc('X','V','T','D'):該選項是“MPEG-4”編碼類型,如果希望得到的視頻大小為平均值,推薦使用該選項,文件拓展名為:“.mp4”
- ④.VideWriter_fourcc('T','H','E','O'):該選項是“Ogg Vorbis”編碼類型,文件拓展名為“.ogv”。
- ⑤.VideWriter_foucc('F','L','V','T'):該選項是Flash編碼類型,文件拓展名是“.flv”。
定義好輸出視頻的格式后,用VideWriter類進(jìn)行寫入的時候,需要指定幀速率和幀大小,因此需要從另一個視頻文件復(fù)制視頻幀,這些屬性可以通過VideoCapture類的get()函數(shù)得到。
通過OpenCV獲取視頻并寫入文件的示例代碼:
#通過OpenCv獲取視頻并寫入 import cv2 cap2=cv2.VideoCapture('D:\Image\Funny.mp4') cap=cv2.VideoCapture(1)#打開攝像頭并獲取視頻 #對視頻幀率fps進(jìn)行賦值 fps=24 #過去視頻幀的大小 size=(int(cap2.get(cv2.CAP_PROP_FRAME_WIDTH)),int(cap2.get(cv2.CAP_PROP_FRAME_HEIGHT))) fourcc=cv2.VideoWriter_fourcc('X','V','I','D') #MP4文件格式 out=cv2.VideoWriter('D:\Image\output.avi',fourcc,fps,size)#定義視頻文件寫入對象 while(cap.isOpened()): ret,frame=cap.read() if ret==True: ''' 獲取幀圖像并翻轉(zhuǎn),cv2.flip()的第二個參數(shù)表示翻轉(zhuǎn)方式:0代表垂直翻轉(zhuǎn),1代表水平翻轉(zhuǎn),-1代表水平垂直翻轉(zhuǎn) ''' frame=cv2.flip(frame,1) out.write(frame)#將翻轉(zhuǎn)后的幀圖像寫入文件 cv2.imshow('幀圖像處理',frame) if cv2.waitKey(1) & 0xFF==ord('q'): break else: break cap.release() out.release() cv2.destroyAllWindows()
運動軌跡標(biāo)記
運動捕捉(Motion Capture)技術(shù)可對運動物體或其特征點在三維空間中的運動軌跡進(jìn)行實時,精確,定量地連續(xù)測量,跟蹤和記錄。運動軌跡則是從物體開始位置運動結(jié)束位置所經(jīng)過的路線組成的空間特征。基于計算機視覺圖像處理技術(shù)的運動捕捉方案在動畫及游戲制作,仿真訓(xùn)練等領(lǐng)域有著廣泛的應(yīng)用。
光流(Optical Flow)是圖像亮度的運動信息描述,是空間運動物體在觀測成像面上的像素運動的瞬時速度,也是對視頻中運動對象軌跡進(jìn)行標(biāo)記的一種常用方法。光流由場景中前景目標(biāo)本身的運動,攝像機的運動,或者兩者的共同運動產(chǎn)生。當(dāng)人通過眼睛觀察運動物體時,物體的景象在人眼的視網(wǎng)膜上形成一系列連續(xù)變化的圖像,這一系列連續(xù)變化的信息不斷“流過”視網(wǎng)膜,猶如光在平面中的流動,故稱之為“光流”。光流的概念在20世紀(jì)40年代首次被提出,該方法利用圖像序列中的像素在時域上的變化,相鄰幀之間的相關(guān)性來找到前一幀與當(dāng)前幀之間存在的對應(yīng)關(guān)系,從而計算出相鄰幀之間物體的運動信息。光流表達(dá)了圖像的變化,由于它包含了目標(biāo)運動的信息,因此可被觀察者用于確定目標(biāo)的運動情況。
在真實的三維空間中,描述物體運動狀態(tài)的物理概念是運動場(Motion Field)。三維空間中的每一個點,經(jīng)過某段時間的運動之后會到達(dá)一個新的位置,而這個位移過程可以用運動場來描述,運動場的實質(zhì)上就是物體在三維真實世界的運動,而時光流暢(Optical Flow Field)是指圖像中所有像素點構(gòu)成的一種二維瞬時速度場它是一個二維矢量場。
三維空間運動到二維平面的投影所形成的光流,當(dāng)描述部分像素時,稱為稀疏光流,當(dāng)描述全部像素時,則稱為稠密光流。
OpenCV實現(xiàn)了不少光流算法,其中,Lucas-Kanade(L-K)是一種廣泛使用的光流估計差分算法,它由布魯斯·D.盧卡斯(Bruce D.Lucas)和金出武雄(Takeo Kanade)提出,L-K算法假設(shè)光流在像素點的領(lǐng)域是一個常數(shù),然后使用最小二乘法對領(lǐng)域中的所有像素點求解基本的光流方程。通過結(jié)合幾個鄰近像素點的信息,L-K算法通常能夠消除光流方程中的多義性。而且與逐點計算的方法比,L-K算法對圖像噪聲不敏感。對于L-K算法,低速度,亮度不變以及區(qū)域一致性都是較強的假設(shè),但是這些條件并不容易滿足。當(dāng)運動速度過快時,這種假設(shè)不成立,使得最終求出的光流值有較大的誤差。吉思——伊卡斯·布格(Jean—Yves Bouguent)提出了一種基于金字塔分層的算法,針對仿射變換的改進(jìn)L-K算法,該算法最明顯的優(yōu)勢在于,對于每一層的光流都會保持很小,但最終計算的光流可進(jìn)行累積,從而可有效地跟蹤特征點。L-K算法現(xiàn)已逐漸發(fā)展成為計算圖像稀疏光流的重要方法。通過金字塔L-K算法計算稀疏光流的示例代碼:
#L-K算法 import numpy as np import cv2 #設(shè)置L-K算法參數(shù) lk_params=dict(winSize=(15,15), #搜索窗口的大小 maxLevel=2,#最大金字塔層數(shù) #迭代算法終止條件(迭代次數(shù)或迭代閾值) criteria=(cv2.TERM_CRITERIA_EPS|cv2.TERM_CRITERIA_COUNT,10,0.03)) feature_params=dict(maxCorners=500, #設(shè)置最多返回的關(guān)鍵點(角點)數(shù) qualityLevel=0.3, #角點閾值:反映一個像素點對強才算一個角點 minDistance=7, #角點之間的最小像素點(歐氏距離) blockSize=7) #計算一個像素點是否為關(guān)鍵點時所取區(qū)域的大小 class App: #構(gòu)造方法,初始化一些參數(shù)和視頻路徑 def __init__(self,video_src): self.track_len=10 #光流標(biāo)記長度 self.detect_interval=5#幀檢測間隔 #跟蹤點幾何初始化,self.tracks中值的格式時:(前一幀角點) self.tracks=[] self.cam=cv2.VideoCapture(video_src) #視頻源 self.frame_idx=0 #幀序列號初始化 def run(self): #運行光流方法 while True: ret,frame=self.cam.read() if ret ==True: frame_gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) vis=frame.copy() #檢測到角點后光流跟蹤 if len(self.tracks)>0: img0,img1=self.prev_gray,frame_gray p0=np.float32([tr[-1] for tr in self.tracks]).reshape(-1,1,2) #將前一幀的角點和當(dāng)前幀的圖像作為輸入的角點在當(dāng)前幀的位置 p1,st,err=cv2.calcOpticalFlowPyrLK(img0,img1,p0,None,**lk_params) #將當(dāng)前幀跟蹤到的角點以及圖像和前一幀的圖像作為輸入得到前一幀的角點檢測 p0r,st,err=cv2.calcOpticalFlowPyrLK(img1,img0,p1,None,**lk_params) #得到角點回溯與前一幀實際角點的位置變化系 d=abs(p0-p0r).reshape(-1,2).max(-1) good=d<1 #判斷d的值是否小于1 new_tracks=[] #將跟蹤的點列為成功跟蹤點 for tr,(x,y),good_flag in zip(self.tracks,p1.reshape(-1,2),good): if not good_flag: continue tr.append((x,y)) if (len(tr)>self.track_len): del tr[0] new_tracks.append(tr) cv2.circle(vis,(x,y),2,(0,255,0),-1) self.tracks=new_tracks #以前一幀角點為初始點,以當(dāng)前幀跟蹤到的點為終點劃線,開始軌跡標(biāo)記 cv2.polylines(vis,[np.int32(tr) for tr in self.tracks],False,(0,255,0)) #每五幀檢測一次 if (self.frame_idx % self.detect_interval==0): mask=np.zeros_like(frame_gray)#初始化和視頻尺寸大小相同的圖像 mask[:]=255 #計算全部圖像的角點 for x,y in [np.int32(tr[-1]) for tr in self.tracks]: cv2.circle(mask,(x,y),5,0,-1) #利用goodFeaturesToTrack進(jìn)行角點檢測 p=cv2.goodFeaturesToTrack(frame_gray,mask=mask,**feature_params) if p is not None: for x,y in np.float32(p).reshape(-1,2): self.tracks.append([(x,y)]) #將檢測到的角點放在預(yù)跟蹤序列 self.frame_idx+=1 self.prev_gray=frame_gray cv2.imshow('Lucas-Kanade光流算法',vis) ch=0xFF & cv2.waitKey(1) if ch==ord('q'):#按q鍵退出 break def main(): import sys video_src='D:\Image\haha.mp4' App(video_src).run() #運行主程序 cv2.destroyAllWindows() if __name__=='__main__': main()
由于視頻是本地文件,不能展示出來,這里就直接呈現(xiàn)代碼;通過運行結(jié)果程序可知,稠密光流算法是一種對圖像進(jìn)行逐點匹配的圖像配準(zhǔn)算法。不同于稀疏光流算法只針對圖像中若干個特征點,稠密光流算法計算圖像上所有的點的偏移量,從而形成一個稠密的光流場。通過這個稠密的光流場,可以進(jìn)行像素級別的圖像配準(zhǔn),因此,其配準(zhǔn)后的效果也明顯優(yōu)于稀疏光流算法配準(zhǔn)的效果。但是其副作用也非常明顯,由于要計算每個點的偏移量,其計算量也明顯大于稀疏光流算法。
CalOpticalFlowFraneback()算法
在OpenCV中,CalOpticalFlowFraneback()函數(shù)利用Gunnar Farneback算法進(jìn)行全局性稠密光流算法,其參數(shù)說明如下:
- (1)prevImg:輸入的8bit單通道前一幀圖像。
- (2)nextImg:輸入的8bit單通道當(dāng)前幀圖像。
- (3)pyr_scale:金字塔參數(shù),0.5為經(jīng)典參數(shù),每一層是下一層尺度的一般。
- (4)levels:金字塔的層數(shù)。
- (5)winsize:窗口大小。
- (6)iterations:迭代次數(shù)。
- (7)poly_n:像素領(lǐng)域的大小,如果值比較大則表示圖像整體比較平滑。
- (8)poly_sigma:高斯標(biāo)準(zhǔn)差。
- (9)flags:可以為這些組合——OPTFLOW_USE_INITIAL_FLOW,OPTFLOW_FARNEBACK_GAUSSIAN,返回值為每一個像素點的位移。
基于稠密光流算法的運動軌跡標(biāo)記的代碼:
#基于稠密光流算法軌跡標(biāo)記 from numpy import * import cv2 #定義光流跟蹤標(biāo)記函數(shù) def draw_flow(im,flow,step=16): h,w=im.shape[:2] y,x=mgrid[step/2:h:step,step/2:w:step].reshape(2,-1) fx,fy=flow[y.astype(int),x.astype(int)].T #創(chuàng)建標(biāo)記線條端點 lines=vstack([x,y,x+fx,y+fy]).T.reshape(-1,2,2) lines=int32(lines) #創(chuàng)建圖像和進(jìn)行線條標(biāo)記 vis=cv2.cvtColor(im,cv2.COLOR_GRAY2BGR) for (x1,y1),(x2,y2) in lines: cv2.line(vis,(x1,y1),(x2,y2),(0,255,0),1) cv2.circle(vis,(x1,y1),1,(0,255,0),-1)#畫圓 return vis cap=cv2.VideoCapture(1)#開啟攝像頭 #讀取視頻幀 ret,im=cap.read() #轉(zhuǎn)化為灰度圖像 prev_gray=cv2.cvtColor(im,cv2.COLOR_BGR2GRAY) while True: #讀取視頻幀 ret,im=cap.read() #轉(zhuǎn)換為灰度圖像 gray=cv2.cvtColor(im,cv2.COLOR_BGR2GRAY) #光流計算 flow=cv2.calcOpticalFlowFarneback(prev_gray,gray,None,0.5,3,15,3,5,1.2,0) prev_gray=gray #繪制光流軌跡 cv2.imshow('稠密光流算法',draw_flow(gray,flow)) if cv2.waitKey(1)&0xFF==ord('q'): #按q鍵結(jié)束 break im.release() cv2.destroyAllWindows()
運動檢測
運動檢測(Motion Detection)是計算機視覺和視頻處理中常用的預(yù)處理步驟,是指從視頻中識別發(fā)生變化或移動的區(qū)域。運動檢測最常見的應(yīng)用場景是運動目標(biāo)檢測,也就是對攝像頭記錄的視頻移動目標(biāo)進(jìn)行定位到過程,有著非常廣泛的應(yīng)用。實時目標(biāo)檢測是許多計算機視覺的重要任務(wù),例如安全監(jiān)控,增強現(xiàn)實應(yīng)用,基于對象的視頻壓縮,基于感知的用戶界面及輔助駕駛等。
運動目標(biāo)檢測算法根據(jù)目標(biāo)與攝像機之間的關(guān)系可以分為靜態(tài)背景下的運動目標(biāo)檢測和動態(tài)背景下的運動目標(biāo)檢測。靜態(tài)背景下的運動目標(biāo)檢測,就是從序列圖像中將實際的的變化區(qū)域與背景分開。在背景靜止的大前提下進(jìn)行運動目標(biāo)檢測的方法有很多,大多側(cè)重于背景擾動小噪聲的消除,如背景差分法(Background Difference Nethod ,BDM),幀間差分法(Inter-Frame Difference Method,IFDM),光流法,高斯混合模型(Gaussion Mixed Model,GMM),碼本(Codebook),自組織背景減除(Self-Organizing Background Subtraction,SOBS),視覺背景提?。╒isual Background Extractor,VIBE)以及這些方法的變種,如三幀差分法,五幀差分法,或者這些方法的結(jié)合。動態(tài)背景下的運動目標(biāo)檢測,相對于靜態(tài)背景而言,算法的思路有所不同,一般更側(cè)重于匹配,需要進(jìn)行圖像的全局運動估計與補償,因為在目標(biāo)和背景同時運動的情況下,無法簡單的根據(jù)運動來判斷。動態(tài)背景下的運動目標(biāo)檢測算法也有很多,例如塊匹配(Block Matching,BM)和光流估計(Optical Flow Estimation,OFE)等。
幀間差分法是一種常用的運動目標(biāo)檢測算法,其基本原理是觀測視頻圖像相鄰幀之間的細(xì)微變化來判斷物體是否在運動。攝像機采集的視頻序列具有連續(xù)性,如果場景內(nèi)沒有運動目標(biāo),則連續(xù)幀的變化很?。蝗绻嬖谶\動目標(biāo),由于場景中的目標(biāo)的運動,目標(biāo)的影像在不同圖像幀之間的位置會不同,從而導(dǎo)致連續(xù)幀之間有顯著變化。該算法通過對時間上連續(xù)的兩幀或三幀圖像進(jìn)行差分運算,對不同幀對應(yīng)的的像素點灰度值想減,來判斷灰度值的絕對值,當(dāng)絕對值超過一定閾值時,則可判斷其為運動目標(biāo),從而實現(xiàn)目標(biāo)的檢測功能。
(二幀差分原理圖)
(三幀差分原理圖)
在OpebCV中用absdiff()函數(shù)實現(xiàn)。
三幀差分法的運算過程如上圖所示。記視頻序列中第n+1幀、第n幀和第n-1幀的圖像分別為fn+1、fn和fn-1,三幀對應(yīng)像素點的灰度值記為fn+1(x,y)、fn(x,y)和fn-1(x,y),分別得到差分圖像Dn和Dn+1,對差分圖像Dn和Dn+1按照下式進(jìn)行計算,得到圖像Dn',然后再進(jìn)行閾值處理、連通性分析,最終提取出運動目標(biāo)。
在幀間差分法中,閾值T的選擇非常重要。如果閾值T選取的值太小,則無法抑制差分圖像中的噪聲;如果閾值T選取的值太大,又有可能掩蓋差分圖像中目標(biāo)的部分信息;而且固定的閾值T無法適應(yīng)場景中光線變化等情況。為此,有人提出了在判決條件中加入對整體光照敏感的添加項的方法,將判決條件修改為:
其中,NA為待檢測區(qū)域中像素的總數(shù)目,λ為光照的抑制系數(shù),A可設(shè)為整幀圖像。紅框中的添加項表達(dá)了整幀圖像中光照的變化情況。如果場景中的光照變化較小,則該項的值趨向于零;如果場景中的光照變化明顯,則該項的值明顯增大,導(dǎo)致上式右側(cè)判決條件自適應(yīng)地增大,最終的判決結(jié)果為沒有運動目標(biāo),這樣就有效地抑制了光線變化對運動目標(biāo)檢測結(jié)果的影響。
下圖中左圖是采用幀間差分法進(jìn)行運動目標(biāo)檢測的實驗結(jié)果,(b)圖是采用兩幀差分法的檢測結(jié)果,(c)圖是采用三幀差分法的檢測結(jié)果。視頻序列中的目標(biāo)運動較快,在這種情況下,運動目標(biāo)在不同圖像幀內(nèi)的位置明顯不同,采用兩幀差分法檢測出的目標(biāo)會出現(xiàn)“重影”的現(xiàn)象,采用三幀差分法,可以檢測出較為完整的運動目標(biāo)。
綜上所述,幀間差分法的原理簡單,計算量小,能夠快速檢測出場景中的運動目標(biāo)。但由實驗結(jié)果可以看出,幀間差分法不能提取出對象的完整區(qū)域,只能提取出邊界。同時依賴于選擇的幀間時間間隔,對快速運動的物體,需要選擇較小的時間間隔,如果選擇不合適,當(dāng)物體在前后兩幀中沒有重疊時,會被檢測為兩個分開的物體,而對慢速運動的物體,應(yīng)該選擇較大的時
間差,如果時間選擇不適當(dāng),當(dāng)物體在前后兩幀中幾乎完全重疊時,則檢測不到物體。
因此幀間差分法通常不單獨用在目標(biāo)檢測中,往往與其它的檢測算法結(jié)合使用。常見的是結(jié)合背景差分法和幀間差分法的優(yōu)缺點,使它們優(yōu)勢互補,從而克服相互的弱點,提高運動檢測的效果。例如在實際的場景中,即便是室內(nèi)環(huán)境,也存在光線等各種變化造成的干擾,或者人為造成的開燈等光線的強烈變化。所以在背景差分法的實現(xiàn)中,它的固定背景不能一成不變。如果不進(jìn)行重新初始化,錯誤的檢測結(jié)果將隨時間不斷累計,造成惡性循環(huán),從而造成監(jiān)控失效。
利用幀間差分法示例代碼:
#利用幀間差分法進(jìn)行目標(biāo)檢測 import time import cv2 import argparse #用于解析參數(shù) import datetime #用于時間和日期相關(guān)處理 import imutils #用于圖像相關(guān)處理 import argparse #創(chuàng)建參數(shù)解析器并解析參數(shù) ap=argparse.ArgumentParser() ap.add_argument('-v','--video',help='path to the video file') ap.add_argument('-a','--min-area',type=int,default=500,help='minimum area size') args=vars(ap.parse_args(args=[])) #這里有兩種表達(dá)式,JUPYTER只能使用這種,表示默認(rèn)參數(shù),pycharme可以用另一種 #如果video參數(shù)為None,那么我們從攝像頭讀取數(shù)據(jù) if args.get('video',None) is None: camera=cv2.VideoCapture(1) if camera is None: print("請檢查攝像頭連接") exit() time.sleep(0.25) else: #讀取一個視頻 camera=cv2.VideoCapture('D:\Image\haha.mp4') ''' 初始化視頻流的第一幀。一般情況下,視頻第一幀不會包含運動而僅僅是背景 ''' firstFrame=None #遍歷視頻的每一幀 while True: ''' 獲取當(dāng)前視幀并初始化顯示文本,調(diào)用camera.read()將返回一個二元組,元組的第一個值是True和False,表明是否成功從緩沖中讀取幀圖像,元組的第二個值就是獲取的當(dāng)前幀圖像的值。 ''' (grabbed,frame)=camera.read() text='No Motion Detected' #如果不能獲取到幀,說明到了視頻的結(jié)尾 if not grabbed: break frame=imutils.resize(frame,width=500) #調(diào)整幀圖像大小 gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) #轉(zhuǎn)換為灰度圖像 gray=cv2.GaussianBlur(gray,(21,21),0)#高斯模糊處理 #如果第一幀是None,對其進(jìn)行初始化 if firstFrame is None: firstFrame=gray continue #將當(dāng)前幀和第一幀圖像對應(yīng)的像素點的灰度值相減并求絕對值來計算兩幀的不同 frameDelta=cv2.absdiff(firstFrame,gray) #對差分圖像進(jìn)行閾值處理來顯示圖像中像素點的灰度值有所變化的區(qū)域 thresh=cv2.threshold(frameDelta,25,255,cv2.THRESH_BINARY)[1] #擴(kuò)展閾值圖像填充空洞,然后找到閾值圖像中的輪廓 thresh=cv2.dilate(thresh,None,iterations=2) ''' cv2.findContours()函數(shù)返回3個值,第一個是所處理的圖像,第二個是輪廓,第三個是每個輪廓對應(yīng)的屬性 ''' contours,hierrarchy=cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) #老版返回3個接受參數(shù),新版返回兩個; #遍歷輪廓 for c in contours: #過濾小的,不相關(guān)的輪廓,如果輪廓面積大于min_area,則在前景和移動區(qū)域畫邊框 if cv2.contourArea(c)<args['min_area']: continue #計算輪廓的邊界框,在當(dāng)前幀中畫出該框并更新相應(yīng)文本 (x,y,w,h)=cv2.boundingRect(c) cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2) text='Motion Detected' #在當(dāng)前幀上寫文本及時間戳 cv2.putText(frame,'Room Status:{}'.format(text),(10,20),cv2.FONT_HERSHEY_SIMPLEX,0.5,(0,0,255),2) cv2.putText(frame,datetime.datetime.now().strftime('%A %%d %B %Y %I:%M:%S%p'),(10,frame.shape[0]-10),cv2.FONT_HERSHEY_SIMPLEX,0.35,(0,0,255),1) #顯示當(dāng)前幀并記錄用戶是否按了鍵 cv2.imshow('視頻監(jiān)控演示',frame) cv2.imshow('閾值輪廓圖像',thresh) cv2.imshow('幀差分圖像',frameDelta) key=cv2.waitKey(1) if key==ord('q'): break #跳出循環(huán) #釋放攝像機資源并關(guān)閉打開的窗口 camera.release() cv2.destroyAllWindows()
由于視頻不太容易上傳,這里就使用截屏作為結(jié)果展示。
運動方向檢測
在某些應(yīng)用場合,檢測出運動的物體之后,我們還需知道物體的運動方向,判斷其是否進(jìn)入或離開檢測區(qū)域。對于運動方向的檢測,一般通過檢測圖像的光流場估算圖像的運動場來實現(xiàn)。根據(jù)傳統(tǒng)估算方法,需要對圖像中的每一個像素進(jìn)行計算,算出圖像每一點的運動場,然后得到整幅圖像的運動場。
檢測物體的運動方向,理論上也可以使在幀間差分法的基礎(chǔ)上通過計算幀圖像輪廓中點的變化來實現(xiàn),但因每次檢測出的輪廓數(shù)量不穩(wěn)定,所以該方式會使得誤差不可控。不過,OpenCv中的goodFeaturesToTrack()函數(shù)可用于獲取圖像最大特征只的角點。我們可以此為契機重新設(shè)計物體運動方向檢測算法,步驟如下:
- (1) 對相鄰兩幀圖像所有像素點通過absdiff()函數(shù)進(jìn)行差分運算得到差分圖像。
- (2) 將差分圖像轉(zhuǎn)化成灰度圖像并進(jìn)行二值化處理。
- (3) 利用goodFeaturesToTrack()函數(shù)獲得最大特征值的角點。
- (4) 計算角點的平均特征值,寫入隊列。
- (5) 維護(hù)一個長度為10的隊列,隊列滿時計算隊列中元素的增減情況,并以此來確定目標(biāo)的運動方向。
利用上述改進(jìn)算法進(jìn)行物體運動方向檢測的示例代碼:
#運動方向檢測 import cv2 import numpy as np import queue #導(dǎo)入庫主要用于隊列處理 camera=cv2.VideoCapture(1) if camera is None: print('請檢查攝像頭鏈接') exit() width=int(camera.get(3)) height=int(camera.get(4)) #參數(shù)初始化 firstFrame=None lastDec=None firstThresh=None feature_params=dict(maxCorners=100, #設(shè)置最多返回的關(guān)鍵點數(shù) qualityLevel=0.3, #角點閾值:響應(yīng)最大值 minDistance=7, #角點之間最少像素點(歐氏距離) blockSize=7) #計算一個像素點是否為關(guān)鍵點時所取區(qū)域 #Lucas-Kanade 光流算法參數(shù)設(shè)置 lk_params=dict(winSize=(15,15),#搜索窗口的大小 maxLevel=2,#最大金字塔層數(shù) criteria=(cv2.TermCriteria_EPS|cv2.TERM_CRITERIA_COUNT,10,0.03)) color=np.random.randint(0,255,(100,3)) num=0 #隊列初始化 q_x=queue.Queue(maxsize=10) q_y=queue.Queue(maxsize=10) while True: #獲取視頻幀并轉(zhuǎn)化為灰度圖像 (grabbed,frame)=camera.read() gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) gray=cv2.GaussianBlur(gray,(21,21),0) if firstFrame is None: firstFrame=gray continue frameDelta=cv2.absdiff(firstFrame,gray) #對圖像進(jìn)行閾值二值化 thresh=cv2.threshold(frameDelta,25,255,cv2.THRESH_BINARY)[1] p0=cv2.goodFeaturesToTrack(thresh,mask=None,**feature_params) if p0 is not None: x_sum=0 y_sum=0 for i,old in enumerate(p0): x,y=old.ravel() x_sum+=x y_sum+=y x_avg=x_sum/len(p0) y_avg=y_sum/len(p0) if q_x.full(): qx_list=list(q_x.queue) key=0 diffx_sum=0 for item_x in qx_list: key+=1 if key<10: diff_x=item_x-qx_list[key] diffx_sum+=diff_x if diffx_sum<0 and x_avg<500:#表明隊列在增加 print('Left') cv2.putText(frame,'Left Motion Detected',(100,100),0,0.5,(0,0,255),2) else: print("Right") cv2.putText(frame,"Right Motion Detected",(300,100),0,0.5,(255,0,255),2) q_x.get() q_x.put(x_avg) cv2.putText(frame,str(x_avg),(300,100),0,0.5,(0,0,255),2) frame=cv2.circle(frame,(int(x_avg),int(y_avg)),5,color[i].tolist(),-1) cv2.imshow('運動方向檢測',frame) firstFrame=gray.copy() key=cv2.waitKey(1)&0xFF if key==ord('q'): break camera.release() cv2.destroyAllWindows()
以上就是Python基于OpenCV的視頻圖像處理詳解的詳細(xì)內(nèi)容,更多關(guān)于Python OpenCV視頻圖像處理的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
配置python連接oracle讀取excel數(shù)據(jù)寫入數(shù)據(jù)庫的操作流程
這篇文章主要介紹了配置python連接oracle,讀取excel數(shù)據(jù)寫入數(shù)據(jù)庫,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03python3+openCV 獲取圖片中文本區(qū)域的最小外接矩形實例
這篇文章主要介紹了python3+openCV 獲取圖片中文本區(qū)域的最小外接矩形實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-06-06基于Python實現(xiàn)一個簡單的學(xué)生管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了如何利用python實現(xiàn)簡單的學(xué)生信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-12-12Python Sqlite3以字典形式返回查詢結(jié)果的實現(xiàn)方法
下面小編就為大家?guī)硪黄狿ython Sqlite3以字典形式返回查詢結(jié)果的實現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-10-10Python matplotlib學(xué)習(xí)筆記之坐標(biāo)軸范圍
這篇文章主要介紹了Python matplotlib學(xué)習(xí)筆記之坐標(biāo)軸范圍,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06