視訊場景切換檢測的FPGA實現

阿不次包發表於2021-07-20

   本文將繼續講述影像處理演算法的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%左右,基本檢測出了視訊場景的切換。

  我個人認為,雙閾值灰度直方圖演算法有一定侷限性。即當視訊中的場景較為穩定時,平均幀差很小,突然來一個變動比較大的畫面時,當前幀差較大,對應的幀差比就偏大,進而產生誤警。當然,這也能解釋該演算法在一些場景下發生錯誤檢測的現象。 

相關文章