FPGA實現圖像的二值形態學濾波:腐蝕和膨脹


  二值圖像(Binary Image)是指將圖像上的每一個像素只有兩種可能的取值或灰度等級狀態,人們經常用黑白、B&W、單色圖像表示二值圖像。二值圖像是指在圖像中,灰度等級只有兩種,也就是說,圖像中的任何像素不是 0 就是 1,再無其他過渡的灰度值。之前的鋼筆畫和 Sobel 算子輸出的就是二值圖像。

  形態學,即數學形態學(Mathematical Morphology),是圖像處理中應用最為廣泛的技術之一,主要用於從圖像中提取對表達和描繪區域形狀有意義的圖像分量,使后續的識別工作能夠抓住目標對象最為本質(最具區分能力 - most discriminative)的形狀特征,如邊界和連通區域等。同時像細化、像素化和修剪毛刺等技術也常應用於圖像的預處理和后處理中,成為圖像增強技術的有力補充。

  腐蝕與膨脹是形態學濾波的兩個基本運算,通過腐蝕和膨脹兩種運算可以實現多種功能,主要如下:
    (1)消除噪聲;
    (2)分割出獨立的圖像元素;
    (3)在圖像中連接相鄰的元素;
    (4)尋找圖像中明顯的極大值和極小值區域;
    (5)求出圖像的梯度。
  本篇博客整理的腐蝕和膨脹算法是基於二值圖像,即像素不是全 0 就是全 1。
 
一、理論分析
1、腐蝕
  腐蝕(erode)是求局部最小值的操作,實現的效果是:白色線條“變細”,黑色線條“變粗”。
  以 3x3 模板為例,1 代表白色,0代表黑色,腐蝕即這 9 個像素相與,結果為 1 則輸出為 1,否則為0。
2、膨脹
  膨脹(dialate)是求局部最大值的操作,實現的效果是:白色線條“變粗”,黑色線條“變細”。
  以 3x3 模板為例,1代表白色,0代表黑色,膨脹即這 9 個像素相與,結果為 0 則輸出為 0,否則為 1。
 
二、MATLAB實現
clc;
clear all;
close all;

RGB = imread('test.png');                %讀圖
[ROW,COL, DIM] = size(RGB);              %得到圖像行列數
%------------------------------< Erode >-----------------------------------
Erode_img = zeros(ROW,COL);
for r = 2:ROW-1
    for c = 2:COL-1
        and1 = bitand(RGB(r-1, c-1), bitand(RGB(r-1, c), RGB(r-1, c+1)));
        and2 = bitand(RGB(  r, c-1), bitand(RGB(  r, c), RGB(  r, c+1)));
        and3 = bitand(RGB(r+1, c-1), bitand(RGB(r+1, c), RGB(r+1, c+1)));
        Erode_img(r, c) = bitand(and1, bitand(and2, and3));
    end
end
%------------------------------< Dilate >----------------------------------
Dilate_img = zeros(ROW,COL);
for r = 2:ROW-1
    for c = 2:COL-1
        or1 = bitor(RGB(r-1, c-1), bitor(RGB(r-1, c), RGB(r-1, c+1)));
        or2 = bitor(RGB(  r, c-1), bitor(RGB(  r, c), RGB(  r, c+1)));
        or3 = bitor(RGB(r+1, c-1), bitor(RGB(r+1, c), RGB(r+1, c+1)));
        Dilate_img(r, c) = bitor(or1, bitor(or2, or3));
    end
end
%------------------------------< show >------------------------------------
figure;         imshow(RGB);       title('原圖');
subplot(2,1,1); imshow(Erode_img); title('腐蝕');
subplot(2,1,2); imshow(Dilate_img);title('膨脹');

   這次圖片選擇的就是一張二值圖片,原圖如下所示:

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

  可以看到,腐蝕后,最細的白色線條消失了,其他圖案也都變得更細了,而膨脹后所有白色線條都變粗了。

 

三、FPGA實現
1、腐蝕

(1)形成3x3矩陣

  這個在前面的博客花了3篇來解釋,就不多說了,我把3x3矩陣的代碼用一個專門的 .v 文件寫好,這里直接調用即可。輸入是 16bi 的二值數據,輸出是矩陣數據。耗費 1 個時鍾周期。

//==========================================================================
//==    matrix_3x3_16bit,生成3x3矩陣,輸入和使能需對齊,耗費1clk
//==========================================================================
//--------------------------------------------------- 矩陣順序
//        {matrix_11, matrix_12, matrix_13}
//        {matrix_21, matrix_22, matrix_23}
//        {matrix_31, matrix_32, matrix_33}
//--------------------------------------------------- 模塊例化
matrix_3x3_16bit
#(
    .COL                    (480                    ),
    .ROW                    (272                    )
)
u_matrix_3x3_16bit
(
    .clk                    (clk                    ),
    .rst_n                  (rst_n                  ),
    .din_vld                (RGB_de                 ),
    .din                    (RGB_data               ),
    .matrix_11              (matrix_11              ),
    .matrix_12              (matrix_12              ),
    .matrix_13              (matrix_13              ),
    .matrix_21              (matrix_21              ),
    .matrix_22              (matrix_22              ),
    .matrix_23              (matrix_23              ),
    .matrix_31              (matrix_31              ),
    .matrix_32              (matrix_32              ),
    .matrix_33              (matrix_33              )
);

(2)腐蝕

  采用流水線方式,一級一級的運算,最后得到結果,耗費 2 個時鍾周期。

//==========================================================================
//==    腐蝕,耗費2clk
//==========================================================================
//clk1,三行各自相與
//---------------------------------------------------
always @ (posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        erode_1 <= 'd0;
        erode_2 <= 'd0;
        erode_3 <= 'd0;
    end
    else begin
        erode_1 <= matrix_11 && matrix_12 && matrix_13;
        erode_2 <= matrix_21 && matrix_22 && matrix_23;
        erode_3 <= matrix_31 && matrix_32 && matrix_33;
    end
end

//clk2,全部相與
//---------------------------------------------------
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        erode <= 'd0;
    end
    else begin
        erode <= erode_1 && erode_2 && erode_3;
    end
end
//==========================================================================
//==    腐蝕后的數據
//==========================================================================
assign erode_data = erode ? 16'hffff : 16'h0000;

(3)信號同步

  共耗費 3 個時鍾周期,相關信號延遲 3 拍。

//==========================================================================
//==    信號同步
//==========================================================================
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        RGB_de_r    <= 3'b0;
        RGB_hsync_r <= 3'b0;
        RGB_vsync_r <= 3'b0;
    end
    else begin  
        RGB_de_r    <= {RGB_de_r[1:0],    RGB_de};
        RGB_hsync_r <= {RGB_hsync_r[1:0], RGB_hsync};
        RGB_vsync_r <= {RGB_vsync_r[1:0], RGB_vsync};
    end
end

assign erode_de    = RGB_de_r[2];
assign erode_hsync = RGB_hsync_r[2];
assign erode_vsync = RGB_vsync_r[2];

 

2、膨脹

(1)形成3x3矩陣

  這個在前面的博客花了3篇來解釋,就不多說了,我把3x3矩陣的代碼用一個專門的 .v 文件寫好,這里直接調用即可。輸入是 16bi 的二值數據,輸出是矩陣數據。耗費 1 個時鍾周期。

//==========================================================================
//==    matrix_3x3_16bit,生成3x3矩陣,輸入和使能需對齊,耗費1clk
//==========================================================================
//--------------------------------------------------- 矩陣順序
//        {matrix_11, matrix_12, matrix_13}
//        {matrix_21, matrix_22, matrix_23}
//        {matrix_31, matrix_32, matrix_33}
//--------------------------------------------------- 模塊例化
matrix_3x3_16bit
#(
    .COL                    (480                    ),
    .ROW                    (272                    )
)
u_matrix_3x3_16bit
(
    .clk                    (clk                    ),
    .rst_n                  (rst_n                  ),
    .din_vld                (RGB_de                 ),
    .din                    (RGB_data               ),
    .matrix_11              (matrix_11              ),
    .matrix_12              (matrix_12              ),
    .matrix_13              (matrix_13              ),
    .matrix_21              (matrix_21              ),
    .matrix_22              (matrix_22              ),
    .matrix_23              (matrix_23              ),
    .matrix_31              (matrix_31              ),
    .matrix_32              (matrix_32              ),
    .matrix_33              (matrix_33              )
);

(2)膨脹

  采用流水線方式,一級一級的運算,最后得到結果,耗費 2 個時鍾周期。

//==========================================================================
//==    膨脹,耗費2clk
//==========================================================================
//clk1,三行各自相或
//---------------------------------------------------
always @ (posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        dilate_1 <= 'd0;
        dilate_2 <= 'd0;
        dilate_3 <= 'd0;
    end
    else begin
        dilate_1 <= matrix_11 || matrix_12 || matrix_13;
        dilate_2 <= matrix_21 || matrix_22 || matrix_23;
        dilate_3 <= matrix_31 || matrix_32 || matrix_33;
    end
end

//clk2,全部相或
//---------------------------------------------------
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        dilate <= 'd0;
    end
    else begin
        dilate <= dilate_1 || dilate_2 || dilate_3;
    end
end
//==========================================================================
//==    膨脹后的數據
//==========================================================================
assign dilate_data = dilate ? 16'hffff : 16'h0000;

(3)信號同步

  共耗費 3 個時鍾周期,相關信號延遲 3 拍。

//==========================================================================
//==    信號同步
//==========================================================================
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        RGB_de_r    <= 3'b0;
        RGB_hsync_r <= 3'b0;
        RGB_vsync_r <= 3'b0;
    end
    else begin  
        RGB_de_r    <= {RGB_de_r[1:0],    RGB_de};
        RGB_hsync_r <= {RGB_hsync_r[1:0], RGB_hsync};
        RGB_vsync_r <= {RGB_vsync_r[1:0], RGB_vsync};
    end
end

assign dilate_de    = RGB_de_r[2];
assign dilate_hsync = RGB_hsync_r[2];
assign dilate_vsync = RGB_vsync_r[2];

 

四、上板驗證
  二值圖像原圖:
 
  腐蝕后:
 
  膨脹后:
  和 MATLAB 結果一致,實驗成功。
 
 
參考資料:

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

[2] CrazyBingo:基於VIP_Board Mini的FPGA視頻圖像算法(HDL-VIP)開發教程-V1.6 

[3] NingHechuan:FPGA圖像處理教程

[4] 牟新剛、周曉、鄭曉亮.基於FPGA的數字圖像處理原理及應用[M]. 電子工業出版社,2017.

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

 


免責聲明!

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



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