Canny边缘检测FPGA实现

canny原理 传输门
滤波可以用高斯滤波、中值滤波。

第一步, 使用高斯滤波器(或中值滤波),以平滑图像,滤除噪声。
卷积核及假设像素如下
卷积核

高斯滤波也分成3级流水线完成。
第一级流水线,计算每一行3个单元的加法,耗费一个时钟。
在这里插入图片描述
第二级流水线,计算3行的总和,耗费一个时钟。
在这里插入图片描述

第三级流水线,输出滤波结果,耗费一个时钟。
在这里插入图片描述

其实接下来的canny算法应该称作伪canny算法,因为在计算过程中做了几个近似。

第二步,计算图像中每个像素点的梯度强度和方向。
参照Sobel边缘检测计算梯度的公式,得到梯度的幅值。梯度方向的求取需要反三角函数,公式如下
在这里插入图片描述
1 对梯度方向的近似
Arctan(x) 对FPGA来说并不友好,在非极大值抑制的环节中只需要知道该像素的梯度方向在哪块区域就可以,并不需要判断出实际的角度。除非在非极大值抑制的环节需要对比较像素进行插值计算。本设计直接的区域做法就不会对比较像素进行插值计算了,直接比较该区域方向两端的梯度值。如水平、垂直、斜对角的两个像素,只要中心像素大于两端的两个像素,那么就保留中心像素。

原点定义在左上角
图 1 表示从左上角第一个像素定义的X、Y坐标。原点定义在左上角
在这里插入图片描述
图2 表示 4个梯度方向区域分布
180度分成四个区域,每个区域占45°,另外的180°都是相对应的
对于梯度方向的近似,通过以下公式来观察近似区域和实际区域的差距。
在这里插入图片描述
在第1区域,以 21.8代替22.5 ,它们只相差0.7 。
在这里插入图片描述

在第3区域,以68.2 代替 67.5,它们只相差 0.7 。
G_x 、G_y为Sobel算子对x y方向做的差分和。
梯度方向有以下规律。
在第一个区域里G_x 、G_y不论正负比较绝对值,符合G_x> G_y * 2.414 ,那么缩小一点1区域的范围,做一点近似符合G_x> G_y2.5。
在第三个区域里G_x 、G_y不论正负比较绝对值,符合G_y> G_x * 2.414 ,那么缩小一点3区域的范围,做一点近似符合G_y> G_x
2.5。
在第二个区域里,G_x 、G_y的绝对值不符合第一、三区域,但G_x 、G_y同号。
在第四个区域里,G_x 、G_y的绝对值不符合第一、三区域,但G_x 、G_y异号。
通过上述规律可以简单的通过G_x 、G_y的倍数关系以及符号去判断梯度方向。
1,通过在Sobel求G_x 、G_y 并判断它们的正负。
2,判断G_x 、G_y的倍数关系以及同异号情况。
3,通过第二步的结果去判断梯度方向落在哪块区域里。

第三步, 应用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测带来的杂散响应。
第一种做法
将梯度方向信息与梯度幅值合成,一起进入另一个由Shift Ram 构成的矩阵中,用中心像素梯度幅值去比较梯度方向两端像素的幅值,全部大于的标记该像素为真,否则标记为假。同时用中心像素的梯度幅值去与设定的高低阈值做比较,公式如下
H_T为高阈值,L_T为低阈值,G为梯度幅值。

在这里插入图片描述

第二种做法
采取近似将第三步与第四步融为一步
在双阈值和连通域中的近似
本来应该判断完极大值之后在进行双阈值的处理,而我为了不再用一个shift_ram构成3 X 3的矩阵,直接在进行非极大值抑制的那一步进行了双阈值处理,前提是认为一个像素的八连通域中有梯度值大于高阈值的像素,就认为这个像素有效(当然这个像素必须符合大于低阈值的要求)。

假定图像已经经历了灰度化和滤波处理,那么下一步就是取像素的梯度(幅值和方向),梯度算子用的是平常的sobel算子,梯度方向的求取按上文的近似判断。插一句嘴,fpga实现矩阵有几种方法,我用的是altera的Ip核 shift_ram
在这里插入图片描述
从图3中可知,缓存两行有两个tap,每一行有1024个单元,每个单元是8位。

在这里插入图片描述
图 4为Shift Ram 工作示意图,假定每行6个单元,缓存2行。每个时钟移位一次,Taps的输出和当前的输入构成矩阵的一列,将每个列向前移动,构成 3 X 3 的矩阵。

既然已经求出了像素梯度的幅值和方向了,为了实现第二个近似,还得去判断梯度幅值的高低情况,一个像素梯度经过开方器本来是10位,为了能够记录该像素的其他几个信息,再加4位表示4个方向,2位表示幅值的高低。这些信息必须和梯度的幅值融为一体能进入下面的环节,之后就要进行非极大值抑制的环节了,再次建立一个shift_ram构成一个3X3矩阵,一个时钟处理一个像素,根据梯度的值是否大于高阈值,是否大于低阈值且周围有高阈值的像素,以及该像素的方向,与当前矩阵的不同值相比较,去求得极大值。这种做法会有误判点,如果对边缘的要求很高的话,可以正常的双阈值处理。(也就是使极大值的点保持原值,非极大值全部置0,数据流在进入一个shift_ram,构成矩阵在去判断阈值大小)
实际效果如下,方法简单,结果也可以接受。

