一、sobel算子边缘检测理论

sobel算子是广泛应用的微分算子之一,可以计算图像处理中的边缘检测,计算图像的灰度地图。在技术上,它是一个离散的一阶差分算子,用来计算图像亮度函数的一阶梯度之近似值。在图像的任何一点使用此算子,将会产生该点对应的梯度矢量或是其法矢量原理就是基于图像的卷积来实现在水平方向与垂直方向检测对于方向上的边缘。

这个实验在有学过上述图像矩阵中值运算的基础上来做并不难,把中值改为一个固定的矩阵相乘,这一部分也是卷积操作,与之前老师让学习的神经网络的卷积是相同的运算,也是打神经网络的基础吧。

对于源图像与奇数Sobel水平核Gx,竖直核Gy进行卷积可计算水平核竖直变换,当内核大小为3x3时:

sobel 边缘算子python_学习


原图中的作用点像素值通过卷积之后为:

sobel 边缘算子python_学习_02


可以化简成,结果相差不大:

sobel 边缘算子python_sed_03


二、MATLAB实现

sobel 边缘算子python_fpga开发_04


效果图:

sobel 边缘算子python_学习_05


三、FPGA实现

首先,sobel边缘检测依然是建立在灰度图输出Y分量的基础上:

sobel 边缘算子python_sobel 边缘算子python_06


Y分量输入3*3矩阵变化:

module matrix_3x3_8bit
(input   wire                clk                     ,
input   wire                rst_n                   ,
//input ---------------------------------------------
input   wire                din_vld                 ,
input   wire    [ 7:0]      din                     ,
//output --------------------------------------------
output  reg     [ 7:0]      matrix_11               ,
output  reg     [ 7:0]      matrix_12               ,
output  reg     [ 7:0]      matrix_13               ,
output  reg     [ 7:0]      matrix_21               ,
output  reg     [ 7:0]      matrix_22               ,
output  reg     [ 7:0]      matrix_23               ,
output  reg     [ 7:0]      matrix_31               ,
output  reg     [ 7:0]      matrix_32               ,
output  reg     [ 7:0]      matrix_33           
	);	
/*************************************************/
wire                        wr_en_1                 ;
wire                        wr_en_2                 ;
wire                        rd_en_1                 ;
wire                        rd_en_2                 ;
wire    [ 7:0]              q_1                     ;
wire    [ 7:0]              q_2                     ;
reg     [10:0]              cnt_col                 ;
wire                        add_cnt_col             ;
wire                        end_cnt_col             ;
reg     [10:0]              cnt_row                 ;
wire                        add_cnt_row             ;
wire                        end_cnt_row             ;

//fifO_showahead模式
FIFO_show_2048_8bit u1(
	.clock	( clk),
	.data		( din),
	.rdreq	( rd_en_1),
	.wrreq	( wr_en_1),
	.full		( ),	
	.q			( q_1)
			);

FIFO_show_2048_8bit u2(
	.clock	( clk),
	.data		( din),
	.rdreq	( rd_en_2),
	.wrreq	( wr_en_1),
	.full		( ),	
	.q			( q_2)
			);
		
//对数据进行行列的划分
//列	
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cnt_col <= 11'd0;
    else if(add_cnt_col) begin     //当使能时列计数器计数
        if(end_cnt_col)            //列计数器计数且记满时归零
            cnt_col <= 11'd0;
        else
            cnt_col <= cnt_col + 11'd1;
    end
end
		
assign add_cnt_col = din_vld;
assign end_cnt_col = add_cnt_col && cnt_col== COL-11'd1;

//行
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cnt_row <= 11'd0;
    else if(add_cnt_row) begin
        if(end_cnt_row)
            cnt_row <= 11'd0;
        else
            cnt_row <= cnt_row + 11'd1;
    end
end

assign add_cnt_row = end_cnt_col;
assign end_cnt_row = add_cnt_row && cnt_row== ROW-11'd1;

