1、整体项目架构

 这就不多说了,自己先根据项目结构图来理解一下,知道思路后按照以下设计完成各模块编写即可。

2、设计流程

1、中值和高斯滤波。

        这一部分我前面写过,至于为什么要这么写呢?第一个就是我们前面说到过图像传输过程或者采集过程,有很多噪声。为了消除这些噪声影响,我们需要先进行滤波,以便为后面的条件判断。第二个是为了对图像边缘均衡化。那如果不进行滤波处理呢?请看效果图:(以下是二值化输出图像)

由此可以不滤波的图像内部是有很多噪声的,为了消除这些影响我们进行三次中值滤波。为什么是三次?这是我经过很多次验证得来的。为了使得边缘平滑化,我们进行一次高斯滤波。最终效果图:

2、RGB转YUV

这一部分没什么可说得无非就几个公式,具体完整代码请看我以前得文章。

3、椭圆域

        这里就要引入一个问题,为什么要加入椭圆限制,这很简单。人脸近视一个椭圆,并且为了防止伪肤色干扰,我们加一重限制,防止出现人脸误判。

        椭圆公式:

转化:

当然这里为了不消耗大量资源我们将除法转为乘法:

代码:这里根据网上得经验将产长轴和短轴设为固定得且合适的常数。

    parameter Cb0 = 110, Cr0 = 145;
    parameter A2  = 25*25;
    parameter B2  = 15*15;

    wire signed [15:0] dcb = cb - Cb0;
    wire signed [15:0] dcr = cr - Cr0;
    wire [31:0] skin_val = (dcb*dcb)*B2 + (dcr*dcr)*A2;

    wire skin_bin = (skin_val <= A2*B2) ? 1'b1 : 1'b0;

4、两次腐蚀

这里也不多说了直接贴代码:该模块可以根据输入信号类型选择腐蚀还是膨胀。

module rgb_etch_handle
(
	input					sys_clk		,
    input					sys_rst_n	,
			
	input 					bin_vs		,//场信号
	input 					bin_hr		,//行信号	
	input 			[7:0]	bin_data	,
	
	input   				en_chose	,//1腐蚀,0膨胀。

	output	reg				etc_vs		,
    output	reg		        etc_hr		,
    output	reg		[7:0]	etc_data	 
);


wire		matrix_vs	;
wire		matrix_hr	;

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  ;

always @(posedge sys_clk)
	if(!sys_rst_n)
		etc_vs<=1'b0;
	else
		etc_vs<=matrix_vs;

always @(posedge sys_clk)
	if(!sys_rst_n)
		etc_hr<=1'b0;
	else
		etc_hr<=matrix_hr;

always @(posedge sys_clk)
	if(!sys_rst_n)
		etc_data<=8'd0;
	else if(en_chose)
		etc_data<=(matrix_p11[0]||matrix_p12[0]||matrix_p13[0]||
				   matrix_p21[0]||matrix_p22[0]||matrix_p23[0]||
				   matrix_p31[0]||matrix_p32[0]||matrix_p33[0])	?	8'hff	:	8'd0;//ff白色,00黑色
	else
		etc_data<=(matrix_p11[0]&&matrix_p12[0]&&matrix_p13[0]&&
				   matrix_p21[0]&&matrix_p22[0]&&matrix_p23[0]&&
				   matrix_p31[0]&&matrix_p32[0]&&matrix_p33[0])	?	8'hff	:	8'd0;//ff白色,00黑色
		
		
matrix_3x3 matrix_3x3_un 
(
	.sys_clk	(sys_clk		),
	.sys_rst_n	(sys_rst_n		),

	.yuv_vs		(bin_vs			),
	.yuv_hr		(bin_hr			),
	.yuv_data	(bin_data		),

	.matrix_vs	(matrix_vs		),
	.matrix_hr	(matrix_hr		),

	.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 	)
);
endmodule

5、坐标计算模块

        这个模块比较简单,其实就是根据像素值判断坐标区域,也就是0和1(0:背景,1:前景完成坐标判断):通过该模块将计算出的坐标输出进入VGA控制区域画框。