第四步,应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘。
借助第三步求得的像素真假与高低阈值比较的结果,将其合成再进入一个Shift Ram构成的矩阵,只判断像素为真的情况,在像素为真的条件下,考察Compare变量的值,像素为边缘点满足以下任意一个条件即可。
1、中心像素的Compare == 2 ’ b10 。
2、中心像素的Compare == 2’ b01 ,同时在其八连域中存在像素的Compare == 2’ b10 。

在这里插入图片描述在这里插入图片描述

在rom里存200*200的图像,canny处理后在LCD上显示(fpga的M9K真的太少了…)
在这里插入图片描述
这是摄像头视频的canny取边缘,VGA显示 。30fps

在这里插入图片描述
猜想:这是用灰度值进行处理,如果是取多颜色聚合体的边缘,如魔方的边缘是用HSV空间的HS 与 V 分量的融合算法效果会好点呢,或者直接用RGB、HSV 3维向量的梯度。
新人小白,如有错误,恳请指出。
第一个代码有两个近似处理,第二个代码只做了第一个近似(梯度方向),完整的双域值效果要好些。

module canny
(
	input			clk,
	input			rst_s,
	input    	[7:0] filter_out,
	output  reg [7:0]	canny_out,
	input			filter_de,
	input			filter_hs,
	input			filter_vs,
	output		canny_hs,
	output		canny_vs,
	output		canny_de
		
);
//双阈值的高低阈值
parameter THRESHOLD_LOW  = 10'd50;
parameter THRESHOLD_HIGH = 10'd100;

// shift_ram移位出口
wire [7:0] tap_0;
wire [7:0] tap_1;//输出端口

reg[9:0] Gx_1;//GX第一列计数
reg[9:0] Gx_3;
reg[9:0] Gy_1;
reg[9:0] Gy_3;

reg[10:0] Gx;//Gx Gy 做差分 求偏导
reg[10:0] Gy;

reg[20:0] sqrt_in;//计算梯度值的两个平方和
reg[9:0] sqrt_out;//开平方得到的梯度
reg[10:0] sqrt_rem;//开平方的余数
wire [20:0] sqrt_in_n;
wire [9:0] sqrt_out_n;
wire [10:0] sqrt_rem_n;
//对filter——de hs vs延迟
reg [5:0]hs_buf;
reg [5:0]vs_buf;
reg [5:0]de_buf;
wire sobel_de;
wire sobel_hs;
wire sobel_vs;

//9X9矩阵 sobel算子用
reg [7:0]  ma1_1;
reg [7:0]  ma1_2;
reg [7:0]  ma1_3;
reg [7:0]  ma2_1;
reg [7:0]  ma2_2;
reg [7:0]  ma2_3;
reg [7:0]  ma3_1;
reg [7:0]  ma3_2;
reg [7:0]  ma3_3;
//记录行上升沿,可以设置前两行全为8'h00,也可以随其自然
reg edge_de_a;
reg edge_de_b;
wire edge_de;
reg [9:0] row_cnt;
//-----非极大值抑制----
reg[1:0] sign;//Gx Gy  正 负
reg type; // Gx Gy 异号  同号

reg  path_one;
wire path_two;
reg  path_thr;
wire path_fou;//四个梯度方向
wire start;//判断,;xy轴方向有没有选中
reg [15:0] gra_path;//梯度幅值+方向+高低阈值状态
//--非极大值的ram出口
wire [15:0] tap_2;
wire [15:0] tap_3;
// 9x9矩阵,非极大值抑制用
reg [15:0]  max1_1;
reg [15:0]  max1_2;
reg [15:0]  max1_3;
reg [15:0]  max2_1;
reg [15:0]  max2_2;
reg [15:0]  max2_3;
reg [15:0]  max3_1;
reg [15:0]  max3_2;
reg [15:0]  max3_3;
//对sobel de hs vs 延迟3拍
reg [2:0]de_buf_n;
reg [2:0]hs_buf_n; 
reg [2:0]vs_buf_n;
//case四个方向选择
wire [3:0] path_se;
wire search;//八连通域判断是否有大于高阈值的点
 

shift_ram	shift_ram_inst (
	.aclr ( ~filter_vs),
	.clock ( clk),
	.clken ( filter_de),
	.shiftin ( filter_out ),//输入端口  第三行
	.shiftout (),//和tap——1一样的输出
	.taps0x ( tap_0 ),//第二行
	.taps1x ( tap_1 )//第一行
	);
	

//对矩阵第一行进行移位赋值
always @ (posedge clk or negedge rst_s)
begin
	if (!rst_s)
	{ma1_1,ma1_2,ma1_3} <= 24'd0;
	else if (filter_de)
	{ma1_1,ma1_2,ma1_3} <= {ma1_2,ma1_3,tap_1};
	else
	{ma1_1,ma1_2,ma1_3} <= {ma1_1,ma1_2,ma1_3};
end
//对矩阵第二行进行移位赋值
always @ (posedge clk or negedge rst_s)
begin
	if (!rst_s)
	{ma2_1,ma2_2,ma2_3} <= 24'd0;
	else if (filter_de)
	{ma2_1,ma2_2,ma2_3} <= {ma2_2,ma2_3,tap_0};
	else
	{ma2_1,ma2_2,ma2_3} <= {ma2_1,ma2_2,ma2_3};
end

