FPGA:基于的肤色的人脸识别
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:

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