Morphology(形态学)
在数学形态学中,闭运算被定义为先膨胀后腐蚀,反之,开运算被定义为先腐蚀后膨胀。膨胀与腐蚀(Dilate and Erode)操作被称为形态学操作。(注意,这里是先闭后开,即膨胀腐蚀腐蚀膨胀,输入大小为 640*480,窗口为 21*21)。
1、膨胀
会扩大一幅图像中的组成部分(扩大白色区域,缩小黑色区域);例如,5*5 大小的 Filter 在一幅二值化图像上移动,就像一个滤波器一样,如果像素区域在 5*5 窗口中都是黑色,则将像素变成黑色,黑色为 0(0),即全 0 为 0 ,否则为 1。
2、腐蚀
会缩小一幅图像中的组成部分(缩小白色区域,扩大黑色区域),例如,5*5 大小的 Filter 在一幅二值化图像上移动,就像一个滤波器一样,如果像素区域在 5*5 窗口中都是白色,则将像素变成白色,白色为 255(1),即全 1 为 1 ,否则为 0。
数学形态学在降噪中是有用的,可以平滑物体的轮廓、消除小斑点或突出物、填补轮廓线中的断裂、关闭物体内的小孔或暗区等等;例如开运算中,腐蚀首先消除噪声并收缩物体,然后膨胀再次扩大物体。
1、开运算的效果:
- 删除小物体(滤掉突刺);
- 将物体拆分为小物体(切断细长搭接)起分离作用;
- 平滑大物体边界而不明显改变它们面积(平滑图像轮廓)。
2、闭运算的作用:
- 填充物体的裂缝和小洞;
- 连相近物体(搭接短的间断)起连通补接作用;
- 平滑大物体边界而不明显改变它们面积(平滑图像轮廓)。
以下是简单地形态学操作代码:
module Morphology(
clk_i,
rst_n,
iStart,
iData,
oStart,
oData
);
input clk_i;
input rst_n;
input iStart;
input iData;
output oStart;
output oData;
//=======================================================
// output reg and wire
//=======================================================
wire oStart;
wire oData;
//=======================================================
//说明,先膨胀后腐蚀
//(暂时做近距离,膨胀5*29,腐蚀9*34)
//先一行29的缓存,将29个data的或结果,作为输出1
//输出1的进行5行缓存,将5个输出做或,作为输出2
//将输出2进行34行缓存,34个data的与结果,作为输出3
//将输出3进行9行缓存,将9个输出做与,作为最终输出。
//所有寄存器默认为0.
//=======================================================
//=======================================================
//1 29个Data缓存,或运算
//=======================================================
reg [28:0]Dilate_H;
wire Data1;
//=======================================================
//2 5行缓存,5列同位置做或运算
//=======================================================
wire [4:0]Dilate_V;
wire [23:0]Dilate_RowBuffer_taps;
wire Data2;
//=======================================================
//3 34个Data缓存,与运算
//=======================================================
reg [33:0]Erode_H;
wire Data3;
//=======================================================
//4 9行缓存,9个同位置数据做与运算
//=======================================================
wire [8:0]Erode_V;
wire [47:0]Erode_RowBuffer_taps;
wire Data4;
//=======================================================
//5 Start signal
//=======================================================
reg Start_d1,Start_d2;
reg [4:0]Delay_Count;
//=======================================================
//1 29个Data缓存,或运算
//=======================================================
always @ (posedge clk_i or negedge rst_n)begin
if(!rst_n)begin
Dilate_H <= 29'd0;
end
else begin
if(iStart)begin
Dilate_H <= {Dilate_H[27:0],iData};
end
else begin
Dilate_H <= 29'd0;
end
end
end
assign Data1 = Dilate_H == 29'd0 ? 1'b0 : 1'b1;
//=======================================================
//2 5行缓存,5列同位置做或运算
// Data2 为膨胀结果
//=======================================================
LPR_LPL_Morphology_DilateRowBuffer LPR_LPL_Morphology_DilateRowBuffer (
.clock(clk_i),
.shiftin(Data1),
.taps(Dilate_RowBuffer_taps)
);
assign Dilate_V = {Dilate_RowBuffer_taps[19],
Dilate_RowBuffer_taps[14],
Dilate_RowBuffer_taps[9],
Dilate_RowBuffer_taps[4],
Data1};
assign Data2 = Dilate_V == 5'd0 ? 1'b0 : 1'b1;
//=======================================================
//3 34个Data缓存,与运算
//=======================================================
always @ (posedge clk_i or negedge rst_n)begin
if(!rst_n)begin
Erode_H <= 34'd0;
end
else begin
if(iStart)begin
Erode_H <= {Erode_H[32:0],Data2};
end
else begin
Erode_H <= 34'd0;
end
end
end
assign Data3 = Erode_H == 34'h3_ffff_ffff ? 1'b1 : 1'b0;
//=======================================================
//4 9行缓存,9个同位置数据做与运算
//=======================================================
LPR_LPL_Morphology_ErodeRowBuffer LPR_LPL_Morphology_ErodeRowBuffer(
.clock(clk_i),
.shiftin(Data3),
.taps(Erode_RowBuffer_taps)
);
assign Erode_V = { Erode_RowBuffer_taps[39],
Erode_RowBuffer_taps[34],
Erode_RowBuffer_taps[29],
Erode_RowBuffer_taps[24],
Erode_RowBuffer_taps[19],
Erode_RowBuffer_taps[14],
Erode_RowBuffer_taps[9],
Erode_RowBuffer_taps[4],
Data3};
assign Data4 = Erode_V == 9'h1ff ? 1'b1 : 1'b0;
assign oData = Data4;
//=======================================================
//5 Start signal
//实际准确输出的数据要比输入的少,但是边界影响不大,
//所以简单延时几个,对齐每行数据即可。
//两个29和34延时用的是reg,所以延时两个clk即可(理论上)
//=======================================================
always @ (posedge clk_i or negedge rst_n)begin
if(!rst_n)begin
Start_d1 <= 1'd0;
Start_d2 <= 1'd0;
Delay_Count <= 5'd31;
end
else begin
Start_d1 <= iStart;
if(Delay_Count == 5'd31)begin
if(Start_d1 ~^ iStart)
Start_d2 <= Start_d1;
else begin
Delay_Count <= 5'd0;
Start_d2 <= Start_d2;
end
end
else begin
Delay_Count <= Delay_Count + 1'b1;
Start_d2 <= Start_d2;
end
end
end
assign oStart = Start_d2;
endmodule