//对矩阵第3行进行移位赋值
always @ (posedge clk or negedge rst_s)
begin
	if (!rst_s)
	{ma3_1,ma3_2,ma3_3} <= 24'd0;
	else if (filter_de)
	{ma3_1,ma3_2,ma3_3} <= {ma3_2,ma3_3,filter_out};
	else
	{ma3_1,ma3_2,ma3_3} <= {ma3_1,ma3_2,ma3_3};
end


//----------------Sobel Parameter--------------------------------------------
//      Gx             Gy				 Pixel
// [+1  0  -1]   [+1  +2  +1]   [ma1_1  ma1_2  ma1_3]
// [+2  0  -2]   [ 0   0   0]   [ma2_1  ma2_2  ma2_3]
// [+1  0  -1]   [-1  -2  -1]   [ma3_1  ma3_2  ma3_3]
//-------------------------------------------------------------
//将GX两列Gy 2列行先加  第一级流水线     
always @ (posedge clk or negedge rst_s)
begin
	if(!rst_s)
		begin
		Gx_1 <= 10'd0;
		Gx_3 <= 10'd0;
		end
	else
		begin
		Gx_1 <= {2'b00,ma1_1} + {1'b0,ma2_1,1'b0} +{2'b0,ma3_1};
		Gx_3 <= {2'b00,ma1_3} + {1'b0,ma2_3,1'b0} +{2'b0,ma3_3};
		end
end

always @ (posedge clk or negedge rst_s)
begin
	if(!rst_s)
		begin
		Gy_1 <= 10'd0;
		Gy_3 <= 10'd0;
		end
	else
		begin
		Gy_1 <= {2'b00,ma1_1} + {1'b0,ma1_2,1'b0} +{2'b0,ma1_3};
		Gy_3 <= {2'b00,ma3_1} + {1'b0,ma3_2,1'b0} +{2'b0,ma3_3};
		end
end
//---Gx1 Gx3;Gy1 Gy3  做差  差分 xy方向的偏导  再判断GX GY的正负 第二级    
always @(posedge clk or negedge rst_s)
begin
	if(!rst_s)
		begin
		Gx <= 11'd0;
		Gy <= 11'd0;
		sign <= 2'b00;
		end
	else
		begin
		Gx <= (Gx_1 >= Gx_3)? Gx_1 - Gx_3 : Gx_3 - Gx_1;
		Gy <= (Gy_1 >= Gy_3)? Gy_1 - Gy_3 : Gy_3 - Gy_1;
		sign[0] <= (Gx_1 >= Gx_3)? 1'b1 : 1'b0;//判断GX Gy 正负,1 正 0 负
		sign[1] <= (Gy_1 >= Gy_3)? 1'b1 : 1'b0;
		end
end
//第三级 平方和  + GX、GY异同号?+  GX GY 大小级别 + 梯度方向 
//求 Gx^2 Gy^2,提供给开方Ip计算梯度, //梯度的方向就是函数f(x,y)在这点增长最快的方向,梯度的模为方向导数的最大值。
// 梯度的摸 = (Gx^2 + Gy^2)开平方
always @(posedge clk or negedge rst_s)
begin
	if(!rst_s)
		sqrt_in <= 21'd0;
	else
		sqrt_in <= Gx*Gx + Gy*Gy;
end
assign sqrt_in_n = sqrt_in;

//对Gx Gy  正负的情况做分类  两类  异号 1 同号 0
always @ (posedge clk or negedge rst_s)
begin
	if(!rst_s)
	type <= 1'b0;
	else if (sign[0]^sign[1])
		type <= 1'b1;
	else
		type <= 1'b0;
end

// 对 GX GY 大小级别做判断,也就是 GX > GY*2.5 ? Gy > GX*2.5?
// 符合 GX > GY*2.5 必定为x轴方向
always @ (posedge clk or negedge rst_s)
begin
	if (!rst_s)
		path_one <= 1'b0;
	else if(Gx > (Gy + Gy + Gy[10:1]))
		path_one <= 1'b1;
//这里有个失误点,本来Gx Gy是10位,但对于GY*2.5 超过1023时,只取低10位,进位消失,该if成立,就会出现XY轴同时为1
	else
		path_one <= 1'b0;
	
end
// 符合 Gy > Gx*2.5 必定为y轴方向
always @ (posedge clk or negedge rst_s)
begin
	if (!rst_s)
		path_thr <= 1'b0;
	else if(Gy > (Gx + Gx + Gx[10:1]))
		path_thr <= 1'b1;
	else
		path_thr <= 1'b0;
	
end

//  判断完 x y 轴方向 再判断两个对角方向
// 由于坐标轴原点在左上角 ------->  x
//			     |
//			     |
//			    y|
// 同号 为 \   异号为  /  (当然得在 X Y 轴 都不是的情况下)
assign start = (path_one | path_thr)? 1'b0 : 1'b1;
assign path_two = (start) ? type : 1'b0;
assign path_fou = (start) ? ~type: 1'b0;		
				
								
//开方IP组合逻辑,花的时间很少,送进去马上就得出数据,在下一个时钟赋给输出
sqrt	sqrt_inst (
	.radical ( sqrt_in_n ),
	.q ( sqrt_out_n ),
	.remainder ( sqrt_rem_n )
	);