module pic_frame
(
	input					sys_clk		,
    input					sys_rst_n	,
			
	input					h_vs		,
	input					h_hr		,
	input			[15:0]	h_data		,
	
	output 					led_n0		,//场信号
	output 					led_n1		,//行信号	
	output 				 	led_n2		,
	output 				 	led_n3		,
	
	output	reg		[9:0 ]	x_min_r     ,
	output	reg		[9:0 ]	x_max_r     ,
	output	reg		[9:0 ]	y_min_r     ,
	output	reg		[9:0 ]	y_max_r     
);
reg			h_vs_reg;
reg			h_hr_reg;

always @(posedge sys_clk)
    if(!sys_rst_n)begin
        h_vs_reg<=1'b0;
        h_hr_reg<=1'b0;
	end
    else begin
        h_vs_reg<=h_vs;
        h_hr_reg<=h_hr;
	end
wire	h_vs_neg=(!h_vs&&h_vs_reg)	?	1'b1	:	1'b0;
wire    h_hr_neg=(!h_hr&&h_hr_reg)	?	1'b1	:	1'b0;
wire	h_vs_pos=(h_vs&&!h_vs_reg)	?	1'b1	:	1'b0;

reg [9:0]	x;
reg [9:0]	y;

always @(posedge sys_clk)
	if(!sys_rst_n)
		x <= 10'd0;
	else if(h_hr_neg)
		x <= 10'd0;       
	else if(h_hr)
		x <= x + 1'b1;  
	else
		x<=10'd0;

always @(posedge sys_clk )
    if(!sys_rst_n||h_vs_pos||h_vs_neg)
        y <= 10'd0;
    else if(h_hr_neg)
		y<=y+1'b1;
	else
		y<=y;

//==========================================================================
//==隔帧人脸框选
//==========================================================================

reg	[9:0] x_min;
reg	[9:0] x_max;
reg	[9:0] y_min;
reg	[9:0] y_max;

