1. 程式人生 > >FPGA影象處理系列——實現窗處理

FPGA影象處理系列——實現窗處理

窗處理是影象處理中常見的一種處理,它的思想是對於影象矩陣,通過一個固定大小(例如3*3)的小矩陣對影象進行運算操作。常用的窗處理包括Sobel邊緣檢測,形態學操作,模糊濾波,高斯濾波等。在基於PC的影象處理領域,可以方便的實現窗處理操作。比如,在opencv庫中可以自己隨意構建視窗大小,然後呼叫相關的函式實現窗處理。FPGA是一種可定製的邏輯電路,它擁有並行的結構特徵,在設計上能實現硬體並行和流水線技術,可以實現演算法的加速,而且價效比較高。本文即根據Sobel演算法的理論,結合FPGA的結構特徵,在FPGA上設計並嘗試實現了Sobel窗處理演算法方案。

Sobel邊緣檢測的思想是:該運算元包含兩組3 x 3的矩陣,分別為橫向及縱向,將之與影象作平面卷積,即可分別得出橫向及縱向的亮度查分近似值。如果以A代表原始影象,Gx及Gy分別代表經縱向向及橫向邊緣檢測的影象,其公式如下(來源於百度圖片):

 
對於FPGA實現Sobel,首先,是檢測窗的實現。這種窗擴充套件了點運算,以一個區域性鄰域或視窗內畫素值的函式運算結果作為輸出:
Q[x,y] = f(I[x,y],...,I[x+△x,y+△y]),  (△x,△y)∈W
其中,W是I[x,y]為中心的視窗。以3×3大小的視窗為例,如圖所示:
 
Sobel運算元所需的視窗大小為3×3。在設計過程中,視窗應當滿足以下要求:
1.能同時對視窗中所有的元素進行並行操作。 
2.視窗採用流水的形式遍歷整個影象。
設計的思路是:因為視窗在遍歷影象過程中,每一個畫素都會被視窗多次使用,因此需要通過快取來儲存畫素,使得它們能在後續的視窗位置被重複利用。在FPGA中,可以設計3個單口行快取(linebuffer),藉助狀態機和列計數值實現這種視窗,採用流水處理的方法,實現加速計算。如圖為視窗結構:
 
其中狀態訊號用環形計數器來實現(計數值0-2),其驅動時鐘為每一行第一個畫素的畫素時鐘。視窗的行0由3個移位暫存器組成;行1——行2由狀態機決定和行快取的對應關係。視窗的控制邏輯如表1所示:
表1 視窗的控制邏輯
 

輸入行 狀態 行快取0 行快取1 行快取2 輸出行
0 0 (行2) (行1) (空)
1 1 行1 (行2) (空)
2
2 行2 行1 1
3 0 行2 行1 2
4 1 行1 行2 3
5 2 行2 行1 4
6 0 行2 行1 5
  …… …… …… …… ……

  (注:行x對應視窗結構中的行x)
  對於視窗操作,存在的一個問題是邊界部分無法得到處理。不過,本系統的視窗比較小,那麼邊界畫素的輸出可以不計算。這樣輸出的影象比輸入影象減小了1行和1列,並不會影響影象顯示效果。

具體實現思路:
1.視窗模組:
3個行快取,每個大小為640×8 bit;
狀態機,狀態訊號(0-2),以每一行第一個畫素時鐘作為驅動狀態訊號的時鐘;
3個移位暫存器,每個大小為8 bit,作為檢測視窗的第一行
視窗的實現,需要建立一個畫素時鐘驅使的always塊,根據視窗結構和表1的控制邏輯,構造視窗;
2.邊緣判斷模組:
    通過視窗可在畫素點P處得到以其為中心,周圍8個點的畫素值。根據Sobel運算元結構,進行計算。設定一個閾值,當計算後的值大於此閾值時,判定此畫素為邊緣。

關鍵部分程式碼(verilog):

[email protected](posedge PCLK)begin//產生行時鐘,作為狀態機驅動時鐘  
begin  
     if(VtcHCnt==0)  
       Line_CLK <= 1;  
     else  
       Line_CLK <= 0;  
end  
end  
  
reg[1:0] state;  
initial state = 0;  
  
