本文將繼續講述圖像處理算法的FPGA實現,后續可能更新圖像旋轉(1080P)、畫中畫、快速DCT等算法。視頻場景切換檢測常用於視頻編解碼領域,我選用的算法是雙閾值灰度直方圖檢測法,起初在MATLAB上實現並測出最佳雙閾值,然后將其轉換為verilog代碼,最終在XILINX K7開發板上實現視頻場景切換檢測的效果。
雙閾值灰度直方圖算法,顧名思義是采用兩個灰度閾值來判斷視頻場景的切換。其中一個是漸變閾值T1,表明視頻場景可能發生改變;另一個是突變閾值T2,表示閾值已經發生改變。當相鄰幀灰度直方圖差值比大於閾值T2時表明為場景突變幀,而當差值比大於閾值T1而小於閾值T2時則表示為場景漸變幀,差值比小於閾值T1則為連續幀。其中,相鄰灰度直方圖差值實際指的是一個權重,即當前灰度幀差與平均幀差的比,如公式(1)所示。這個比值越大,說明圖像變化越大,就越有可能發生場景切換,反之則相反。
Prop_CurrentFrame=FrameDiff/AveFrameDiff (1)
雙閾值灰度算法的MATALAB實現較為簡單,更多的是依靠公式進行計算,並對差值進行判別。當然由於讀取的是1080P視頻,又需要遍歷整幀的像素點,所以程序運行的比較慢。而程序實現的思路較為清晰,即:視頻從第二幀開始讀取,一次選取兩幀,並將讀取的圖像數據轉YUV格式,提取其中的Y分量;然后利用imhist()函數求取其對應的灰度直方圖,並計算其相鄰幀差,同時統計平均幀差;最后根據統計的平均幀差和當前幀差作比較,並根據閾值跳轉到漸變幀或者突變幀,記錄突變幀位置和突變次數。部分代碼如下所示:
for i=2:FrameNum Frame_0=read(Obj,i-1);%前一幀 Frame_1=read(Obj,i);%當前幀 ImageYuv_0=rgb2ycbcr(Frame_0); ImageYuv_1=rgb2ycbcr(Frame_1); Y_0=ImageYuv_0(:,:,1);%Y Y_1=ImageYuv_1(:,:,1);%Y GrayHist_0=imhist(Y_0); %獲取灰度直方圖 GrayHist_1=imhist(Y_1); %獲取灰度直方圖 FrameDiff=sum(abs(GrayHist_1-GrayHist_0));%當前幀與前一幀灰度直方圖幀差 SumFrameDiff=(SumFrameDiff+FrameDiff); AveFrameDiff=SumFrameDiff/(GapFrameCnt-1); end
最終經過大量測試,確定雙閾值在分別為3和5時檢測效果較好,視頻場景檢測效率大概為90%。
而對於視頻場景切換檢測算法的FPGA實現,比較關鍵的內容在於幀差的求取。幀差,顧名思義,兩幀的像素之差,而本文使用的兩幀的亮度分量(Y)差。對於1080P的圖像存儲,自然會使用到DDR。而計算幀差最少需要兩幀數據,假如使得DDR同時輸出相鄰的兩幀,然后對數據流分別統計求直方圖,依次相減,再求和,倒的確是能得到兩幀的亮度差。但是該方法DDR控制較為復雜,且最終得到像素差位寬太大,所以不采用。本文選用DDR緩存整幀圖像+bram存儲圖像亮度直方圖的方式實現。即選用兩組bram乒乓操作,一組對DDR輸出一幀圖像數據進行抽樣,在行方向每4個點抽取一次,在列方向每4列抽取一列,且像素位寬由10bit降為8bit,求取對應的灰度直方圖數據,並將其寫入bram中;另一組bram在當前幀做完灰度直方圖統計時,依次讀取上一幀緩存的對應位置灰度直方圖數據,並依次做差求和,最終求出灰度直方圖幀差以及平均幀差。
其中,灰度直方圖的統計也是極其重要的一個環節。由前面的分析可知,為縮小數據量對一幀圖像進行采樣,數據量由原來的1920*1080變為現在的129600(17bit),可這仍然是一個非常大的數字,尤其在幀差計算時帶來的累積求和,所以進一步降低統計數據量, 采用2級雙口BRAM實現灰度直方圖統計。即第一級bram采用9bit位寬,第二級bram為8bit。特選用像素灰度值尋址,當一個8bit亮度統計值計滿512時則第二級bram對應地址數值加一,直到統計完所有采樣點。在這之中,選用雙口bram的原因在於灰度直方圖的統計過程中,
當一個新的像素點傳來時,需提前一拍讀取該地值統計值,然后累加。部分RTL代碼如下所示:
//bram讀地址,提前2拍,一拍是先於bram寫,一拍是bram讀延遲 always @(posedge vid_clk or posedge reset)begin if(reset)begin BRAM_addrb_0<='h0; BRAM_addrb_1<='h0; end else if(HistData_rden)begin//外部讀取直方圖數據 if(pos_HistData_rden)begin BRAM_addrb_0<= odd_frame ? 'h0 : BRAM_addrb_0; BRAM_addrb_1<=~odd_frame ? 'h0 : BRAM_addrb_1; end else begin BRAM_addrb_0<= odd_frame ? BRAM_addrb_0+1'b1 : BRAM_addrb_0;//奇數幀讀bram0 BRAM_addrb_1<=~odd_frame ? BRAM_addrb_1+1'b1 : BRAM_addrb_1;//偶數幀讀bram1 end end else begin//寫直方圖數據前提前2拍讀 BRAM_addrb_0<= (Hist_wren && ~odd_frame) ? Hist_addr:BRAM_addrb_0;//偶數幀寫bram0 BRAM_addrb_1<= (Hist_wren && odd_frame) ? Hist_addr:BRAM_addrb_1;//奇數幀讀bram1 end end
最終,為更為方便觀察視頻場景切換的效果,特在檢測到視頻場景切換時,使得邊框變為紅色,持續0.5s。最后檢測的效果與MATLAB上測試的效果相當,正確檢測率在90%左右,基本檢測出了視頻場景的切換。
我個人認為,雙閾值灰度直方圖算法有一定局限性。即當視頻中的場景較為穩定時,平均幀差很小,突然來一個變動比較大的畫面時,當前幀差較大,對應的幀差比就偏大,進而產生誤警。當然,這也能解釋該算法在一些場景下發生錯誤檢測的現象。