形态学,即数学形态学(Mathematical Morphology),是图像处理中应用最为广泛的技术之一,主要用于从图像中提取对表达和描绘区域形状有意义的图像分量,使后续的识别工作能够抓住目标对象最为本质(最具区分能力 - most discriminative)的形状特征,如边界和连通区域等。同时像细化、像素化和修剪毛刺等技术也常应用于图像的预处理和后处理中,成为图像增强技术的有力补充。


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('膨胀');
这次图片选择的就是一张二值图片,原图如下所示:
点击运行,得到如下结果:
可以看到,腐蚀后,最细的白色线条消失了,其他图案也都变得更细了,而膨胀后所有白色线条都变粗了。
(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];
(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];



[1] OpenS Lee:FPGA开源工作室(公众号)
[2] CrazyBingo:基于VIP_Board Mini的FPGA视频图像算法(HDL-VIP)开发教程-V1.6
[3] NingHechuan:FPGA图像处理教程
[4] 牟新刚、周晓、郑晓亮.基于FPGA的数字图像处理原理及应用[M]. 电子工业出版社,2017.
[5] 张铮, 王艳平, 薛桂香. 数字图像处理与机器视觉[M]. 人民邮电出版社, 2010.