//第四级
//开方得到梯度,再加上4个方向gra_path[13:10]
//提前对梯度进行双域值判断,以防进行非极大值抑制后又要来一个3X3的shift_ram
//意图在进行非极大值抑制的时候进行八连通域分析
//其实是伪双阈值,不再去判断大于高阈值的是不是极大值点,只要中间像素(小于高,大于低)周围
//有大于高阈值的点,就认为中间像素有效
//gra_path[15:14]高低阈值,gra_path[13:10]四个方向,gra_path[9:0]梯度幅值
always @(posedge clk or negedge rst_s)
begin
	if(!rst_s)
		gra_path <= 16'd0;
	else if (sqrt_out_n > THRESHOLD_HIGH)
		gra_path <= {1'b1,1'b0,path_fou,path_thr,path_two,path_one,sqrt_out_n};
	else if (sqrt_out_n > THRESHOLD_LOW)
		gra_path <= {1'b0,1'b1,path_fou,path_thr,path_two,path_one,sqrt_out_n};
	else
		gra_path <= 16'd0;
end
//对 hs vs de 进行适当的延迟,匹配VGA的时钟
always@(posedge clk or negedge rst_s)
begin
  if (!rst_s)
  begin
    hs_buf <= 6'd0 ;
    vs_buf <= 6'd0 ;
    de_buf <= 6'd0 ;
  end
  else
  begin
	   hs_buf <= {hs_buf[4:0], filter_hs} ;
	   vs_buf <= {vs_buf[4:0], filter_vs} ;
	   de_buf <= {de_buf[4:0], filter_de} ;
  end
end

assign sobel_hs = hs_buf[5] ;
assign sobel_vs = vs_buf[5] ;
assign sobel_de = de_buf[5] ;

//在计算一个像素的梯度和方向后,开始非极大值抑制

shift_ram_maximum 	shift_ram_maximum_m0 (
	.aclr (~sobel_vs),
	.clock ( clk),
	.clken ( sobel_de),
	.shiftin ( gra_path ),//输入端口  第三行
	.shiftout (),//和tap——1一样的输出
	.taps0x ( tap_2 ),//第二行
	.taps1x ( tap_3 )//第一行
	);

//对矩阵第一行进行移位赋值
always @ (posedge clk or negedge rst_s)
begin
	if (!rst_s)
	{max1_1,max1_2,max1_3} <= 48'd0;
	else if (sobel_de)
	{max1_1,max1_2,max1_3} <= {max1_2,max1_3,tap_3};
	else
	{max1_1,max1_2,max1_3} <= {max1_1,max1_2,max1_3};
end
//对矩阵第二行进行移位赋值
always @ (posedge clk or negedge rst_s)
begin
	if (!rst_s)
	{max2_1,max2_2,max2_3} <= 48'd0;
	else if (sobel_de)
	{max2_1,max2_2,max2_3} <= {max2_2,max2_3,tap_2};
	else
	{max2_1,max2_2,max2_3} <= {max2_1,max2_2,max2_3};
end

//对矩阵第3行进行移位赋值
always @ (posedge clk or negedge rst_s)
begin
	if (!rst_s)
	{max3_1,max3_2,max3_3} <= 48'd0;
	else if (sobel_de)
	{max3_1,max3_2,max3_3} <= {max3_2,max3_3,gra_path};
	else
	{max3_1,max3_2,max3_3} <= {max3_1,max3_2,max3_3};
end
//进行非极大值抑制

