FPGA實現圖像幾何變換:裁剪


序章

  包含相同內容的兩幅圖像可能由於成像角度、透視關系乃至鏡頭自身原因所造成的幾何失真而呈現出截然不同的外觀,這就給觀測者或是圖像識別程序帶來了困擾。通過適當的幾何變換可以最大程度地消除這些幾何失真所產生的負面影響,有利於我們在后續的處理和識別工作中將注意力集中於圖像內容本身,更確切地說是圖像中的對象,而不是該對象的角度和位置等。因此,幾何變換常常作為其他圖像處理應用的預處理步驟,是圖像歸一化的核心工作之一。

  圖像幾何變換又稱為圖像空間變換,它將一幅圖像中的坐標位置映射到另一幅圖像中的新坐標位置。我們學習幾何變換的關鍵就是要確定這種空間映射關系,以及映射過程中的變換參數。幾何變換不改變圖像的像素值,只是在圖像平面上進行像素的重新安排。一個幾何變換需要兩部分運算:首先是空間變換所需的運算,如平移、旋轉和鏡像等,需要用它來表示輸出圖像與輸入圖像之間的(像素)映射關系;此外,還需要使用灰度插值算法,因為按照這種變換關系進行計算,輸出圖像的像素可能被映射到輸入圖像的非整數坐標上。

  設原圖像 f(x0,y0) 經過幾何變換產生的目標圖像為 g(x1 ,y1), 則該空間變換(映射)關系可表示為:

x1 = s(x0,y0) y1 = t(x0,y0)

  其中,s(x0,y0) 和 t(x0,y0) 為由 f(x0,y0) 到 g(x1 ,y1) 的坐標變換函數。例如,當 x= s(x0,y0) = 2x0,y1= t(x0, y0) = 2y時,變換后的圖像 g(x1,y1) 只是簡單地在 x 和 y 兩個空間方向上將 f(x0,y0) 的尺寸放大一倍。 由此可知,只要掌握了有關變換函數 s(x0, y0) 和 t(x0, y0) 的情況,就可以實現幾何變換。

   而對於FPGA來說,輸出像素通常來說並不是來自同一個輸入像素位置。這就意味着需要一些形式的緩存來處理由幾何形狀改變引起的延遲。最簡單的方法是將輸入圖像或輸出圖像(或兩者)保存在一個幀緩存中。大部分的幾何變換不太容易用數據流同時實現輸入和輸出。

 

一、FPGA幾何裁剪

  本篇博客整理一下 FPGA 實現幾何變換中最簡單的一個:幾何裁剪。前向映射將原圖像的像素坐標作為自變量,以某個變換函數得出目標圖像的像素坐標,裁剪變換的變換函數如下,Q為輸出,I為輸入,x和y為原圖像坐標,t、b、l、r為四個邊界,從某種角度來看,它實際上一種非線性濾波器,保留輸入坐標的同時變換輸出色彩。

 

 

  所以,實現一個裁剪模塊實際上是要通過給定的邊界信息來確定可以輸出的一個區域,然后根據是否在這個區域內來確定輸出。

 

二、MATLAB實現

  我們的輸入是一張 480x272 的圖片,准備裁剪成一張 200x200 的圖片。代碼如下所示:

 1 %--------------------------------------------------------------------------
 2 %                       裁剪
 3 %--------------------------------------------------------------------------
 4 clc;
 5 clear all;
 6 RGB = imread('Naruto.jpg'); %讀取圖像
 7 
 8 cut = RGB(37:236,141:340,:);%圖像裁剪
 9 
10 figure;imshow(RGB); title('原圖');
11 figure;imshow(cut); title('裁剪圖');

  代碼令人大跌眼鏡,但是它的確有效,RGB的原始三維數據應該是(1:272,1:480,3),我們直接將它的橫縱坐標切割,剩下的就是裁剪后的圖像了。

  點擊運行,得到如下結果:

  從結果來看,本次的 MATLAB 實現沒有問題,成功的將一張原本 480x272 的圖片裁剪為了 200x =200 的圖片。而如此短的 MATLAB 代碼也告訴我們,圖像的幾何裁剪,就是這么的簡單。

 

