作者:FPGA入门到精通
图像膨胀算法是形态学图像处理中的一种基本操作,类似于“领域被扩张”的过程,与图像腐蚀相反。
这种操作可以将图像中的前景色进行扩张,其运行结果比原图的区域更大,适用于二值化图像。
本文介绍一下图像膨胀算法的基本原理、Python实现以及FPGA实现。
老规矩:源码在文章末尾
一、图像膨胀算法
1、基本原理
膨胀的运算符号是“⊕”,其运算规则是:A⊕B={x∣Bx∩A≠∅},该公式表示图像A用卷积模板B来进行膨胀处理。
注意这里要区分,不然分不清楚图像腐蚀和图像膨胀:
一幅二值图像的前景色和背景色,前景色可以是黑色,对应背景色是白色,前景色也可以是白色,对应背景色是黑色,图像膨胀和图像腐蚀处理的是前景色,也就是前景色会扩张或缩小。
这里给一个通俗点的膨胀过程解释:
(1)在执行膨胀操作时,我们通常选择一个小的结构元素(例如3x3或5x5的窗口),然后遍历整个图像。
(2)对于图像中的每一个像素点,对应结构元素的中心点。
(3)如果白色是前景,黑色是背景,那么膨胀操作时,检查原图像与结构元素覆盖区域中所有像素点,如果覆盖区域中有前景色白色像素点,则将这个中心点像素填入白色,这样就会图形边界会扩张,只有覆盖区域所有像素点的都是背景色黑色,才能保持这个像素的黑色值。
如果黑色是前景,白色是背景,那么膨胀操作时,检查原图像与结构元素覆盖区域中所有像素点,如果覆盖区域中有前景色黑色像素点,则将这个中心点像素填入黑色,这样就会图形边界会扩张,只有覆盖区域所有像素点的都是背景色白色,才能保持这个像素的白色值。
B结构元素是3*3窗口,示例如下:

红色框中的为中心点,值为1的表示结构元素的有效覆盖区域,为0表示不需要考虑的点。
通过上面这个分析解释,相信大家应该很容理解图像膨胀了。
2、应用场景
膨胀操作通常用来填补目标区域中的某些空洞、扩大物体的边界等。
填补空洞:图像中可能存在一些空洞区域,膨胀操作可以帮助我们填补这些区域,使得物体的边界更加完整。这种方法在图像分割、边缘检测和形状分析等任务中非常有用。
连接相邻元素:在图像中,可能存在一些断裂的线段或者区域。通过膨胀操作,可以扩大这些元素的边界,使得断裂的部分连接在一起,从而实现图像的修复。
二、Python实现
1、图像膨胀函数
dst = cv2.dilate(src,kernel,anchor,iterations)
函数参数说明:
(1)dst,目标图像
(2)src,原图像
(3)kernel,腐蚀操作的结构元素,默认为一个简单的 3x3 矩
(4)anchor,默认为Point(-1,-1),结构元素中心点,可以忽略
(5)iterations,迭代次数,默认值1
注意:opencv中默认图像的背景色是黑色
2、实现代码
import cv2import numpy as np
img = cv2.imread('1280_720.bmp')
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
erode_img = cv2.dilate(img, kernel=np.ones((2, 2), np.uint8), iterations=3)
cv2.namedWindow("orignal", 0)cv2.resizeWindow("orignal", 300, 180) # 设置窗口大小cv2.namedWindow("erode", 0)cv2.resizeWindow("erode", 300, 180) # 设置窗口大小cv2.imshow('orignal', img)cv2.imshow('erode', erode_img)
cv2.waitKey(0)cv2.destroyAllWindows()
3、运行结果

背景色是黑色,前景色是白色,从图中可以看出,图像膨胀后的图像,白色区域有扩张,黑色区域有明显减少。
三、FPGA实现
1、实现逻辑分析
图像膨胀的实现,需要多个算法模块才能实现,包括灰度二值化、3行缓存、图像膨胀算法模块。

大家可以将前面学习的算法模块,拿过来串联一起就行,可以实现流水线式的图像处理。
我这里按照二值化图像一般认为黑色是前景色,白色是背景色来处理。
这里分析一下图像膨胀模块的实现关键逻辑:
图像膨胀采用模版1的结构化系数模版,那就可以简单理解为,只有3*3窗口中所有像素值都是255时,才能将当前像素值为255输出,其余全部为0,这样前景色黑色区域就扩张了。
2、FPGA实现关键源码
module image_dilate(
input wire clk,
input wire reset,
input wire valid_i,
input wire [23:0] last_line_data,
input wire [23:0] cur_line_data,
input wire [23:0] next_line_data,
output reg valid_o,
output reg [23:0] img_data_o
);
//常量声明
parameter N = 3; //窗口大小
//结构系数模版为{1,1,1,1,1,1,1,1,1}
//变量声明
reg valid_d1;
reg [23:0] last_line_data_d1, last_line_data_d2;
reg [23:0] cur_line_data_d1, cur_line_data_d2;
reg [23:0] next_line_data_d1, next_line_data_d2;
//中心点位置,为cur_line_data_d1
always@(posedge clk or posedge reset) begin
if(reset) begin
valid_d1 <= 0;
{last_line_data_d1, last_line_data_d2} <= 0;
{cur_line_data_d1, cur_line_data_d2} <= 0;
{next_line_data_d1, next_line_data_d2} <= 0;
end else begin
valid_d1 <= valid_i;
last_line_data_d1 <= last_line_data;
last_line_data_d2 <= last_line_data_d1;
cur_line_data_d1 <= cur_line_data;
cur_line_data_d2 <= cur_line_data_d1;
next_line_data_d1 <= next_line_data;
next_line_data_d2 <= next_line_data;
end
end
//模板窗口范围内判断,选出最小值
//简单理解也就是,3*3窗口中所有像素值都是255时,才能将当前像素值为255输出
always@(posedge clk or posedge reset) begin
if(reset) begin
valid_o <= 0;
img_data_o <= 0;
end else if(valid_d1) begin
if({last_line_data_d2, last_line_data_d1, last_line_data,
cur_line_data_d2, cur_line_data_d1, cur_line_data,
next_line_data_d2, next_line_data_d1, next_line_data} == {27{8'd255}}) begin
img_data_o <= cur_line_data_d1;
end else begin
img_data_o <= 0;
end
valid_o <= 1'b1;
end else begin
valid_o <= 0;
img_data_o <= 0;
end
end
endmodule3、仿真测试结果图

文章来源:FPGA入门到精通