Python基于OpenCV的視頻圖像處理詳解
初識OpenCV
OpenCV是一個開源的,跨平臺的計算機(jī)視覺庫,它采用優(yōu)化的C/C++代碼編寫,能夠充分利用多核處理器的優(yōu)勢,提供了Python,Ruby,MATPLOAB以及其他高級語言接口。
OpenCV的設(shè)計目標(biāo)是執(zhí)行速度盡量快,主要面向?qū)崟r應(yīng)用,是視頻信號處理的主要工具之一,它封裝了豐富的視頻處理相關(guān)的工具包。視頻信號是重要的視覺信息來源,其中包含的信息要遠(yuǎn)大于圖像,對視頻的分析也是計算機(jī)視覺領(lǐng)域的重要研究方向之一。視頻在本質(zhì)上由連鎖的多幀圖像構(gòu)成,因此,視頻信號處理最終仍歸屬圖像處理范疇。但在視頻中,其時間維度也包含了許多有用的信息。
視頻讀寫處理
視頻一般有兩種來源,一種是從本地磁盤加載,另一種是從攝像頭等設(shè)備實(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'),該選項(xiàng)為一個未壓縮的YUV顏色編碼類型,是4:2:0色度子采樣。改編碼有著很好的兼容性,但是會產(chǎn)生較大的文件,文件拓展名為:“.avi”
- ②.VideoWriter_fourcc('P','T','M','1'):該選項(xiàng)為“MPEG-1”編碼類型,文件拓展名為“.mpeg”
- ③.VideoWrite_fourcc('X','V','T','D'):該選項(xiàng)是“MPEG-4”編碼類型,如果希望得到的視頻大小為平均值,推薦使用該選項(xiàng),文件拓展名為:“.mp4”
- ④.VideWriter_fourcc('T','H','E','O'):該選項(xiàng)是“Ogg Vorbis”編碼類型,文件拓展名為“.ogv”。
- ⑤.VideWriter_foucc('F','L','V','T'):該選項(xiàng)是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()運(yùn)動軌跡標(biāo)記
運(yùn)動捕捉(Motion Capture)技術(shù)可對運(yùn)動物體或其特征點(diǎn)在三維空間中的運(yùn)動軌跡進(jìn)行實(shí)時,精確,定量地連續(xù)測量,跟蹤和記錄。運(yùn)動軌跡則是從物體開始位置運(yùn)動結(jié)束位置所經(jīng)過的路線組成的空間特征?;谟嬎銠C(jī)視覺圖像處理技術(shù)的運(yùn)動捕捉方案在動畫及游戲制作,仿真訓(xùn)練等領(lǐng)域有著廣泛的應(yīng)用。
光流(Optical Flow)是圖像亮度的運(yùn)動信息描述,是空間運(yùn)動物體在觀測成像面上的像素運(yùn)動的瞬時速度,也是對視頻中運(yùn)動對象軌跡進(jìn)行標(biāo)記的一種常用方法。光流由場景中前景目標(biāo)本身的運(yùn)動,攝像機(jī)的運(yùn)動,或者兩者的共同運(yùn)動產(chǎn)生。當(dāng)人通過眼睛觀察運(yùn)動物體時,物體的景象在人眼的視網(wǎng)膜上形成一系列連續(xù)變化的圖像,這一系列連續(xù)變化的信息不斷“流過”視網(wǎng)膜,猶如光在平面中的流動,故稱之為“光流”。光流的概念在20世紀(jì)40年代首次被提出,該方法利用圖像序列中的像素在時域上的變化,相鄰幀之間的相關(guān)性來找到前一幀與當(dāng)前幀之間存在的對應(yīng)關(guān)系,從而計算出相鄰幀之間物體的運(yùn)動信息。光流表達(dá)了圖像的變化,由于它包含了目標(biāo)運(yùn)動的信息,因此可被觀察者用于確定目標(biāo)的運(yùn)動情況。
在真實(shí)的三維空間中,描述物體運(yùn)動狀態(tài)的物理概念是運(yùn)動場(Motion Field)。三維空間中的每一個點(diǎn),經(jīng)過某段時間的運(yùn)動之后會到達(dá)一個新的位置,而這個位移過程可以用運(yùn)動場來描述,運(yùn)動場的實(shí)質(zhì)上就是物體在三維真實(shí)世界的運(yùn)動,而時光流暢(Optical Flow Field)是指圖像中所有像素點(diǎn)構(gòu)成的一種二維瞬時速度場它是一個二維矢量場。
三維空間運(yùn)動到二維平面的投影所形成的光流,當(dāng)描述部分像素時,稱為稀疏光流,當(dāng)描述全部像素時,則稱為稠密光流。
OpenCV實(shí)現(xiàn)了不少光流算法,其中,Lucas-Kanade(L-K)是一種廣泛使用的光流估計差分算法,它由布魯斯·D.盧卡斯(Bruce D.Lucas)和金出武雄(Takeo Kanade)提出,L-K算法假設(shè)光流在像素點(diǎn)的領(lǐng)域是一個常數(shù),然后使用最小二乘法對領(lǐng)域中的所有像素點(diǎn)求解基本的光流方程。通過結(jié)合幾個鄰近像素點(diǎn)的信息,L-K算法通常能夠消除光流方程中的多義性。而且與逐點(diǎn)計算的方法比,L-K算法對圖像噪聲不敏感。對于L-K算法,低速度,亮度不變以及區(qū)域一致性都是較強(qiáng)的假設(shè),但是這些條件并不容易滿足。當(dāng)運(yùn)動速度過快時,這種假設(shè)不成立,使得最終求出的光流值有較大的誤差。吉思——伊卡斯·布格(Jean—Yves Bouguent)提出了一種基于金字塔分層的算法,針對仿射變換的改進(jìn)L-K算法,該算法最明顯的優(yōu)勢在于,對于每一層的光流都會保持很小,但最終計算的光流可進(jìn)行累積,從而可有效地跟蹤特征點(diǎ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)鍵點(diǎn)(角點(diǎn))數(shù)
qualityLevel=0.3, #角點(diǎn)閾值:反映一個像素點(diǎn)對強(qiáng)才算一個角點(diǎn)
minDistance=7, #角點(diǎn)之間的最小像素點(diǎn)(歐氏距離)
blockSize=7) #計算一個像素點(diǎn)是否為關(guān)鍵點(diǎn)時所取區(qū)域的大小
class App:
#構(gòu)造方法,初始化一些參數(shù)和視頻路徑
def __init__(self,video_src):
self.track_len=10 #光流標(biāo)記長度
self.detect_interval=5#幀檢測間隔
#跟蹤點(diǎn)幾何初始化,self.tracks中值的格式時:(前一幀角點(diǎn))
self.tracks=[]
self.cam=cv2.VideoCapture(video_src) #視頻源
self.frame_idx=0 #幀序列號初始化
def run(self): #運(yùn)行光流方法
while True:
ret,frame=self.cam.read()
if ret ==True:
frame_gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
vis=frame.copy()
#檢測到角點(diǎn)后光流跟蹤
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)
#將前一幀的角點(diǎn)和當(dāng)前幀的圖像作為輸入的角點(diǎn)在當(dāng)前幀的位置
p1,st,err=cv2.calcOpticalFlowPyrLK(img0,img1,p0,None,**lk_params)
#將當(dāng)前幀跟蹤到的角點(diǎn)以及圖像和前一幀的圖像作為輸入得到前一幀的角點(diǎn)檢測
p0r,st,err=cv2.calcOpticalFlowPyrLK(img1,img0,p1,None,**lk_params)
#得到角點(diǎn)回溯與前一幀實(shí)際角點(diǎn)的位置變化系
d=abs(p0-p0r).reshape(-1,2).max(-1)
good=d<1 #判斷d的值是否小于1
new_tracks=[]
#將跟蹤的點(diǎn)列為成功跟蹤點(diǎn)
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
#以前一幀角點(diǎn)為初始點(diǎn),以當(dāng)前幀跟蹤到的點(diǎn)為終點(diǎn)劃線,開始軌跡標(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 #計算全部圖像的角點(diǎn)
for x,y in [np.int32(tr[-1]) for tr in self.tracks]:
cv2.circle(mask,(x,y),5,0,-1)
#利用goodFeaturesToTrack進(jìn)行角點(diǎ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)]) #將檢測到的角點(diǎn)放在預(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() #運(yùn)行主程序
cv2.destroyAllWindows()
if __name__=='__main__':
main() 由于視頻是本地文件,不能展示出來,這里就直接呈現(xiàn)代碼;通過運(yùn)行結(jié)果程序可知,稠密光流算法是一種對圖像進(jìn)行逐點(diǎn)匹配的圖像配準(zhǔn)算法。不同于稀疏光流算法只針對圖像中若干個特征點(diǎn),稠密光流算法計算圖像上所有的點(diǎn)的偏移量,從而形成一個稠密的光流場。通過這個稠密的光流場,可以進(jìn)行像素級別的圖像配準(zhǔn),因此,其配準(zhǔn)后的效果也明顯優(yōu)于稀疏光流算法配準(zhǔn)的效果。但是其副作用也非常明顯,由于要計算每個點(diǎ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,返回值為每一個像素點(diǎn)的位移。
基于稠密光流算法的運(yùn)動軌跡標(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)記線條端點(diǎn)
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()
運(yùn)動檢測
運(yùn)動檢測(Motion Detection)是計算機(jī)視覺和視頻處理中常用的預(yù)處理步驟,是指從視頻中識別發(fā)生變化或移動的區(qū)域。運(yùn)動檢測最常見的應(yīng)用場景是運(yùn)動目標(biāo)檢測,也就是對攝像頭記錄的視頻移動目標(biāo)進(jìn)行定位到過程,有著非常廣泛的應(yīng)用。實(shí)時目標(biāo)檢測是許多計算機(jī)視覺的重要任務(wù),例如安全監(jiān)控,增強(qiáng)現(xiàn)實(shí)應(yīng)用,基于對象的視頻壓縮,基于感知的用戶界面及輔助駕駛等。
運(yùn)動目標(biāo)檢測算法根據(jù)目標(biāo)與攝像機(jī)之間的關(guān)系可以分為靜態(tài)背景下的運(yùn)動目標(biāo)檢測和動態(tài)背景下的運(yùn)動目標(biāo)檢測。靜態(tài)背景下的運(yùn)動目標(biāo)檢測,就是從序列圖像中將實(shí)際的的變化區(qū)域與背景分開。在背景靜止的大前提下進(jìn)行運(yù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)背景下的運(yùn)動目標(biāo)檢測,相對于靜態(tài)背景而言,算法的思路有所不同,一般更側(cè)重于匹配,需要進(jìn)行圖像的全局運(yùn)動估計與補(bǔ)償,因?yàn)樵谀繕?biāo)和背景同時運(yùn)動的情況下,無法簡單的根據(jù)運(yùn)動來判斷。動態(tài)背景下的運(yùn)動目標(biāo)檢測算法也有很多,例如塊匹配(Block Matching,BM)和光流估計(Optical Flow Estimation,OFE)等。
幀間差分法是一種常用的運(yùn)動目標(biāo)檢測算法,其基本原理是觀測視頻圖像相鄰幀之間的細(xì)微變化來判斷物體是否在運(yùn)動。攝像機(jī)采集的視頻序列具有連續(xù)性,如果場景內(nèi)沒有運(yùn)動目標(biāo),則連續(xù)幀的變化很??;如果存在運(yùn)動目標(biāo),由于場景中的目標(biāo)的運(yùn)動,目標(biāo)的影像在不同圖像幀之間的位置會不同,從而導(dǎo)致連續(xù)幀之間有顯著變化。該算法通過對時間上連續(xù)的兩幀或三幀圖像進(jìn)行差分運(yùn)算,對不同幀對應(yīng)的的像素點(diǎn)灰度值想減,來判斷灰度值的絕對值,當(dāng)絕對值超過一定閾值時,則可判斷其為運(yùn)動目標(biāo),從而實(shí)現(xiàn)目標(biāo)的檢測功能。