//fifo读写,第一个fifo从第一行读,但不写最后一行;第二个从第二行读,不写后两行,加上原输入的那一行,形成三行
assign wr_en_1 = (cnt_row < ROW - 11'd1) ? din_vld : 1'd0; //不写最后1行
assign rd_en_1 = (cnt_row > 11'd0      ) ? din_vld : 1'd0; //从第1行开始读
assign wr_en_2 = (cnt_row < ROW - 11'd2) ? din_vld : 1'd0; //不写最后2行
assign rd_en_2 = (cnt_row > 11'd1      ) ? din_vld : 1'd0; //从第2行开始读

//矩阵数据选取
assign row_1 = q_2;
assign row_2 = q_1;
assign row_3 = din;          //原输入是没有延时最先输出的、最大的数据

//打拍形成矩阵数据,延时1个clk
								//矩阵使用边界复制方法
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        {matrix_11, matrix_12, matrix_13} <= {8'd0, 8'd0, 8'd0};
        {matrix_21, matrix_22, matrix_23} <= {8'd0, 8'd0, 8'd0};
        {matrix_31, matrix_32, matrix_33} <= {8'd0, 8'd0, 8'd0};
    end
	//第一排
	 else if(cnt_row == 11'd0) begin
        if(cnt_col == 11'd0) begin          //第1个矩阵,清零的时候,矩阵全部为din数据
            {matrix_11, matrix_12, matrix_13} <= {row_3, row_3, row_3};
            {matrix_21, matrix_22, matrix_23} <= {row_3, row_3, row_3};
            {matrix_31, matrix_32, matrix_33} <= {row_3, row_3, row_3};
        end
        else begin                          //剩余矩阵
            {matrix_11, matrix_12, matrix_13} <= {matrix_12, matrix_13, row_3};
            {matrix_21, matrix_22, matrix_23} <= {matrix_22, matrix_23, row_3};
            {matrix_31, matrix_32, matrix_33} <= {matrix_32, matrix_33, row_3};
        end
    end
    //------------------------------------------------------------------------- 第2排矩阵
    else if(cnt_row == 11'd1) begin
        if(cnt_col == 11'd0) begin          //第1个矩阵,第二行有输入,矩阵由填充
            {matrix_11, matrix_12, matrix_13} <= {row_2, row_2, row_2};
            {matrix_21, matrix_22, matrix_23} <= {row_2, row_2, row_2};
            {matrix_31, matrix_32, matrix_33} <= {row_3, row_3, row_3};
        end
        else begin                          //剩余矩阵
            {matrix_11, matrix_12, matrix_13} <= {matrix_12, matrix_13, row_2};
            {matrix_21, matrix_22, matrix_23} <= {matrix_22, matrix_23, row_2};
            {matrix_31, matrix_32, matrix_33} <= {matrix_32, matrix_33, row_3};
        end
    end
    //------------------------------------------------------------------------- 剩余矩阵
    else begin
        if(cnt_col == 11'd0) begin          //第1个矩阵,第三行有输入,矩阵完成
            {matrix_11, matrix_12, matrix_13} <= {row_1, row_1, row_1};
            {matrix_21, matrix_22, matrix_23} <= {row_2, row_2, row_2};
            {matrix_31, matrix_32, matrix_33} <= {row_3, row_3, row_3};
        end
        else begin                          //剩余矩阵
            {matrix_11, matrix_12, matrix_13} <= {matrix_12, matrix_13, row_1};
            {matrix_21, matrix_22, matrix_23} <= {matrix_22, matrix_23, row_2};
            {matrix_31, matrix_32, matrix_33} <= {matrix_32, matrix_33, row_3};
        end
    end
end

endmodule

至此,得到sobel算子运算需要的矩阵,下一步进行处理:

//sobel处理

module sobel(
			input wire clk,
			input wire rst_n,
			 
			input wire Y_de,
			input wire Y_hsync,		
			input wire Y_vsync,
			input wire [ 7:0] Y_data,
			
			output wire sobel_de,
			output wire sobel_hsync,
			output wire sobel_vsync,
			output wire [15:0] sobel_data		
					);

		

//matrix_3x3 ----------------------------------------
		wire  [ 7:0]   matrix_11  ;
		wire  [ 7:0]   matrix_12  ;
		wire  [ 7:0]   matrix_13  ;
		wire  [ 7:0]   matrix_21  ;
		wire  [ 7:0]   matrix_22  ;
		wire  [ 7:0]   matrix_23  ;
		wire  [ 7:0]   matrix_31  ;
		wire  [ 7:0]   matrix_32  ;
		wire  [ 7:0]   matrix_33  ;

//sobel ---------------------------------------------作为行列的寄存,最终取值
reg     [ 9:0]              Gx1,Gx3,Gy1,Gy3,Gx,Gy   ;
reg     [10:0]              G                       ;		

//阈值
wire    [ 7:0]      value ;  
		
//信号同步 ----------------------------------------------
reg     [ 3:0]              Y_de_r                  ;
reg     [ 3:0]              Y_hsync_r               ;
reg     [ 3:0]              Y_vsync_r               ;



matrix_3x3_8bit u_matrix_3x3_8bit   
(
    .clk            (clk        ),
    .rst_n          (rst_n      ),
    .din_vld        (Y_de       ),
    .din            (Y_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  )
);


//sobel处理

/*
		-1 0 +1							 +1 +2 +1 							
 gx =	-2 0 +2  提取竖直分量边界  gy = 0  0  0										
		-1 0 +3						    -1 -2 -1						
		*/
                            //延时1个clk		
	always @ (negedge rst_n or posedge clk)
begin
	// Reset whenever the reset signal goes low, regardless of the clock
	if (!rst_n)
		begin
		  Gx1 <= 10'd0;
        Gx3 <= 10'd0;
        Gy1 <= 10'd0;
        Gy3 <= 10'd0; 
		
		end
	// If not resetting, update the register output on the clock's rising edge
	else
		begin
		  Gx1 <= matrix_11 + (matrix_21 << 1) + matrix_31;    //矩阵相乘,第一列为x1+2x2+x1,
        Gx3 <= matrix_13 + (matrix_23 << 1) + matrix_33;
        Gy1 <= matrix_11 + (matrix_12 << 1) + matrix_13;
        Gy3 <= matrix_31 + (matrix_32 << 1) + matrix_33;
		end
end

//      G = |Gx| + |Gy|
						       //延时1个clk
 //取  Gx 、Gy的绝对值
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        Gx <= 10'd0;
        Gy <= 10'd0;
    end
    else begin  //也可判断bit[7]来确定
        Gx <= (Gx1 > Gx3) ? (Gx1 - Gx3) : (Gx3 - Gx1);
        Gy <= (Gy1 > Gy3) ? (Gy1 - Gy3) : (Gy3 - Gy1);
    end 
end 

//得G
						       //延时1个clk
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        G <= 10'd0;
    end
    else begin
        G <= Gx + Gy;
    end
end

//设置判决阈值
assign value = 8'd120;

assign sobel_data = (G > value) ? 16'h0000 : 16'hFFFF;

//==    信号同步
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        Y_de_r    <= 4'b0;
        Y_hsync_r <= 4'b0;
        Y_vsync_r <= 4'b0;
    end
    else begin  
        Y_de_r    <= {Y_de_r[2:0],    Y_de};
        Y_hsync_r <= {Y_hsync_r[2:0], Y_hsync};
        Y_vsync_r <= {Y_vsync_r[2:0], Y_vsync};
    end
end
assign sobel_de    = Y_de_r[3];
assign sobel_hsync = Y_hsync_r[3];
assign sobel_vsync = Y_vsync_r[3];

endmodule

连接模块接口,接入顶层。

三、上板验证

sobel 边缘算子python_fpga开发_07


sobel 边缘算子python_硬件工程_08