Bootstrap

FPGA图像处理仿真实验——sobel算子

1、原理

        sobel算子是一个离散的一阶差分算子,广泛应用于边缘检测等领域。算法的 应用原理比较简单,可以完成对水平方向和垂直方向的边缘检测。分别用图中的两个卷积模板对图像进行滑动窗口的卷积计算,将卷积模板和图像3*3窗口对应的数据相乘,相乘的结果相加得到GxGy,通过G=\sqrt{G_{x}^{2}+G_{y}^{2}}计算的得到G,再通过阈值比较得到二值图像。有时为了提高计算效率,通过G=\left | G_{x} \right |+\left | G_{y} \right |来近似得到G,本文采用开方的方法计算。

 

2、实现过程

        图像加边界和取3*3矩阵的操作和中值滤波和均值滤波算法一样,请参考之前发布的文章。在计算Gx和Gy的时候,对正值部分和负值部分分开计算,因为最后要进行乘方计算,所以可以不考虑正负号,直接将两部分的差值乘方就可以了。乘方操作调用viviado中乘法器的IP核完成,开方操作调用vivado中cordic IP核中Square Root完成。将开方后的数据与设定好的阈值进行比较,生成二值化图像。在最后数据输出对齐部分,在代码注释中给了详细的解释。

3、IP核配置

乘法器IP核

 开方运算IP核

 4、代码

sobel模块

module sobel(
    input clk,
    input rst_n,
    
    input per_frame_vsync,
    input per_frame_href,
    input per_frame_clken,
    input [7:0] per_img_Y,
    input [7:0] threshold,
    
    output post_frame_vsync,
    output post_frame_href,
    output post_frame_clken,
    output [7:0] post_img_data
    );
    
    
parameter [10:0] delay=11'd1310;    
wire matrix_frame_clken;
wire matrix_frame_href;
wire matrix_frame_vsync;
wire [7:0] matrix_p11;
wire [7:0] matrix_p12;
wire [7:0] matrix_p13;
wire [7:0] matrix_p21;
wire [7:0] matrix_p22;
wire [7:0] matrix_p23;
wire [7:0] matrix_p31;
wire [7:0] matrix_p32;
wire [7:0] matrix_p33;
reg [9:0] col_cnt; 
reg [10:0] sum0_gx;
reg [10:0] sum1_gx;
reg [10:0] sum0_gy;
reg [10:0] sum1_gy;
wire [10:0] sobel_gx;
wire [10:0] sobel_gy;
wire [21:0] sobel_gx_2;
wire [21:0] sobel_gy_2;
wire [23:0] sobel_g;
wire [15:0] sobel_sq;
wire [7:0] sobel_out;

generate_3_3 #(delay) u(
    .clk(clk),
    .rst_n(rst_n),
    
    .per_frame_vsync(per_frame_vsync),
    .per_frame_href(per_frame_href),
    .per_frame_clken(per_frame_clken),
    .per_img_Y(per_img_Y),
    .matrix_frame_vsync ( matrix_frame_vsync ),
    .matrix_frame_href  ( matrix_frame_href  ),
    .matrix_frame_clken ( matrix_frame_clken ),
    .matrix_p11         ( matrix_p11         ),
    .matrix_p12         ( matrix_p12         ),
    .matrix_p13         ( matrix_p13         ),
    .matrix_p21         ( matrix_p21         ),
    .matrix_p22         ( matrix_p22         ),
    .matrix_p23         ( matrix_p23         ),
    .matrix_p31         ( matrix_p31         ),
    .matrix_p32         ( matrix_p32         ),
    .matrix_p33         ( matrix_p33         )
);

//列计数
always @(negedge matrix_frame_clken or negedge rst_n) begin
    if(!rst_n)
        col_cnt<=10'd0;
    else begin
        if(col_cnt==640)
            col_cnt<=10'd1;
        else
            col_cnt<=col_cnt+1;
    end
end