(二幀差分原理圖)

(三幀差分原理圖)
在OpebCV中用absdiff()函數(shù)實(shí)現(xiàn)。
三幀差分法的運(yùn)算過程如上圖所示。記視頻序列中第n+1幀、第n幀和第n-1幀的圖像分別為fn+1、fn和fn-1,三幀對應(yīng)像素點(diǎn)的灰度值記為fn+1(x,y)、fn(x,y)和fn-1(x,y),分別得到差分圖像Dn和Dn+1,對差分圖像Dn和Dn+1按照下式進(jìn)行計算,得到圖像Dn',然后再進(jìn)行閾值處理、連通性分析,最終提取出運(yùn)動目標(biāo)。

在幀間差分法中,閾值T的選擇非常重要。如果閾值T選取的值太小,則無法抑制差分圖像中的噪聲;如果閾值T選取的值太大,又有可能掩蓋差分圖像中目標(biāo)的部分信息;而且固定的閾值T無法適應(yīng)場景中光線變化等情況。為此,有人提出了在判決條件中加入對整體光照敏感的添加項(xiàng)的方法,將判決條件修改為:

其中,NA為待檢測區(qū)域中像素的總數(shù)目,λ為光照的抑制系數(shù),A可設(shè)為整幀圖像。紅框中的添加項(xiàng)表達(dá)了整幀圖像中光照的變化情況。如果場景中的光照變化較小,則該項(xiàng)的值趨向于零;如果場景中的光照變化明顯,則該項(xiàng)的值明顯增大,導(dǎo)致上式右側(cè)判決條件自適應(yīng)地增大,最終的判決結(jié)果為沒有運(yùn)動目標(biāo),這樣就有效地抑制了光線變化對運(yùn)動目標(biāo)檢測結(jié)果的影響。
下圖中左圖是采用幀間差分法進(jìn)行運(yùn)動目標(biāo)檢測的實(shí)驗(yàn)結(jié)果,(b)圖是采用兩幀差分法的檢測結(jié)果,(c)圖是采用三幀差分法的檢測結(jié)果。視頻序列中的目標(biāo)運(yùn)動較快,在這種情況下,運(yùn)動目標(biāo)在不同圖像幀內(nèi)的位置明顯不同,采用兩幀差分法檢測出的目標(biāo)會出現(xiàn)“重影”的現(xiàn)象,采用三幀差分法,可以檢測出較為完整的運(yùn)動目標(biāo)。

