基于Sobel算子的图像边缘检测
索贝尔算子(Sobeloperator)主要用于获得数字图像的一阶梯度,是一种离散性差分算子。它是prewitt算子的改进形式,改进之处在于sobel算子认为,邻域的像素对当前像素产生的影响不是等价的,所以距离不同的像素具有不同的权值,对算子结果产生的影响也不同。一般来说,距离越远,产生的影响越小。
在边缘检测中,常用的一种模板是Sobel 算子。Sobel 算子有两个,一个是检测水平边缘的 ;另一个是检测垂直边缘的 。与Prewitt算子相比,Sobel算子对于象素的位置的影响做了加权,可以降低边缘模糊程度,因此效果更好。
Sobel算子另一种形式是各向同性Sobel(Isotropic Sobel)算子,也有两个,一个是检测水平边缘的 ,另一个是检测垂直边缘的 。各向同性Sobel算子和普通Sobel算子相比,它的位置加权系数更为准确,在检测不同方向的边沿时梯度的幅度一致。将Sobel算子矩阵中的所有2改为根号2,就能得到各向同性Sobel的矩阵。
Sobel算子包含两组3x3的矩阵,分别为横向及纵向,将之与图像作平面卷积,即可分别得出横向及纵向的亮度差分近似值。如果以A代表原始图像,Gx及Gy分别代表经横向及纵向边缘检测的图像,其公式如下:
图像的每一个像素的横向及纵向梯度近似值可用以下的公式结合,来计算梯度的大小:
可大略的简化成:
根据该公式可以编写Verilog代码,难点主要在于3*3矩阵的构建。笔者这里是通过使用两个FIFO来进行矩阵构建的,具体代码如下:
Sobel_Edge.v
1 //************************************************************************** 2 // *** file name : Sobel_Edge.v 3 // *** version : 1.0 4 // *** Description : Sobel algorithm for edge detection 5 // *** Blogs : https://www.cnblogs.com/WenGalois123/ 6 // *** Author : Galois_V 7 // *** Date : 2022.08.18 8 // *** Changes : Initial 9 //************************************************************************** 10 `timescale 1ns/1ps 11 module Sobel_Edge 12 #( 13 parameter THRESHOLD = 40 14 ) 15 ( 16 input i_sys_clk , 17 input i_sys_rstn , 18 input i_frame_rst , 19 input i_edge_en , 20 input [9:0] i_s_stream_data , 21 input i_s_stream_valid , 22 output o_s_stream_ready , 23 output [25:0] o_m_stream_data , 24 output o_m_stream_valid , 25 input i_m_stream_ready 26 ); 27 28 wire w_rst_all ; 29 wire w_valid ; 30 wire [9:0] w_matrix_00 ; 31 wire [9:0] w_matrix_10 ; 32 wire [9:0] w_matrix_20 ; 33 wire [9:0] w_matrix_10T ; 34 wire [9:0] w_matrix_20T ; 35 wire w_line0_rd ; 36 wire w_line1_rd ; 37 wire w_line2_rd ; 38 wire w_empty ; 39 wire w_prog_full ; 40 41 reg r_1st_line_en ; 42 reg r_2rd_line_en ; 43 reg r_edge_en ; 44 reg [9:0] r_matrix_01 ; 45 reg [9:0] r_matrix_02 ; 46 reg [9:0] r_matrix_11 ; 47 reg [9:0] r_matrix_12 ; 48 reg [9:0] r_matrix_21 ; 49 reg [9:0] r_matrix_22 ; 50 51 assign w_rst_all = i_frame_rst & (~i_sys_rstn); 52 assign w_valid = o_s_stream_ready & i_s_stream_valid; 53 assign w_matrix_00 = i_s_stream_data; 54 55 always@(posedge i_sys_clk) 56 begin 57 r_edge_en <= i_edge_en; 58 end 59 /******************************************************************************\ 60 Generate 3 * 3 matrix 61 \******************************************************************************/ 62 assign w_line0_rd = w_valid; 63 assign w_line1_rd = w_valid & r_1st_line_en; 64 assign w_line2_rd = w_valid & r_2rd_line_en; 65 66 always@(posedge i_sys_clk) 67 begin 68 if(w_rst_all) 69 begin 70 r_1st_line_en <= 'd0; 71 r_2rd_line_en <= 'd0; 72 end 73 else if(w_valid & w_matrix_00[8]) 74 begin 75 r_1st_line_en <= 1'b1; 76 r_2rd_line_en <= r_1st_line_en; 77 end 78 end 79 80 fifo_2048x10 row1_fifo 81 ( 82 .clk (i_sys_clk ), 83 .srst (w_rst_all ), 84 .din (w_matrix_00 ), 85 .wr_en (w_line0_rd ), 86 .dout (w_matrix_10T ), 87 .rd_en (w_line1_rd ), 88 .full ( ), 89 .empty ( ) 90 ); 91 fifo_2048x10 row2_fifo 92 ( 93 .clk (i_sys_clk ), 94 .srst (w_rst_all ), 95 .din (w_matrix_10T ), 96 .wr_en (w_line1_rd ), 97 .dout (w_matrix_20T ), 98 .rd_en (w_line2_rd ), 99 .full ( ), 100 .empty ( ) 101 ); 102 assign w_matrix_10 = w_line1_rd ? w_matrix_10T : w_matrix_00; 103 assign w_matrix_20 = w_line2_rd ? w_matrix_20T : w_matrix_10; 104 105 always@(posedge i_sys_clk) 106 begin 107 if(w_valid) 108 begin 109 r_matrix_01 <= w_matrix_00; 110 r_matrix_11 <= w_matrix_10; 111 r_matrix_21 <= w_matrix_20; 112 113 r_matrix_02 <= r_matrix_01; 114 r_matrix_12 <= r_matrix_11; 115 r_matrix_22 <= r_matrix_21; 116 end 117 end 118 /******************************************************************************\ 119 Data processing 120 \******************************************************************************/ 121 wire [11:0] w_add_x0 ; 122 wire [11:0] w_add_x2 ; 123 wire [11:0] w_add_y0 ; 124 wire [11:0] w_add_y2 ; 125 wire [11:0] w_abs_x ; 126 wire [11:0] w_abs_y ; 127 wire [12:0] w_abs_add ; 128 wire [9:0] w_fifo_out ; 129 reg [9:0] r_fifo_din ; 130 reg r_fifo_wr ; 131 132 assign w_add_x0 = {1'b0,w_matrix_00} + {r_matrix_01,1'b0} + {1'b0,r_matrix_02}; //First row 133 assign w_add_x2 = {1'b0,w_matrix_20} + {r_matrix_21,1'b0} + {1'b0,r_matrix_22}; //Third row 134 assign w_add_y0 = {1'b0,w_matrix_00} + {w_matrix_10,1'b0} + {1'b0,w_matrix_20}; //First column 135 assign w_add_y2 = {1'b0,r_matrix_02} + {r_matrix_12,1'b0} + {1'b0,r_matrix_22}; //Third column 136 137 assign w_abs_x = w_add_x0 > w_add_x2 ? w_add_x0 - w_add_x2 : w_add_x2 - w_add_x0; 138 assign w_abs_y = w_add_y0 > w_add_y2 ? w_add_y0 - w_add_y2 : w_add_y2 - w_add_y0; 139 assign w_abs_add = w_abs_x + w_abs_y; 140 141 always@(posedge i_sys_clk) 142 begin 143 if(w_rst_all) 144 begin 145 r_fifo_din <= 'd0; 146 r_fifo_wr <= 'd0; 147 end 148 else if(r_edge_en) 149 begin 150 r_fifo_din <= (w_abs_add > THRESHOLD) ? {w_matrix_00[9:8],8'h00} : {w_matrix_00[9:8],8'hff}; 151 r_fifo_wr <= w_valid; 152 end 153 else 154 begin 155 r_fifo_din <= w_matrix_00; 156 r_fifo_wr <= w_valid; 157 end 158 end 159 160 fifo_2048x10_f2000 dout_fifo 161 ( 162 .clk (i_sys_clk ), 163 .srst (w_rst_all ), 164 .din (r_fifo_din ), 165 .wr_en (r_fifo_wr ), 166 .dout (w_fifo_out ), 167 .rd_en (i_m_stream_ready ), 168 .full ( ), 169 .empty (w_empty ), 170 .prog_full (w_prog_full ) 171 ); 172 assign o_m_stream_data = {w_fifo_out,w_fifo_out[7:0],w_fifo_out[7:0]}; 173 assign o_m_stream_valid = ~w_empty & i_m_stream_ready; 174 assign o_s_stream_ready = ~w_prog_full; 175 endmodule
笔者的工程是用OV5640的1280*720的分辨率做的实验,所以这里要保证图像数据的完整性,则FIFO的深度必须要大于或者等于一行有效像素(这里为1280)的数值。这里说明下,笔者输入的数据是有RGB转换成YUV的Y通道的8bit数据,其中高2位分别为:每帧图像的第一个像素点,每行像素的最后一点。该代码是可能会有点缺陷,每帧图像的前两行及前两列的数据可能会有一些问题。实际显示可能不是很明显,甚至可以忽略。
实验结果如下,第一张图为YUV中Y通道的显示结果,第二图为Sobel边缘检测后的结果。
Y通道数据图像显示:
Sobel处理后的图像显示:
实验成功