//计算gx,gy,同时通过列计数器完成在图像左右两侧的补边界
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        sum0_gx<=10'd0;
        sum1_gx<=10'd0;
        sum0_gy<=10'd0;
        sum1_gy<=10'd0;
    end
    else if(col_cnt==10'd1) begin
        sum1_gx<=matrix_p12+(matrix_p22<<1)+matrix_p32;
        sum0_gx<=matrix_p13+(matrix_p23<<1)+matrix_p33;
        sum0_gy<=matrix_p12+(matrix_p12<<1)+matrix_p13;
        sum1_gy<=matrix_p32+(matrix_p32<<1)+matrix_p33;
    end
    else if(col_cnt==10'd640) begin
        sum1_gx<=matrix_p12+(matrix_p22<<1)+matrix_p32;
        sum0_gx<=matrix_p13+(matrix_p23<<1)+matrix_p33;
        sum0_gy<=matrix_p12+(matrix_p13<<1)+matrix_p13;
        sum1_gy<=matrix_p32+(matrix_p33<<1)+matrix_p33;
    end
    else begin
        sum1_gx<=matrix_p11+(matrix_p21<<1)+matrix_p31;
        sum0_gx<=matrix_p13+(matrix_p23<<1)+matrix_p33;
        sum0_gy<=matrix_p11+(matrix_p12<<1)+matrix_p13;
        sum1_gy<=matrix_p31+(matrix_p32<<1)+matrix_p33;
    end
end

assign sobel_gx = (sum0_gx>=sum1_gx)?(sum0_gx-sum1_gx):(sum1_gx-sum0_gx);
assign sobel_gy = (sum0_gy>=sum1_gy)?(sum0_gy-sum1_gy):(sum1_gy-sum0_gy);

mult_gen_0 m1 (
  .CLK(clk),  // input wire CLK
  .A(sobel_gx),      // input wire [8 : 0] A
  .B(sobel_gx),      // input wire [8 : 0] B
  .P(sobel_gx_2)      // output wire [17 : 0] P
);
mult_gen_0 m2 (
  .CLK(clk),  // input wire CLK
  .A(sobel_gy),      // input wire [8 : 0] A
  .B(sobel_gy),      // input wire [8 : 0] B
  .P(sobel_gy_2)      // output wire [17 : 0] P
);
assign sobel_g=sobel_gx_2+sobel_gy_2;
cordic_0 c1 (
  .aclk(clk),                                        // input wire aclk
  .s_axis_cartesian_tvalid(matrix_frame_href),  // input wire s_axis_cartesian_tvalid
  .s_axis_cartesian_tdata(sobel_g),    // input wire [23 : 0] s_axis_cartesian_tdata
  .m_axis_dout_tvalid(),            // output wire m_axis_dout_tvalid
  .m_axis_dout_tdata(sobel_sq)              // output wire [15 : 0] m_axis_dout_tdata
);
assign sobel_out=(sobel_sq>threshold)?255:0;
//延时8个像素时钟周期(16个系统时钟周期),因为对3*3矩阵内的数进行计算时,当每一行第二个像素送入时,我们计算的结果替代的是第一个像素的值
//所以输出的像素时钟比3*3矩阵输入的像素时钟延迟一个像素时钟周期(2个系统时钟周期),计算sum0_gx、sum1_gx、sum0_gy、sum1_gy时延迟了一个系统时钟周期,用乘法器IP核计算sobel_gx_2、sobel_gy_2(乘方)又延时了一个系统时钟周期
//用开方IP核计算开方结果时延时12个系统时钟周期,最后总共延时8个像素时钟周期(16个系统时钟周期),完成数据对齐
reg [15:0] post_frame_clken_dy;
reg [15:0] post_frame_href_dy;
reg [15:0] post_frame_vsync_dy;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        post_frame_clken_dy<=16'd0;
        post_frame_href_dy<=16'd0;
        post_frame_vsync_dy<=16'd0;
    end
    else begin
        post_frame_clken_dy<={post_frame_clken_dy[14:0],matrix_frame_clken};
        post_frame_href_dy<={post_frame_href_dy[14:0],matrix_frame_href};
        post_frame_vsync_dy<={post_frame_vsync_dy[14:0],matrix_frame_vsync};
    end
end
assign post_frame_clken=post_frame_clken_dy[15];
assign post_frame_href=post_frame_href_dy[15];
assign post_frame_vsync=post_frame_vsync_dy[15];
assign post_img_data=post_frame_href?sobel_out:0;


endmodule

 3*3矩阵生成模块

module generate_3_3(
    input				clk,  				//cmos video pixel clock
	input				rst_n,				//global reset

	//Image data prepred to be processd
	input				per_frame_vsync,	//Prepared Image data vsync valid signal
	input				per_frame_href,		//Prepared Image data href vaild  signal
	input				per_frame_clken,	//Prepared Image data output/capture enable clock
	input		[7:0]	per_img_Y,			//Prepared Image brightness input

	//Image data has been processd
	output				matrix_frame_vsync,	//Prepared Image data vsync valid signal
	output				matrix_frame_href,	//Prepared Image data href vaild  signal
	output				matrix_frame_clken,	//Prepared Image data output/capture enable clock	
	output	reg	[7:0]	matrix_p11, matrix_p12, matrix_p13,	
	output	reg	[7:0]	matrix_p21, matrix_p22, matrix_p23,
	output	reg	[7:0]	matrix_p31, matrix_p32, matrix_p33
    );
parameter [10:0] delay=11'd1310;     
wire [7:0] row1_data;
wire [7:0] row2_data;
wire [7:0] row3_data;
wire [7:0] row1_data1;
wire [7:0] row2_data1;
wire row2_rd_en;
wire row1_rd_en;
wire row2_wr_en;
wire row1_wr_en;
wire [9:0] data_count1,data_count2;
reg [8:0] row_cnt;
reg per_frame_href_delay;
wire [7:0] fifo_in;
reg [delay-1:0] per_frame_href_dl;
reg [delay-1:0] per_frame_clken_dl;
reg [delay-1:0] per_frame_vsync_dl;
wire per_frame_href_;
wire per_frame_clken_;
wire per_frame_vsync_;

assign row2_rd_en=(per_frame_clken||per_frame_clken_)&&(data_count2==10'd640);
assign row1_rd_en=(per_frame_clken||per_frame_clken_)&&(data_count1==10'd640);
assign row2_wr_en=(per_frame_clken||per_frame_clken_);
assign row1_wr_en=(per_frame_clken||per_frame_clken_)&&(data_count2==10'd640);
assign row3_data=(per_frame_href_==1&&per_frame_href==0)?row2_data:per_img_Y;  //在图像后面延时了一行行有效信号,在这一行把row2_data复制,实现在图像下方通过复制补边界的操作
assign row1_data=(row_cnt==1||row_cnt==0)?row2_data1:row1_data1;    //在行计数等于1之前,将row2_data1的值给row1_data,实现在图像上方通过复制补一行边界
assign row2_data=row2_data1;
assign fifo_in=(row_cnt==480)?0:per_img_Y;  //在图像最后一行的后面再向fifo中送入一行0,使fifo多工作一行,以便在图像的下方补一行边界

fifo_generator_0 u2 (
  .clk(clk),                // input wire clk
  .srst(!rst_n),              // input wire srst
  .din(fifo_in),                // input wire [7 : 0] din
  .wr_en(row2_wr_en),            // input wire wr_en
  .rd_en(row2_rd_en),            // input wire rd_en
  .dout(row2_data1),              // output wire [7 : 0] dout
  .full(),              // output wire full
  .empty(),            // output wire empty
  .data_count(data_count2)  // output wire [9 : 0] data_count
);

fifo_generator_0 u1 (
  .clk(clk),                // input wire clk
  .srst(!rst_n),              // input wire srst
  .din(row2_data1),                // input wire [7 : 0] din
  .wr_en(row1_wr_en),            // input wire wr_en
  .rd_en(row1_rd_en),            // input wire rd_en
  .dout(row1_data1),              // output wire [7 : 0] dout
  .full(),              // output wire full
  .empty(),            // output wire empty
  .data_count(data_count1)  // output wire [9 : 0] data_count
);


wire	     read_frame_href ;
wire	     read_frame_clken;
reg  [1:0]  per_frame_vsync_r;
reg  [1:0]  per_frame_href_r;
reg  [1:0]  per_frame_clken_r;
assign	read_frame_href    = per_frame_href_r[0] ;
assign	read_frame_clken   = per_frame_clken_r[0];
assign	matrix_frame_vsync = per_frame_vsync_r[1];
assign	matrix_frame_href  = per_frame_href_r[1] ;
assign	matrix_frame_clken = per_frame_clken_r[1];

//delay 2 clk
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) begin		
		per_frame_vsync_r <= 0;
		per_frame_href_r  <= 0;
		per_frame_clken_r <= 0;
	end
	else begin		
		per_frame_vsync_r <= { per_frame_vsync_r[0], per_frame_vsync_ };
		per_frame_href_r  <= { per_frame_href_r[0],  per_frame_href_  };
		per_frame_clken_r <= { per_frame_clken_r[0], per_frame_clken_ };
	end
end


always @(posedge clk or negedge rst_n) begin
    if(~rst_n)begin
        per_frame_href_dl<=0;
        per_frame_clken_dl<=0;
        per_frame_vsync_dl<=0;
    end
    else begin
        per_frame_href_dl<={per_frame_href_dl[delay-2:0],per_frame_href};
        per_frame_clken_dl<={per_frame_clken_dl[delay-2:0],per_frame_clken};
        per_frame_vsync_dl<={per_frame_vsync_dl[delay-2:0],per_frame_vsync};
    end
end
assign per_frame_href_=per_frame_href_dl[delay-1];
assign per_frame_clken_=per_frame_clken_dl[delay-1];
assign per_frame_vsync_=per_frame_vsync_dl[delay-1];

always @(posedge clk) begin
    per_frame_href_delay<=per_frame_href_;
end
//行计数
always @(posedge clk or negedge rst_n) begin
    if(!rst_n||~per_frame_vsync_)
        row_cnt<=9'd0;
    else if(~per_frame_href_delay&per_frame_href_) begin
        if(row_cnt==9'd480)
            row_cnt<=9'd1;
        else
            row_cnt<=row_cnt+1;
    end
    else
        row_cnt<=row_cnt;
end

//generate the 3X3 matrix 
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) begin		
		{matrix_p11, matrix_p12, matrix_p13} <= 24'h0;
		{matrix_p21, matrix_p22, matrix_p23} <= 24'h0;
		{matrix_p31, matrix_p32, matrix_p33} <= 24'h0;
	end
	else if(per_frame_href_||read_frame_href) begin
	    
		if(per_frame_clken_) begin			
			{matrix_p11, matrix_p12, matrix_p13} <= {matrix_p12, matrix_p13, row1_data};
            {matrix_p21, matrix_p22, matrix_p23} <= {matrix_p22, matrix_p23, row2_data};
            {matrix_p31, matrix_p32, matrix_p33} <= {matrix_p32, matrix_p33, row3_data};
		end
		
		else begin			
			{matrix_p11, matrix_p12, matrix_p13} <= {matrix_p11, matrix_p12, matrix_p13};
			{matrix_p21, matrix_p22, matrix_p23} <= {matrix_p21, matrix_p22, matrix_p23};
			{matrix_p31, matrix_p32, matrix_p33} <= {matrix_p31, matrix_p32, matrix_p33};
		end	
	end
	else begin		
		{matrix_p11, matrix_p12, matrix_p13} <= {matrix_p11, matrix_p12, matrix_p13};
        {matrix_p21, matrix_p22, matrix_p23} <= {matrix_p21, matrix_p22, matrix_p23};
        {matrix_p31, matrix_p32, matrix_p33} <= {matrix_p31, matrix_p32, matrix_p33};
	end
	

end


endmodule

 5、结果图

 

 

;