綜上所述,幀間差分法的原理簡單,計算量小,能夠快速檢測出場景中的運(yùn)動目標(biāo)。但由實(shí)驗(yàn)結(jié)果可以看出,幀間差分法不能提取出對象的完整區(qū)域,只能提取出邊界。同時依賴于選擇的幀間時間間隔,對快速運(yùn)動的物體,需要選擇較小的時間間隔,如果選擇不合適,當(dāng)物體在前后兩幀中沒有重疊時,會被檢測為兩個分開的物體,而對慢速運(yùn)動的物體,應(yīng)該選擇較大的時
間差,如果時間選擇不適當(dāng),當(dāng)物體在前后兩幀中幾乎完全重疊時,則檢測不到物體。
因此幀間差分法通常不單獨(dú)用在目標(biāo)檢測中,往往與其它的檢測算法結(jié)合使用。常見的是結(jié)合背景差分法和幀間差分法的優(yōu)缺點(diǎn),使它們優(yōu)勢互補(bǔ),從而克服相互的弱點(diǎn),提高運(yùn)動檢測的效果。例如在實(shí)際的場景中,即便是室內(nèi)環(huán)境,也存在光線等各種變化造成的干擾,或者人為造成的開燈等光線的強(qiáng)烈變化。所以在背景差分法的實(shí)現(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')
'''
初始化視頻流的第一幀。一般情況下,視頻第一幀不會包含運(yùn)動而僅僅是背景
'''
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)的像素點(diǎn)的灰度值相減并求絕對值來計算兩幀的不同
frameDelta=cv2.absdiff(firstFrame,gray)
#對差分圖像進(jìn)行閾值處理來顯示圖像中像素點(diǎ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)
#釋放攝像機(jī)資源并關(guān)閉打開的窗口
camera.release()
cv2.destroyAllWindows()
由于視頻不太容易上傳,這里就使用截屏作為結(jié)果展示。
運(yùn)動方向檢測
在某些應(yīng)用場合,檢測出運(yùn)動的物體之后,我們還需知道物體的運(yùn)動方向,判斷其是否進(jìn)入或離開檢測區(qū)域。對于運(yùn)動方向的檢測,一般通過檢測圖像的光流場估算圖像的運(yùn)動場來實(shí)現(xiàn)。根據(jù)傳統(tǒng)估算方法,需要對圖像中的每一個像素進(jìn)行計算,算出圖像每一點(diǎn)的運(yùn)動場,然后得到整幅圖像的運(yùn)動場。
檢測物體的運(yùn)動方向,理論上也可以使在幀間差分法的基礎(chǔ)上通過計算幀圖像輪廓中點(diǎn)的變化來實(shí)現(xiàn),但因每次檢測出的輪廓數(shù)量不穩(wěn)定,所以該方式會使得誤差不可控。不過,OpenCv中的goodFeaturesToTrack()函數(shù)可用于獲取圖像最大特征只的角點(diǎn)。我們可以此為契機(jī)重新設(shè)計物體運(yùn)動方向檢測算法,步驟如下:
- (1) 對相鄰兩幀圖像所有像素點(diǎn)通過absdiff()函數(shù)進(jìn)行差分運(yùn)算得到差分圖像。
- (2) 將差分圖像轉(zhuǎn)化成灰度圖像并進(jìn)行二值化處理。
- (3) 利用goodFeaturesToTrack()函數(shù)獲得最大特征值的角點(diǎn)。
- (4) 計算角點(diǎn)的平均特征值,寫入隊(duì)列。
- (5) 維護(hù)一個長度為10的隊(duì)列,隊(duì)列滿時計算隊(duì)列中元素的增減情況,并以此來確定目標(biāo)的運(yùn)動方向。
利用上述改進(jìn)算法進(jìn)行物體運(yùn)動方向檢測的示例代碼:
#運(yùn)動方向檢測
import cv2
import numpy as np
import queue #導(dǎo)入庫主要用于隊(duì)列處理
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)鍵點(diǎn)數(shù)
qualityLevel=0.3, #角點(diǎn)閾值:響應(yīng)最大值
minDistance=7, #角點(diǎn)之間最少像素點(diǎn)(歐氏距離)
blockSize=7) #計算一個像素點(diǎn)是否為關(guān)鍵點(diǎ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
#隊(duì)列初始化
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:#表明隊(duì)列在增加
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('運(yùn)動方向檢測',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實(shí)現(xiàn)七大排序算法的代碼實(shí)例
這篇文章主要介紹了使用Python實(shí)現(xiàn)七大排序算法的代碼實(shí)例,所謂排序,就是使一串記錄,按照其中的某個或某些關(guān)鍵字的大小,遞增或遞減的排列起來的操作,需要的朋友可以參考下2023-07-07
配置python連接oracle讀取excel數(shù)據(jù)寫入數(shù)據(jù)庫的操作流程
這篇文章主要介紹了配置python連接oracle,讀取excel數(shù)據(jù)寫入數(shù)據(jù)庫,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03
python3+openCV 獲取圖片中文本區(qū)域的最小外接矩形實(shí)例
這篇文章主要介紹了python3+openCV 獲取圖片中文本區(qū)域的最小外接矩形實(shí)例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-06-06
基于Python實(shí)現(xiàn)一個簡單的學(xué)生管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了如何利用python實(shí)現(xiàn)簡單的學(xué)生信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-12-12
Python Sqlite3以字典形式返回查詢結(jié)果的實(shí)現(xiàn)方法
下面小編就為大家?guī)硪黄狿ython Sqlite3以字典形式返回查詢結(jié)果的實(shí)現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-10-10
Python matplotlib學(xué)習(xí)筆記之坐標(biāo)軸范圍
這篇文章主要介紹了Python matplotlib學(xué)習(xí)筆記之坐標(biāo)軸范圍,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06

