作者: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 endmodule
3、仿真测试结果图
文章来源:FPGA入门到精通