[email protected](posedge Line_CLK)begin//產生狀態計數器  
if(VtcVCnt==1)  
state <= 2'b00;  
else begin  
   if(state == 2'b10)  
       state <= 2'b00;  
    else   
       state <= state + 1;  
    end  
end   
//定義視窗第0行的三個元素  
reg[7:0]S_Window0;  
reg[7:0]S_Window1;  
reg[7:0]S_Window2;  
  
  
//定義行快取  
reg[7:0]LineBuffer0[639:0];  
reg[7:0]LineBuffer1[639:0];  
reg[7:0]LineBuffer2[639:0];  
[email protected](posedge PCLK)begin  
   case(state)  
     2'b00: begin  
        <span style="white-space:pre">    </span>LineBuffer0[VtcHCnt] <= GRAY;  
         LineBuffer1[VtcHCnt] <= LineBuffer1[VtcHCnt];  
         LineBuffer2[VtcHCnt] <= LineBuffer2[VtcHCnt];     
         S_Window0 <= GRAY;  
         S_Window1 <= S_Window0;  
             S_Window2 <= S_Window1;  
        if(VtcHCnt>=2&&VtcVCnt>=3)begin  
         if((S_Window0 + S_Window1*2 + S_Window2)>(LineBuffer1[VtcHCnt-2]+LineBuffer1[VtcHCnt-1]*2 +LineBuffer1[VtcHCnt]))  
              DOUT_reg <= ((S_Window0 + S_Window1*2  + S_Window2 -LineBuffer1[VtcHCnt-2]-LineBuffer1[VtcHCnt-1]*2  -LineBuffer1[VtcHCnt]))/4;  
         else  
         DOUT_reg <= (LineBuffer1[VtcHCnt-2]+LineBuffer1[VtcHCnt-1]*2  +LineBuffer1[VtcHCnt]-(S_Window0 + S_Window1*2  + S_Window2))/4;  
        end   
        end   
     2'b01: begin  
             LineBuffer1[VtcHCnt] <= GRAY;  
         LineBuffer0[VtcHCnt] <= LineBuffer0[VtcHCnt];  
         LineBuffer2[VtcHCnt] <= LineBuffer2[VtcHCnt];          
         S_Window0 <= GRAY;  
             S_Window1 <= S_Window0;  
             S_Window2 <= S_Window1;  
       if(VtcHCnt>=2&&VtcVCnt>=3)begin  
     if((S_Window0 + S_Window1 *2 + S_Window2)>(LineBuffer2[VtcHCnt-2]+LineBuffer2[VtcHCnt-1]*2  +LineBuffer2[VtcHCnt]))  
         DOUT_reg <= ((S_Window0 + S_Window1*2  + S_Window2 -LineBuffer2[VtcHCnt-2]-LineBuffer2[VtcHCnt-1] *2 -LineBuffer2[VtcHCnt]))/4;  
         else  
         DOUT_reg <= (LineBuffer2[VtcHCnt-2]+LineBuffer2[VtcHCnt-1] *2 +LineBuffer2[VtcHCnt]-(S_Window0 + S_Window1*2  + S_Window2))/4;    
        end          
        end  
     2'b10: begin  
        <span style="white-space:pre">    </span> LineBuffer2[VtcHCnt] <= GRAY;     
         LineBuffer0[VtcHCnt] <= LineBuffer0[VtcHCnt];  
         LineBuffer1[VtcHCnt] <= LineBuffer1[VtcHCnt];     
         S_Window0 <= GRAY;  
        S_Window1 <= S_Window0;  
        S_Window2 <= S_Window1;  
        if(VtcHCnt>=2&&VtcVCnt>=3)begin  
         if((S_Window0 + S_Window1*2  + S_Window2)>(LineBuffer0[VtcHCnt-2]+LineBuffer0[VtcHCnt-1] *2 +LineBuffer0[VtcHCnt]))  
         DOUT_reg <= ((S_Window0 + S_Window1*2  + S_Window2 -LineBuffer0[VtcHCnt-2]-LineBuffer0[VtcHCnt-1]*2  -LineBuffer0[VtcHCnt]))/4;  
         else  
         DOUT_reg <= (LineBuffer0[VtcHCnt-2]+LineBuffer0[VtcHCnt-1]*2  +LineBuffer0[VtcHCnt]-(S_Window0 + S_Window1*2  + S_Window2))/4;  
        end  
        end  
    default:    begin  
           DOUT_reg <= DOUT_reg;  
             LineBuffer0[VtcHCnt] <= LineBuffer0[VtcHCnt];  
            LineBuffer1[VtcHCnt] <= LineBuffer1[VtcHCnt];      
            LineBuffer2[VtcHCnt] <= LineBuffer2[VtcHCnt];      
             end  
    endcase  
  
end