三、FPGA實現

  在本人之前的博客《協議——VGA》中,我設計的 VGA 和 TFT 屏驅動程序就提供了坐標點功能,當時我還沒有學會 SDRAM 的操作,因此用 RAM 傳輸了一張 140x140x16bit 的圖片,最后圖片顯示在了 480x272 分辨率的 TFT 屏正中央。那時其實就是運用到了圖像的幾何裁剪,將顯示區域由 480x272 裁剪為位於正中央的 140x140。這次為了這一系列圖像處理博客的連續性,我無意直接拿當時的程序來改造,而是重新來設計圖像裁剪的代碼。

  本次設計不需要用到 TFT 驅動程序的坐標信號,而是直接由輸入的圖像數據使能信號重新設計行列規划,然后設計邊界點,當像素處於邊界內時,輸出原數據,當像素處於邊界外時,輸出為0。

1、參數設置

  首先還是參數設置,我先列出來,后面的程序才容易懂。這里要提一點,MATLAB 程序中的計數是從1開始,而Verilog是從0開始,因此數值稍有不同。本次裁剪設計總共需要用到以下參數:

parameter COL               = 11'd480               ; //圖片長度
parameter ROW               = 11'd272               ; //圖片高度
//---------------------------------------------------
parameter TOP               = 11'd36                ; //邊界:上
parameter BOTTOM            = 11'd236               ; //邊界:下
parameter LEFT              = 11'd140               ; //邊界:左
parameter RIGHT             = 11'd340               ; //邊界:右

2、行列規划

  代碼最開始是行列規划,用像素的使能信號來完成:

//==========================================================================
//==    行列划分
//==========================================================================
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cnt_col <= 11'd0;
    else if(add_cnt_col) begin
        if(end_cnt_col)
            cnt_col <= 11'd0;
        else
            cnt_col <= cnt_col + 11'd1;
    end
end

assign add_cnt_col = RGB_de;
assign end_cnt_col = add_cnt_col && cnt_col== COL-11'd1;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cnt_row <= 11'd0;
    else if(add_cnt_row) begin
        if(end_cnt_row)
            cnt_row <= 11'd0;
        else
            cnt_row <= cnt_row + 11'd1;
    end
end

assign add_cnt_row = end_cnt_col;
assign end_cnt_row = add_cnt_row && cnt_row== ROW-11'd1;

3、裁剪算法

  然后就是本篇博客的核心代碼——裁剪,用行列計數器和設定的參數進行比較,選擇輸出為原數據還是0即可。

//==========================================================================
//==    裁剪
//==========================================================================
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cut_data <= 'd0;
    end
    else if((cnt_row >= TOP) && (cnt_row < BOTTOM) &&(cnt_col >= LEFT) && (cnt_col < RIGHT)) begin
        cut_data <= RGB_data;
    end
    else begin
        cut_data <= 'd0;
    end
end

4、信號同步

  最后是信號同步,這個切記不能忘,本次設計比較簡單,只是在裁剪時耗費了一個時鍾周期,因此打一拍輸出即可:

//==========================================================================
//==    信號同步
//==========================================================================
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cut_de    <= 1'd0;
        cut_vsync <= 1'd0;
        cut_hsync <= 1'd0;
    end
    else begin
        cut_de    <= RGB_de;
        cut_vsync <= RGB_vsync;
        cut_hsync <= RGB_hsync;
    end
end

  OK,檢查一下代碼和工程,編譯上板看看吧。

 

三、上板驗證

  點擊編譯生成 sof 文件,下載到開發板,打開串口助手將波特率調到1562500,用MATLAB提前生成好的圖片數據用串口助手發送出去,圖片就顯示出來了:

  之所以選擇動漫圖片,其實是因為我的FPGA板子出現了問題,灰度數據會顯示為紅色。在工程中我設計了一個按鍵,按下后就會切換為裁剪處理后的圖片,如下所示:

  和上面 MATLAB 對比可以看到,實驗成功。

 

后記

  本次設計非常簡單,但是作為一名學生,再簡單的試卷你也得做,若不是經歷過 MATLAB 到 FPGA 上板,你又怎能知道它簡單呢。其次里面包含的參數妙用、坐標思想、時序對齊等,其實也是一種不錯的練習。

  后續我會繼續整理其他的幾何變換,或許就沒那么簡單了。

 

參考資料:

[1] OpenS Lee:FPGA開源工作室(公眾號)

[2] 張錚, 王艷平, 薛桂香. 數字圖像處理與機器視覺[M]. 人民郵電出版社, 2010.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM