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实现

image

1、腐蚀

(1)形成3x3矩阵

  这个在前面的博客花了3篇来解释,就不多说了,我把3x3矩阵的代码用一个专门的 .v 文件写好,这里直接调用即可。输入是 16bi 的二值数据,输出是矩阵数据。耗费 1 个时钟周期。

//**************************************************************************
// *** 名称 : matrix_3x3_8bit.v
// *** 作者 : xianyu_FPGA
// *** 博客 : https://www.cnblogs.com/xianyufpga/
// *** 日期 : 2020年3月
// *** 描述 : 3x3矩阵,边界采用像素复制,最大支持1024x1024,耗费1clk
//**************************************************************************

module matrix_3x3_8bit
//========================< 参数 >==========================================
#(
parameter H_DISP            = 12'd480               ,   //图像宽度
parameter V_DISP            = 12'd272                   //图像高度
)
//========================< 端口 >==========================================
(
input   wire                clk                     ,
input   wire                rst_n                   ,
//input ---------------------------------------------
input   wire                din_vld                 ,
input   wire    [ 7:0]      din                     ,
//output --------------------------------------------
output  reg     [ 7:0]      matrix_11               ,
output  reg     [ 7:0]      matrix_12               ,
output  reg     [ 7:0]      matrix_13               ,
output  reg     [ 7:0]      matrix_21               ,
output  reg     [ 7:0]      matrix_22               ,
output  reg     [ 7:0]      matrix_23               ,
output  reg     [ 7:0]      matrix_31               ,
output  reg     [ 7:0]      matrix_32               ,
output  reg     [ 7:0]      matrix_33                
);
//========================< 信号 >==========================================
reg     [11:0]              cnt_col                 ;
wire                        add_cnt_col             ;
wire                        end_cnt_col             ;
reg     [11:0]              cnt_row                 ;
wire                        add_cnt_row             ;
wire                        end_cnt_row             ;
wire                        wr_en_1                 ;
wire                        wr_en_2                 ;
wire                        rd_en_1                 ;
wire                        rd_en_2                 ;
wire    [ 7:0]              q_1                     ;
wire    [ 7:0]              q_2                     ;
wire    [ 7:0]              row_1                   ;
wire    [ 7:0]              row_2                   ;
wire    [ 7:0]              row_3                   ;
//==========================================================================
//==    FIFO例化,show模式,深度为大于两行数据个数
//==========================================================================
fifo_show_2048x8 u1
(
    .clock                  (clk                    ),
    .data                   (din                    ),
    .wrreq                  (wr_en_1                ),
    .rdreq                  (rd_en_1                ),
    .q                      (q_1                    )
);

fifo_show_2048x8 u2
(
    .clock                  (clk                    ),
    .data                   (din                    ),
    .wrreq                  (wr_en_2                ),
    .rdreq                  (rd_en_2                ),
    .q                      (q_2                    )
);
//==========================================================================
//==    行列划分
//==========================================================================
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cnt_col <= 12'd0;
    else if(add_cnt_col) begin
        if(end_cnt_col)
            cnt_col <= 12'd0;
        else
            cnt_col <= cnt_col + 12'd1;
    end
end

assign add_cnt_col = din_vld;
assign end_cnt_col = add_cnt_col && cnt_col== H_DISP-12'd1;

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

assign add_cnt_row = end_cnt_col;
assign end_cnt_row = add_cnt_row && cnt_row== V_DISP-12'd1;
//==========================================================================
//==    fifo 读写信号
//==========================================================================
assign wr_en_1 = (cnt_row < V_DISP - 12'd1) ? din_vld : 1'd0; //不写最后1行
assign rd_en_1 = (cnt_row > 12'd0         ) ? din_vld : 1'd0; //从第1行开始读
assign wr_en_2 = (cnt_row < V_DISP - 12'd2) ? din_vld : 1'd0; //不写最后2行
assign rd_en_2 = (cnt_row > 12'd1         ) ? din_vld : 1'd0; //从第2行开始读
//==========================================================================
//==    形成 3x3 矩阵,边界采用像素复制
//==========================================================================
//矩阵数据选取
//---------------------------------------------------
assign row_1 = q_2;
assign row_2 = q_1;
assign row_3 = din;

//打拍形成矩阵,1clk
//---------------------------------------------------
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        {matrix_11, matrix_12, matrix_13} <= {8'd0, 8'd0, 8'd0};
        {matrix_21, matrix_22, matrix_23} <= {8'd0, 8'd0, 8'd0};
        {matrix_31, matrix_32, matrix_33} <= {8'd0, 8'd0, 8'd0};
    end
    //------------------------------------------------------------------------- 第1排矩阵
    else if(cnt_row == 12'd0) begin
        if(cnt_col == 12'd0) begin          //第1个矩阵
            {matrix_11, matrix_12, matrix_13} <= {row_3, row_3, row_3};
            {matrix_21, matrix_22, matrix_23} <= {row_3, row_3, row_3};
            {matrix_31, matrix_32, matrix_33} <= {row_3, row_3, row_3};
        end
        else begin                          //剩余矩阵
            {matrix_11, matrix_12, matrix_13} <= {matrix_12, matrix_13, row_3};
            {matrix_21, matrix_22, matrix_23} <= {matrix_22, matrix_23, row_3};
            {matrix_31, matrix_32, matrix_33} <= {matrix_32, matrix_33, row_3};
        end
    end
    //------------------------------------------------------------------------- 第2排矩阵
    else if(cnt_row == 12'd1) begin
        if(cnt_col == 12'd0) begin          //第1个矩阵
            {matrix_11, matrix_12, matrix_13} <= {row_2, row_2, row_2};
            {matrix_21, matrix_22, matrix_23} <= {row_2, row_2, row_2};
            {matrix_31, matrix_32, matrix_33} <= {row_3, row_3, row_3};
        end
        else begin                          //剩余矩阵
            {matrix_11, matrix_12, matrix_13} <= {matrix_12, matrix_13, row_2};
            {matrix_21, matrix_22, matrix_23} <= {matrix_22, matrix_23, row_2};
            {matrix_31, matrix_32, matrix_33} <= {matrix_32, matrix_33, row_3};
        end
    end
    //------------------------------------------------------------------------- 剩余矩阵
    else begin
        if(cnt_col == 12'd0) begin          //第1个矩阵
            {matrix_11, matrix_12, matrix_13} <= {row_1, row_1, row_1};
            {matrix_21, matrix_22, matrix_23} <= {row_2, row_2, row_2};
            {matrix_31, matrix_32, matrix_33} <= {row_3, row_3, row_3};
        end
        else begin                          //剩余矩阵
            {matrix_11, matrix_12, matrix_13} <= {matrix_12, matrix_13, row_1};
            {matrix_21, matrix_22, matrix_23} <= {matrix_22, matrix_23, row_2};
            {matrix_31, matrix_32, matrix_33} <= {matrix_32, matrix_33, row_3};
        end
    end
end



endmodule

(2)腐蚀

  采用流水线方式,一级一级的运算,最后得到结果,耗费 2 个时钟周期。

//**************************************************************************
// *** 名称 : erode.v
// *** 作者 : 咸鱼FPGA
// *** 博客 : https://www.cnblogs.com/xianyufpga/
// *** 日期 : 2020年3月
// *** 描述 : erode腐蚀处理,输入必须为二值图像
//**************************************************************************

module erode
//========================< 参数 >==========================================
#(
parameter H_DISP            = 12'd480               ,   //图像宽度
parameter V_DISP            = 12'd272                   //图像高度
)
//========================< 端口 >==========================================
(
input   wire                clk                     ,   //时钟
input   wire                rst_n                   ,   //复位
//input ---------------------------------------------
input   wire                bina_de                 ,   //bina分量行同步
input   wire                bina_hsync              ,   //bina分量场同步
input   wire                bina_vsync              ,   //bina分量数据
input   wire    [ 7:0]      bina_data               ,   //bina分量数据使能
//output --------------------------------------------
output  wire                erode_de                ,   //erode行同步
output  wire                erode_hsync             ,   //erode场同步
output  wire                erode_vsync             ,   //erode数据
output  wire    [ 7:0]      erode_data                  //erode数据使能
);
//========================< 信号 >==========================================
//matrix_3x3 ----------------------------------------
wire    [ 7:0]              matrix_11               ;
wire    [ 7:0]              matrix_12               ;
wire    [ 7:0]              matrix_13               ;
wire    [ 7:0]              matrix_21               ;
wire    [ 7:0]              matrix_22               ;
wire    [ 7:0]              matrix_23               ;
wire    [ 7:0]              matrix_31               ;
wire    [ 7:0]              matrix_32               ;
wire    [ 7:0]              matrix_33               ;
//erode ---------------------------------------------
reg                         erode_1                 ;
reg                         erode_2                 ;
reg                         erode_3                 ;
reg                         erode                   ;
//同步 ----------------------------------------------
reg     [ 2:0]              bina_de_r               ;
reg     [ 2:0]              bina_hsync_r            ;
reg     [ 2:0]              bina_vsync_r            ;
//==========================================================================
//==    matrix_3x3_8bit,生成3x3矩阵,输入和使能需对齐,耗费1clk
//==========================================================================
//--------------------------------------------------- 矩阵顺序
//        {matrix_11, matrix_12, matrix_13}
//        {matrix_21, matrix_22, matrix_23}
//        {matrix_31, matrix_32, matrix_33}
//--------------------------------------------------- 模块例化
matrix_3x3_8bit
#(
    .H_DISP                 (H_DISP                 ),  //图像宽度
    .V_DISP                 (V_DISP                 )   //图像高度
)
u_matrix_3x3_8bit
(
    .clk                    (clk                    ),
    .rst_n                  (rst_n                  ),
    .din_vld                (bina_de                ),
    .din                    (bina_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              )
);
//==========================================================================
//==    腐蚀,耗费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 ? 8'hff : 8'h00;

//==========================================================================
//==    信号同步
//==========================================================================
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        bina_de_r    <= 3'b0;
        bina_hsync_r <= 3'b0;
        bina_vsync_r <= 3'b0;
    end
    else begin  
        bina_de_r    <= {bina_de_r[1:0],    bina_de};
        bina_hsync_r <= {bina_hsync_r[1:0], bina_hsync};
        bina_vsync_r <= {bina_vsync_r[1:0], bina_vsync};
    end
end

assign erode_de    = bina_de_r[2];
assign erode_hsync = bina_hsync_r[2];
assign erode_vsync = bina_vsync_r[2];
    


endmodule

 

2、膨胀

(1)形成3x3矩阵

  这个在前面的博客花了3篇来解释,就不多说了,我把3x3矩阵的代码用一个专门的 .v 文件写好,这里直接调用即可。输入是 16bi 的二值数据,输出是矩阵数据。耗费 1 个时钟周期。

//**************************************************************************
// *** 名称 : matrix_3x3_8bit.v
// *** 作者 : xianyu_FPGA
// *** 博客 : https://www.cnblogs.com/xianyufpga/
// *** 日期 : 2020年3月
// *** 描述 : 3x3矩阵,边界采用像素复制,最大支持1024x1024,耗费1clk
//**************************************************************************

module matrix_3x3_8bit
//========================< 参数 >==========================================
#(
parameter H_DISP            = 12'd480               ,   //图像宽度
parameter V_DISP            = 12'd272                   //图像高度
)
//========================< 端口 >==========================================
(
input   wire                clk                     ,
input   wire                rst_n                   ,
//input ---------------------------------------------
input   wire                din_vld                 ,
input   wire    [ 7:0]      din                     ,
//output --------------------------------------------
output  reg     [ 7:0]      matrix_11               ,
output  reg     [ 7:0]      matrix_12               ,
output  reg     [ 7:0]      matrix_13               ,
output  reg     [ 7:0]      matrix_21               ,
output  reg     [ 7:0]      matrix_22               ,
output  reg     [ 7:0]      matrix_23               ,
output  reg     [ 7:0]      matrix_31               ,
output  reg     [ 7:0]      matrix_32               ,
output  reg     [ 7:0]      matrix_33                
);
//========================< 信号 >==========================================
reg     [11:0]              cnt_col                 ;
wire                        add_cnt_col             ;
wire                        end_cnt_col             ;
reg     [11:0]              cnt_row                 ;
wire                        add_cnt_row             ;
wire                        end_cnt_row             ;
wire                        wr_en_1                 ;
wire                        wr_en_2                 ;
wire                        rd_en_1                 ;
wire                        rd_en_2                 ;
wire    [ 7:0]              q_1                     ;
wire    [ 7:0]              q_2                     ;
wire    [ 7:0]              row_1                   ;
wire    [ 7:0]              row_2                   ;
wire    [ 7:0]              row_3                   ;
//==========================================================================
//==    FIFO例化,show模式,深度为大于两行数据个数
//==========================================================================
fifo_show_2048x8 u1
(
    .clock                  (clk                    ),
    .data                   (din                    ),
    .wrreq                  (wr_en_1                ),
    .rdreq                  (rd_en_1                ),
    .q                      (q_1                    )
);

fifo_show_2048x8 u2
(
    .clock                  (clk                    ),
    .data                   (din                    ),
    .wrreq                  (wr_en_2                ),
    .rdreq                  (rd_en_2                ),
    .q                      (q_2                    )
);
//==========================================================================
//==    行列划分
//==========================================================================
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cnt_col <= 12'd0;
    else if(add_cnt_col) begin
        if(end_cnt_col)
            cnt_col <= 12'd0;
        else
            cnt_col <= cnt_col + 12'd1;
    end
end

assign add_cnt_col = din_vld;
assign end_cnt_col = add_cnt_col && cnt_col== H_DISP-12'd1;

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

assign add_cnt_row = end_cnt_col;
assign end_cnt_row = add_cnt_row && cnt_row== V_DISP-12'd1;
//==========================================================================
//==    fifo 读写信号
//==========================================================================
assign wr_en_1 = (cnt_row < V_DISP - 12'd1) ? din_vld : 1'd0; //不写最后1行
assign rd_en_1 = (cnt_row > 12'd0         ) ? din_vld : 1'd0; //从第1行开始读
assign wr_en_2 = (cnt_row < V_DISP - 12'd2) ? din_vld : 1'd0; //不写最后2行
assign rd_en_2 = (cnt_row > 12'd1         ) ? din_vld : 1'd0; //从第2行开始读
//==========================================================================
//==    形成 3x3 矩阵,边界采用像素复制
//==========================================================================
//矩阵数据选取
//---------------------------------------------------
assign row_1 = q_2;
assign row_2 = q_1;
assign row_3 = din;

//打拍形成矩阵,1clk
//---------------------------------------------------
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        {matrix_11, matrix_12, matrix_13} <= {8'd0, 8'd0, 8'd0};
        {matrix_21, matrix_22, matrix_23} <= {8'd0, 8'd0, 8'd0};
        {matrix_31, matrix_32, matrix_33} <= {8'd0, 8'd0, 8'd0};
    end
    //------------------------------------------------------------------------- 第1排矩阵
    else if(cnt_row == 12'd0) begin
        if(cnt_col == 12'd0) begin          //第1个矩阵
            {matrix_11, matrix_12, matrix_13} <= {row_3, row_3, row_3};
            {matrix_21, matrix_22, matrix_23} <= {row_3, row_3, row_3};
            {matrix_31, matrix_32, matrix_33} <= {row_3, row_3, row_3};
        end
        else begin                          //剩余矩阵
            {matrix_11, matrix_12, matrix_13} <= {matrix_12, matrix_13, row_3};
            {matrix_21, matrix_22, matrix_23} <= {matrix_22, matrix_23, row_3};
            {matrix_31, matrix_32, matrix_33} <= {matrix_32, matrix_33, row_3};
        end
    end
    //------------------------------------------------------------------------- 第2排矩阵
    else if(cnt_row == 12'd1) begin
        if(cnt_col == 12'd0) begin          //第1个矩阵
            {matrix_11, matrix_12, matrix_13} <= {row_2, row_2, row_2};
            {matrix_21, matrix_22, matrix_23} <= {row_2, row_2, row_2};
            {matrix_31, matrix_32, matrix_33} <= {row_3, row_3, row_3};
        end
        else begin                          //剩余矩阵
            {matrix_11, matrix_12, matrix_13} <= {matrix_12, matrix_13, row_2};
            {matrix_21, matrix_22, matrix_23} <= {matrix_22, matrix_23, row_2};
            {matrix_31, matrix_32, matrix_33} <= {matrix_32, matrix_33, row_3};
        end
    end
    //------------------------------------------------------------------------- 剩余矩阵
    else begin
        if(cnt_col == 12'd0) begin          //第1个矩阵
            {matrix_11, matrix_12, matrix_13} <= {row_1, row_1, row_1};
            {matrix_21, matrix_22, matrix_23} <= {row_2, row_2, row_2};
            {matrix_31, matrix_32, matrix_33} <= {row_3, row_3, row_3};
        end
        else begin                          //剩余矩阵
            {matrix_11, matrix_12, matrix_13} <= {matrix_12, matrix_13, row_1};
            {matrix_21, matrix_22, matrix_23} <= {matrix_22, matrix_23, row_2};
            {matrix_31, matrix_32, matrix_33} <= {matrix_32, matrix_33, row_3};
        end
    end
end



endmodule

(2)膨胀

  采用流水线方式,一级一级的运算,最后得到结果,耗费 2 个时钟周期。

//**************************************************************************
// *** 名称 : dilate.v
// *** 作者 : 咸鱼FPGA
// *** 博客 : https://www.cnblogs.com/xianyufpga/
// *** 日期 : 2020年3月
// *** 描述 : dilate膨胀处理,输入必须为二值图像
//**************************************************************************

module dilate
//========================< 参数 >==========================================
#(
parameter H_DISP            = 12'd480               ,   //图像宽度
parameter V_DISP            = 12'd272                   //图像高度
)
//========================< 端口 >==========================================
(
input   wire                clk                     ,   //时钟
input   wire                rst_n                   ,   //复位
//input ---------------------------------------------
input   wire                bina_de                 ,   //bina分量行同步
input   wire                bina_hsync              ,   //bina分量场同步
input   wire                bina_vsync              ,   //bina分量数据
input   wire    [ 7:0]      bina_data               ,   //bina分量数据使能
//output --------------------------------------------
output  wire                dilate_de               ,   //dilate行同步
output  wire                dilate_hsync            ,   //dilate场同步
output  wire                dilate_vsync            ,   //dilate数据
output  wire    [ 7:0]      dilate_data                 //dilate数据使能
);
//========================< 信号 >==========================================
//matrix_3x3 ----------------------------------------
wire    [ 7:0]              matrix_11               ;
wire    [ 7:0]              matrix_12               ;
wire    [ 7:0]              matrix_13               ;
wire    [ 7:0]              matrix_21               ;
wire    [ 7:0]              matrix_22               ;
wire    [ 7:0]              matrix_23               ;
wire    [ 7:0]              matrix_31               ;
wire    [ 7:0]              matrix_32               ;
wire    [ 7:0]              matrix_33               ;
//dilate --------------------------------------------
reg                         dilate_1                ;
reg                         dilate_2                ;
reg                         dilate_3                ;
reg                         dilate                  ;
//同步 ----------------------------------------------
reg     [ 2:0]              bina_de_r               ;
reg     [ 2:0]              bina_hsync_r            ;
reg     [ 2:0]              bina_vsync_r            ;
//==========================================================================
//==    matrix_3x3_8bit,生成3x3矩阵,输入和使能需对齐,耗费1clk
//==========================================================================
//--------------------------------------------------- 矩阵顺序
//        {matrix_11, matrix_12, matrix_13}
//        {matrix_21, matrix_22, matrix_23}
//        {matrix_31, matrix_32, matrix_33}
//--------------------------------------------------- 模块例化
matrix_3x3_8bit
#(
    .H_DISP                 (H_DISP                 ),  //图像宽度
    .V_DISP                 (V_DISP                 )   //图像高度
)
u_matrix_3x3_8bit
(
    .clk                    (clk                    ),
    .rst_n                  (rst_n                  ),
    .din_vld                (bina_de                ),
    .din                    (bina_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              )
);
//==========================================================================
//==    膨胀,耗费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 ? 8'hff : 8'h00;

//==========================================================================
//==    信号同步
//==========================================================================
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        bina_de_r    <= 3'b0;
        bina_hsync_r <= 3'b0;
        bina_vsync_r <= 3'b0;
    end
    else begin  
        bina_de_r    <= {bina_de_r[1:0],    bina_de};
        bina_hsync_r <= {bina_hsync_r[1:0], bina_hsync};
        bina_vsync_r <= {bina_vsync_r[1:0], bina_vsync};
    end
end

assign dilate_de    = bina_de_r[2];
assign dilate_hsync = bina_hsync_r[2];
assign dilate_vsync = bina_vsync_r[2];
    


endmodule

 

四、上板验证

  二值图像原图:
 
  腐蚀后:
 
  膨胀后:
  和 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.

 

posted @ 2020-03-22 13:12  咸鱼IC  阅读(4975)  评论(0)    收藏  举报