always @(posedge sys_clk) 
    if(!sys_rst_n) 
        x_min <= 10'd640;
    else if(h_vs_pos) 
        x_min <= 10'd640;
    else if(h_data==16'hffff && x_min > x&&h_hr) 
        x_min <= x;
//---------------------------------------------------
always @(posedge sys_clk) 
    if(!sys_rst_n) 
        x_max <= 0;
    else if(h_vs_pos) 
        x_max <= 0;
    else if(h_data==16'hffff && x_max < x&&h_hr) 
        x_max <= x;
//---------------------------------------------------
always @(posedge sys_clk) 
    if(!sys_rst_n)
        y_min <= 10'd480;
    else if(h_vs_pos) 
        y_min <= 10'd480;
    else if(h_data==16'hffff && y_min > y&&h_hr) 
        y_min <= y;
//---------------------------------------------------
always @(posedge sys_clk)
    if(!sys_rst_n)
        y_max <= 0;
    else if(h_vs_pos)
        y_max <= 0;
    else if(h_data==16'hffff && y_max < y&&h_hr)
        y_max <= y;

assign led_n0=(x_min 	==10'd640)	?	1'b0	:	1'b1;
assign led_n1=(x_max 	==10'd0)	?	1'b0	:	1'b1;
assign led_n2=(y_min 	==10'd480)	?	1'b0	:	1'b1;
assign led_n3=(y_max 	==10'd0)	?	1'b0	:	1'b1;
//assign led_n1=(x_max_r 	==10'd0	 )	?	1'b0	:	1'b1;
//assign led_n2=(y_min_r 	==10'd480)	?	1'b0	:	1'b1;

//assign led_n3=1'b0;
//assign led_n3=(y_max_r 	!=10'd0)	?	1'b0	:	1'b1;
//==========================================================================
//==    前一帧结束:保存坐标值
//==========================================================================

always @(posedge sys_clk)
    if(!sys_rst_n) begin
        x_min_r <= 0;
        x_max_r <= 0;
        y_min_r <= 0;
        y_max_r <= 0;
    end
    else if(h_vs_pos) begin
        x_min_r <= x_min;
        x_max_r <= x_max;
        y_min_r <= y_min;
        y_max_r <= y_max;
    end



endmodule

6、结果:如图

3、仿真设计。

上述思路其实是比较简单的,主要说一下仿真设计。主要就是坐标计算的仿真,我们用systemverilog语言从我们自定义的设置的4*4像素矩阵验证改模块。在.txt文件输入以下矩形,接下来验证是否正确。

代码:

`timescale  1ns/1ns
module pic_frame_sim();

reg				sys_clk		;
reg				sys_rst_n	;
							
reg				h_vs		;
reg				h_hr		;
reg		[15:0]	h_data		;
							
wire	[9:0 ]	x_min_r     ;
wire	[9:0 ]	x_max_r     ;
wire	[9:0 ]	y_min_r     ;
wire	[9:0 ]	y_max_r     ;

initial begin
	sys_clk=1'b0;
	forever #5 sys_clk = ~sys_clk;
end

initial
begin
    sys_rst_n = 1'b0;
    repeat(4) @(posedge sys_clk);
    sys_rst_n = 1'b1;
end

task data_reg_input;
	bit 	[9:0]	cnt_x;
	bit     [9:0]   cnt_y;
    bit     [15:0]   mem     [15:0];//4*4-1

	$readmemh("./grady1.dat",mem);

	
	repeat(2) @(posedge sys_clk);
	
	for(cnt_y=0;cnt_y<4;cnt_y++)begin
		
		h_vs=1'b1;
		repeat(4) @(posedge sys_clk);
		
		for(cnt_x=0;cnt_x<4;cnt_x++)
		begin
			h_hr=1'b1;
			h_data=mem[cnt_y*4+cnt_x];
			@(posedge sys_clk);
		end
		
		h_hr=1'b0;
		h_data=16'd0;
		repeat(4) @(posedge sys_clk);
		
	end
		repeat(4) @(posedge sys_clk);
		h_vs=1'b0;

endtask : data_reg_input


task data_reg_input1;
	bit 	[9:0]	cnt_x;
	bit     [9:0]   cnt_y;
    bit     [15:0]   mem     [15:0];//4*4-1

	$readmemh("./grady2.dat",mem);

	
	repeat(2) @(posedge sys_clk);
	
	for(cnt_y=0;cnt_y<4;cnt_y++)begin
		
		h_vs=1'b1;
		repeat(4) @(posedge sys_clk);
		
		for(cnt_x=0;cnt_x<4;cnt_x++)
		begin
			h_hr=1'b1;
			h_data=mem[cnt_y*4+cnt_x];
			@(posedge sys_clk);
		end
		
		h_hr=1'b0;
		h_data=16'd0;
		repeat(4) @(posedge sys_clk);
		
	end
		repeat(4) @(posedge sys_clk);
		h_vs=1'b0;

endtask : data_reg_input1

pic_frame pic_frame_un1
(
	.sys_clk	(sys_clk	),
    .sys_rst_n	(sys_rst_n	),

	.h_vs		(h_vs		),
	.h_hr		(h_hr		),
	.h_data		(h_data		),

	.x_min_r    (x_min_r    ),
	.x_max_r    (x_max_r    ),
	.y_min_r    (y_min_r    ),
	.y_max_r    (y_max_r    ) 
);

initial
begin
    h_vs	= 0;
    h_hr	= 0;
    h_data	= 16'd0;
end

initial 
begin
    wait(sys_rst_n);
    fork
        begin 
            repeat(2) @(posedge sys_clk); 
            data_reg_input;
			repeat(2) @(posedge sys_clk); 
            data_reg_input;
			repeat(2) @(posedge sys_clk); 
            data_reg_input;
			repeat(2) @(posedge sys_clk); 
            data_reg_input;
			repeat(2) @(posedge sys_clk); 
            data_reg_input1;
			repeat(2) @(posedge sys_clk); 
            data_reg_input1;
			repeat(2) @(posedge sys_clk); 
            data_reg_input1;
			repeat(2) @(posedge sys_clk); 
            data_reg_input1;
			repeat(2) @(posedge sys_clk); 
            data_reg_input1;
			repeat(2) @(posedge sys_clk); 
            data_reg_input1;
        end
    join
end


endmodule

仿真波形:

现在分析波形,根据我们以上导入的txt文件观察,最后输出的xy的最值是符合我们的需求的然后我们这里写了两个模块一个读取txt1,一个读取txt2里面的最值区域也有不同。结果几个帧后我们换成txt2发现坐标也是符合的。

txt2:

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