assign path_se = max2_2[13:10];//对于目标像素的梯度方向进行分配
assign search = max1_1[15] | max1_2[15] | max1_3[15] | max2_1[15] | max2_2[15] | max2_3[15] | max3_1[15] | max3_2[15] | max3_3[15];//搜寻目标像素周边是否包含梯度值大于高阈值的点,当然自身是高于的话,那么肯定为1  
always @ (posedge clk or negedge rst_s)
begin
	if(!rst_s)
	canny_out <= 8'd0;
	else if (search &(row_cnt > 10'd5))
	begin
	case (path_se)
		
4'b0001:   
canny_out <=((max2_2[9:0]>= max2_1[9:0])&(max2_2[9:0]>= max2_3[9:0]))?8'hff:8'h00;
4'b0010:  
canny_out <=((max2_2[9:0]>= max1_3[9:0])&(max2_2[9:0]>= max3_1[9:0]))?8'hff:8'h00;	
4'b0100: 	
canny_out <=((max2_2[9:0]>= max1_2[9:0])&(max2_2[9:0]>= max3_2[9:0]))?8'hff:8'h00;	
4'b1000:			
canny_out <=((max2_2[9:0]>= max1_1[9:0])&(max2_2[9:0]>= max3_3[9:0]))?8'hff:8'h00;default:
		canny_out <= 8'h00;
	endcase
	end
	else
		canny_out <= 8'h00;
end

always@(posedge clk or negedge rst_s)
begin
  if (!rst_s)
  begin
    hs_buf_n <= 3'd0 ;
    vs_buf_n <= 3'd0 ;
    de_buf_n <= 3'd0 ;
  end
  else
  begin
	   hs_buf_n <= {hs_buf_n[1:0], sobel_hs} ;
	   vs_buf_n <= {vs_buf_n[1:0], sobel_vs} ;
	   de_buf_n <= {de_buf_n[1:0], sobel_de} ;
  end
end

assign canny_hs = hs_buf_n[2] ;
assign canny_vs = vs_buf_n[2] ;
assign canny_de = de_buf_n[2] ;


//检测行标志信号上升沿
always @ (posedge clk or negedge rst_s)
begin
		if (!rst_s)
			begin
			edge_de_a <= 1'b0;
			edge_de_b <= 1'b0;
			end
		else
			begin
			edge_de_a <= filter_de;
			edge_de_b <= edge_de_a;
			end
end
assign edge_de = edge_de_a & ~edge_de_b;
//记录行数,对前4行进行特殊处理
always @ (posedge clk or negedge rst_s)
begin
		if (!rst_s)
			row_cnt <= 10'd0;
		else if(~canny_vs)
			row_cnt <= 10'd0;
		else if(edge_de)
			row_cnt <= row_cnt + 1'b1;
		else
			row_cnt <= row_cnt;
end




endmodule



第二个,取消了双域值近似

module canny
(
	input			clk,
	input			rst_s,
	input    	[7:0] filter_out,
	output  reg [7:0]	canny_out,
	input			filter_de,
	input			filter_hs,
	input			filter_vs,
	output		canny_hs,
	output		canny_vs,
	output		canny_de
		
);
//双阈值的高低阈值
parameter THRESHOLD_LOW  = 10'd50;
parameter THRESHOLD_HIGH = 10'd100;

// shift_ram移位出口
wire [7:0] tap_0;
wire [7:0] tap_1;//输出端口

reg[9:0] Gx_1;//GX第一列计数
reg[9:0] Gx_3;
reg[9:0] Gy_1;
reg[9:0] Gy_3;

reg[10:0] Gx;//Gx Gy 做差分 求偏导
reg[10:0] Gy;

reg[20:0] sqrt_in;//计算梯度值的两个平方和
reg[9:0] sqrt_out;//开平方得到的梯度
reg[10:0] sqrt_rem;//开平方的余数
wire [20:0] sqrt_in_n;
wire [9:0] sqrt_out_n;
wire [10:0] sqrt_rem_n;
//对filter——de hs vs延迟
reg [5:0]hs_buf;
reg [5:0]vs_buf;
reg [5:0]de_buf;
wire sobel_de;
wire sobel_hs;
wire sobel_vs;

//9X9矩阵 sobel算子用
reg [7:0]  ma1_1;
reg [7:0]  ma1_2;
reg [7:0]  ma1_3;
reg [7:0]  ma2_1;
reg [7:0]  ma2_2;
reg [7:0]  ma2_3;
reg [7:0]  ma3_1;
reg [7:0]  ma3_2;
reg [7:0]  ma3_3;
//记录行上升沿,可以设置前两行全为8'h00,也可以随其自然
reg edge_de_a;
reg edge_de_b;
wire edge_de;
reg [9:0] row_cnt;
//-----非极大值抑制----
reg[1:0] sign;//Gx Gy  正 负
reg type; // Gx Gy 异号  同号

reg  path_one;
wire path_two;
reg  path_thr;
wire path_fou;//四个梯度方向
wire start;//判断,;xy轴方向有没有旋中
reg [15:0] gra_path;//梯度幅值+方向+高低阈值状态
//--非极大值的ram出口
wire [15:0] tap_2;
wire [15:0] tap_3;
// 9x9矩阵,非极大值抑制用
reg [15:0]  max1_1;
reg [15:0]  max1_2;
reg [15:0]  max1_3;
reg [15:0]  max2_1;
reg [15:0]  max2_2;
reg [15:0]  max2_3;
reg [15:0]  max3_1;
reg [15:0]  max3_2;
reg [15:0]  max3_3;
//对sobel de hs vs 延迟3拍
reg [3:0]de_buf_n;
reg [3:0]hs_buf_n; 
reg [3:0]vs_buf_n;
wire max_de;
wire max_vs;
wire max_hs;
reg [1:0] max_g;
//case四个方向选择
wire [3:0] path_se;
wire high_low;

//--双阈值出口
wire [1:0] tap_4;
wire [1:0] tap_5;
// 9x9矩阵,非极大值抑制用
reg [1:0]  mag1_1;
reg [1:0]  mag1_2;
reg [1:0]  mag1_3;
reg [1:0]  mag2_1;
reg [1:0]  mag2_2;
reg [1:0]  mag2_3;
reg [1:0]  mag3_1;
reg [1:0]  mag3_2;
reg [1:0]  mag3_3;
//对max de hs vs 延迟3拍
reg [3:0]de_buf_m;
reg [3:0]hs_buf_m; 
reg [3:0]vs_buf_m;
wire search;


 

shift_ram	shift_ram_inst (
	.aclr ( ~filter_vs),
	.clock ( clk),
	.clken ( filter_de),
	.shiftin ( filter_out ),//输入端口  第三行
	.shiftout (),//和tap——1一样的输出
	.taps0x ( tap_0 ),//第二行
	.taps1x ( tap_1 )//第一行
	);
	

//对矩阵第一行进行移位赋值
always @ (posedge clk or negedge rst_s)
begin
	if (!rst_s)
	{ma1_1,ma1_2,ma1_3} <= 24'd0;
	else if (filter_de)
	{ma1_1,ma1_2,ma1_3} <= {ma1_2,ma1_3,tap_1};
	else
	{ma1_1,ma1_2,ma1_3} <= {ma1_1,ma1_2,ma1_3};
end
//对矩阵第二行进行移位赋值
always @ (posedge clk or negedge rst_s)
begin
	if (!rst_s)
	{ma2_1,ma2_2,ma2_3} <= 24'd0;
	else if (filter_de)
	{ma2_1,ma2_2,ma2_3} <= {ma2_2,ma2_3,tap_0};
	else
	{ma2_1,ma2_2,ma2_3} <= {ma2_1,ma2_2,ma2_3};
end

//对矩阵第3行进行移位赋值
always @ (posedge clk or negedge rst_s)
begin
	if (!rst_s)
	{ma3_1,ma3_2,ma3_3} <= 24'd0;
	else if (filter_de)
	{ma3_1,ma3_2,ma3_3} <= {ma3_2,ma3_3,filter_out};
	else
	{ma3_1,ma3_2,ma3_3} <= {ma3_1,ma3_2,ma3_3};
end


//----------------Sobel Parameter--------------------------------------------
//      Gx             Gy				 Pixel
// [+1  0  -1]   [+1  +2  +1]   [ma1_1  ma1_2  ma1_3]
// [+2  0  -2]   [ 0   0   0]   [ma2_1  ma2_2  ma2_3]
// [+1  0  -1]   [-1  -2  -1]   [ma3_1  ma3_2  ma3_3]
//-------------------------------------------------------------
//将GX两列Gy 2列行先加  第一级流水线     
always @ (posedge clk or negedge rst_s)
begin
	if(!rst_s)
		begin
		Gx_1 <= 10'd0;
		Gx_3 <= 10'd0;
		end
	else
		begin
		Gx_1 <= {2'b00,ma1_1} + {1'b0,ma2_1,1'b0} +{2'b0,ma3_1};
		Gx_3 <= {2'b00,ma1_3} + {1'b0,ma2_3,1'b0} +{2'b0,ma3_3};
		end
end

always @ (posedge clk or negedge rst_s)
begin
	if(!rst_s)
		begin
		Gy_1 <= 10'd0;
		Gy_3 <= 10'd0;
		end
	else
		begin
		Gy_1 <= {2'b00,ma1_1} + {1'b0,ma1_2,1'b0} +{2'b0,ma1_3};
		Gy_3 <= {2'b00,ma3_1} + {1'b0,ma3_2,1'b0} +{2'b0,ma3_3};
		end
end
//---Gx1 Gx3;Gy1 Gy3  做差  差分 xy方向的偏导  再判断GX GY的正负 第二级    
always @(posedge clk or negedge rst_s)
begin
	if(!rst_s)
		begin
		Gx <= 11'd0;
		Gy <= 11'd0;
		sign <= 2'b00;
		end
	else
		begin
		Gx <= (Gx_1 >= Gx_3)? Gx_1 - Gx_3 : Gx_3 - Gx_1;
		Gy <= (Gy_1 >= Gy_3)? Gy_1 - Gy_3 : Gy_3 - Gy_1;
		sign[0] <= (Gx_1 >= Gx_3)? 1'b1 : 1'b0;//判断GX Gy 正负,1 正 0 负
		sign[1] <= (Gy_1 >= Gy_3)? 1'b1 : 1'b0;
		end
end
//第三级 平方和  + GX、GY异同号?+  GX GY 大小级别 + 梯度方向 
//求 Gx^2 Gy^2,提供给开方Ip计算梯度, //梯度的方向就是函数f(x,y)在这点增长最快的方向,梯度的模为方向导数的最大值。
// 梯度的摸 = (Gx^2 + Gy^2)开平方
always @(posedge clk or negedge rst_s)
begin
	if(!rst_s)
		sqrt_in <= 21'd0;
	else
		sqrt_in <= Gx*Gx + Gy*Gy;
end
assign sqrt_in_n = sqrt_in;

//对Gx Gy  正负的情况做分类  两类  异号 1 同号 0
always @ (posedge clk or negedge rst_s)
begin
	if(!rst_s)
	type <= 1'b0;
	else if (sign[0]^sign[1])
		type <= 1'b1;
	else
		type <= 1'b0;
end

// 对 GX GY 大小级别做判断,也就是 GX > GY*2.5 ? Gy > GX*2.5?
// 符合 GX > GY*2.5 必定为x轴方向
always @ (posedge clk or negedge rst_s)
begin
	if (!rst_s)
		path_one <= 1'b0;
	else if(Gx > (Gy + Gy + Gy[10:1]))
		path_one <= 1'b1;
//这里有个失误点,本来Gx Gy是10位,但对于GY*2.5 超过1023时,只取低10位,进位消失,该if成立,就会出现XY轴同时为1
	else
		path_one <= 1'b0;
	
end
// 符合 Gy > Gx*2.5 必定为y轴方向
always @ (posedge clk or negedge rst_s)
begin
	if (!rst_s)
		path_thr <= 1'b0;
	else if(Gy > (Gx + Gx + Gx[10:1]))
		path_thr <= 1'b1;
	else
		path_thr <= 1'b0;
	
end

//  判断完 x y 轴方向 再判断两个对角方向
// 由于坐标轴原点在左上角 ------->  x
//			     |
//			     |
//			    y|
// 同号 为 \   异号为  /  (当然得在 X Y 轴 都不是的情况下)
assign start = (path_one | path_thr)? 1'b0 : 1'b1;
assign path_two = (start) ? type : 1'b0;
assign path_fou = (start) ? ~type: 1'b0;		
				
								
//开方IP组合逻辑,花的时间很少,送进去马上就得出数据,在下一个时钟赋给输出
sqrt	sqrt_inst (
	.radical ( sqrt_in_n ),
	.q ( sqrt_out_n ),
	.remainder ( sqrt_rem_n )
	);
//第四级
//开方得到梯度,再加上4个方向gra_path[13:10]
//gra_path[15:14]高低阈值,gra_path[13:10]四个方向,gra_path[9:0]梯度幅值
always @(posedge clk or negedge rst_s)
begin
	if(!rst_s)
		gra_path <= 16'd0;
	else if (sqrt_out_n > THRESHOLD_HIGH)
		gra_path <= {1'b1,1'b0,path_fou,path_thr,path_two,path_one,sqrt_out_n};
	else if (sqrt_out_n > THRESHOLD_LOW)
		gra_path <= {1'b0,1'b1,path_fou,path_thr,path_two,path_one,sqrt_out_n};
	else
		gra_path <= 16'd0;
end
//对 hs vs de 进行适当的延迟,匹配VGA的时钟
always@(posedge clk or negedge rst_s)
begin
  if (!rst_s)
  begin
    hs_buf <= 6'd0 ;
    vs_buf <= 6'd0 ;
    de_buf <= 6'd0 ;
  end
  else
  begin
	   hs_buf <= {hs_buf[4:0], filter_hs} ;
	   vs_buf <= {vs_buf[4:0], filter_vs} ;
	   de_buf <= {de_buf[4:0], filter_de} ;
  end
end

assign sobel_hs = hs_buf[5] ;
assign sobel_vs = vs_buf[5] ;
assign sobel_de = de_buf[5] ;

//在计算一个像素的梯度和方向后,开始非极大值抑制

shift_ram_maximum 	shift_ram_maximum_m0 (
	.aclr (~sobel_vs),
	.clock ( clk),
	.clken ( sobel_de),
	.shiftin ( gra_path ),//输入端口  第三行
	.shiftout (),//和tap——1一样的输出
	.taps0x ( tap_2 ),//第二行
	.taps1x ( tap_3 )//第一行
	);

//对矩阵第一行进行移位赋值
always @ (posedge clk or negedge rst_s)
begin
	if (!rst_s)
	{max1_1,max1_2,max1_3} <= 48'd0;
	else if (sobel_de)
	{max1_1,max1_2,max1_3} <= {max1_2,max1_3,tap_3};
	else
	{max1_1,max1_2,max1_3} <= {max1_1,max1_2,max1_3};
end
//对矩阵第二行进行移位赋值
always @ (posedge clk or negedge rst_s)
begin
	if (!rst_s)
	{max2_1,max2_2,max2_3} <= 48'd0;
	else if (sobel_de)
	{max2_1,max2_2,max2_3} <= {max2_2,max2_3,tap_2};
	else
	{max2_1,max2_2,max2_3} <= {max2_1,max2_2,max2_3};
end

//对矩阵第3行进行移位赋值
always @ (posedge clk or negedge rst_s)
begin
	if (!rst_s)
	{max3_1,max3_2,max3_3} <= 48'd0;
	else if (sobel_de)
	{max3_1,max3_2,max3_3} <= {max3_2,max3_3,gra_path};
	else
	{max3_1,max3_2,max3_3} <= {max3_1,max3_2,max3_3};
end
//进行非极大值抑制

assign path_se = max2_2[13:10];//对于目标像素的梯度方向进行分配
 
always @ (posedge clk or negedge rst_s)
begin
	if(!rst_s)
	max_g <= 2'd0;
	else if  (row_cnt > 10'd5)
	begin
	case (path_se)
		
4'b0001:   
max_g <=((max2_2[9:0]> max2_1[9:0])&(max2_2[9:0]> max2_3[9:0]))?{max2_2[15:14]}:2'd0;
4'b0010:
max_g <=((max2_2[9:0]> max1_3[9:0])&(max2_2[9:0]> max3_1[9:0]))?{max2_2[15:14]}:2'd0;
4'b0100: 	
max_g <=((max2_2[9:0]> max1_2[9:0])&(max2_2[9:0]> max3_2[9:0]))?{max2_2[15:14]}:2'd0;
4'b1000:			
max_g <=((max2_2[9:0]> max1_1[9:0])&(max2_2[9:0]> max3_3[9:0]))?{max2_2[15:14]}:2'd0;
default:
		max_g <= 2'd0;
	endcase
	end
	else
		max_g <= 2'd0;
end

always@(posedge clk or negedge rst_s)
begin
  if (!rst_s)
  begin
    hs_buf_n <= 4'd0 ;
    vs_buf_n <= 4'd0 ;
    de_buf_n <= 4'd0 ;
  end
  else
  begin
	   hs_buf_n <= {hs_buf_n[2:0], sobel_hs} ;
	   vs_buf_n <= {vs_buf_n[2:0], sobel_vs} ;
	   de_buf_n <= {de_buf_n[2:0], sobel_de} ;
  end
end

assign max_hs = hs_buf_n[3] ;
assign max_vs = vs_buf_n[3] ;
assign max_de = de_buf_n[3] ;


//检测行标志信号上升沿
always @ (posedge clk or negedge rst_s)
begin
		if (!rst_s)
			begin
			edge_de_a <= 1'b0;
			edge_de_b <= 1'b0;
			end
		else
			begin
			edge_de_a <= filter_de;
			edge_de_b <= edge_de_a;
			end
end
assign edge_de = edge_de_a & ~edge_de_b;
//记录行数,对前4行进行特殊处理
always @ (posedge clk or negedge rst_s)
begin
		if (!rst_s)
			row_cnt <= 10'd0;
		else if(~canny_vs)
			row_cnt <= 10'd0;
		else if(edge_de)
			row_cnt <= row_cnt + 1'b1;
		else
			row_cnt <= row_cnt;
end
//双阈值,8连通域连接像素

shift_ram_two 	shift_ram_two_m0 (
	.aclr (~max_vs),
	.clock ( clk),
	.clken ( max_de),
	.shiftin ( max_g ),//输入端口  第三行
	.shiftout (),//和tap——1一样的输出
	.taps0x ( tap_4 ),//第二行
	.taps1x ( tap_5 )//第一行
	);

//对矩阵第一行进行移位赋值
always @ (posedge clk or negedge rst_s)
begin
	if (!rst_s)
	{mag1_1,mag1_2,mag1_3} <= 6'd0;
	else if (max_de)
	{mag1_1,mag1_2,mag1_3} <= {mag1_2,mag1_3,tap_5};
	else
	{mag1_1,mag1_2,mag1_3} <= {mag1_1,mag1_2,mag1_3};
end
//对矩阵第二行进行移位赋值
always @ (posedge clk or negedge rst_s)
begin
	if (!rst_s)
	{mag2_1,mag2_2,mag2_3} <= 6'd0;
	else if (max_de)
	{mag2_1,mag2_2,mag2_3} <= {mag2_2,mag2_3,tap_4};
	else
	{mag2_1,mag2_2,mag2_3} <= {mag2_1,mag2_2,mag2_3};
end

//对矩阵第3行进行移位赋值
always @ (posedge clk or negedge rst_s)
begin
	if (!rst_s)
	{mag3_1,mag3_2,mag3_3} <= 6'd0;
	else if (max_de)
	{mag3_1,mag3_2,mag3_3} <= {mag3_2,mag3_3,max_g};
	else
	{mag3_1,mag3_2,mag3_3} <= {mag3_1,mag3_2,mag3_3};
end

assign search = mag1_1[1] | mag1_2[1] | mag1_3[1] | mag2_1[1] | mag2_2[1] | mag2_3[1] | mag3_1[1] | mag3_2[1] | mag3_3[1];//搜寻目标像素周边是否包含梯度值大于高阈值的点,当然自身是高于的话,那么肯定为1  
assign high_low = mag2_2[1] | mag2_2[0];//排除小于低阈值的点
always @ (posedge clk or negedge rst_s)
begin
	if(!rst_s)
	canny_out <= 8'd0;
	else if (~canny_de)
	canny_out <= 8'd0;
	else if(high_low)
	canny_out <= (search) ? 8'hff : 8'h00;
	else
	canny_out <= 8'd0;
end

always@(posedge clk or negedge rst_s)
begin
  if (!rst_s)
  begin
    hs_buf_m <= 4'd0 ;
    vs_buf_m <= 4'd0 ;
    de_buf_m <= 4'd0 ;
  end
  else
  begin
	   hs_buf_m <= {hs_buf_n[2:0], max_hs} ;
	   vs_buf_m <= {vs_buf_n[2:0], max_vs} ;
	   de_buf_m <= {de_buf_n[2:0], max_de} ;
  end
end

assign canny_hs = hs_buf_m[3] ;
assign canny_vs = vs_buf_m[3] ;
assign canny_de = de_buf_m[3] ;






endmodule



  • 18
    点赞
  • 102
    收藏
    觉得还不错? 一键收藏
  • 36
    评论
智慧校园整体解决方案是响应国家教育信息化政策,结合教育改革和技术创新的产物。该方案以物联网、大数据、人工智能和移动互联技术为基础,旨在打造一个安全、高效、互动且环保的教育环境。方案强调从数字化校园向智慧校园的转变,通过自动数据采集、智能分析和按需服务,实现校园业务的智能化管理。 方案的总体设计原则包括应用至上、分层设计和互联互通,确保系统能够满足不同用户角色的需求,并实现数据和资源的整合与共享。框架设计涵盖了校园安全、管理、教学、环境等多个方面,构建了一个全面的校园应用生态系统。这包括智慧安全系统、校园身份识别、智能排课及选课系统、智慧学习系统、精品录播教室方案等,以支持个性化学习和教学评估。 建设内容突出了智慧安全和智慧管理的重要性。智慧安全管理通过分布式录播系统和紧急预案一键启动功能,增强校园安全预警和事件响应能力。智慧管理系统则利用物联网技术,实现人员和设备的智能管理,提高校园运营效率。 智慧教学部分,方案提供了智慧学习系统和精品录播教室方案,支持专业级学习硬件和智能化网络管理,促进个性化学习和教学资源的高效利用。同时,教学质量评估中心和资源应用平台的建设,旨在提升教学评估的科学性和教育资源的共享性。 智慧环境建设则侧重于基于物联网的设备管理,通过智慧教室管理系统实现教室环境的智能控制和能效管理,打造绿色、节能的校园环境。电子班牌和校园信息发布系统的建设,将作为智慧校园的核心和入口,提供教务、一卡通、图书馆等系统的集成信息。 总体而言,智慧校园整体解决方案通过集成先进技术,不仅提升了校园的信息化水平,而且优化了教学和管理流程,为学生、教师和家长提供了更加便捷、个性化的教育体验。
评论 36
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值