基于FPGA的人脸检测

基于FPGA的人脸检测

本设计是基于肤色检测的基础上进行的人脸检测,上一篇用MATLAB实现了对人脸的框选代码实现,根据这个原理,博主将这个算法移植到FPGA上,用纯逻辑来实现。FPGA的并行处理优势,在视频图像采集系统中的进行简单的预处理,这个系统主要分为四个部分,视频图像采集、图像存储、算法处理、图像显示。

到了一定阶段,移植也是一种能力。这句话不是我说的,我从书上看到的。老话说的好,何必重复造轮子。好吧,我想说的是,这个工程中部分代码是我移植过来的。

整个设计人脸检测一共分为以下几步,

  • RGB到YCbCr色彩空间转换
  • Cb、Cr阈值分离肤色二值化
  • 先膨胀后腐蚀(闭运算)
  • 人脸区域框选

肤色识别

简单来说就是通过皮肤的颜色来判断图片中你脸的为止,一副图片有RGB三分量的颜色组成,将RGB色彩空间转换成YCbCr色彩空间上,肤色的判断就可以用Cb和Cr分量的阈值来判断。

肤色识别YCbCr阈值

77 < Cb < 127

133 < Cr < 173

闭运算

先腐蚀后膨胀叫开运算,开运算的作用是清除图像边缘周围非边缘的细小的点。先膨胀后腐蚀为闭运算,闭运算的作用是清除图像内部的空洞,

如果我们的目标物体外面有很多无关的小区域,就用开运算去除掉;如果物体内部有很多小黑洞,就用闭运算填充掉。

RGB2YCbCr算法以及腐蚀膨胀算法已经发表过,想看的朋友后台回复图像处理即可。

人脸框选

经过闭运算后,采集到的肤色相近的区域会显示成白色,其他颜色会被滤除。现在只需要画一个框将图中白色区域框起来。从第一个点开始逐行遍历所有点,根据像素值为不为0(即全1),找到整个人脸边框的四个顶点。

flag信号为每一帧开始的标志,每一帧扫描完成后,四个顶点的坐标恢复初始值,然后重新扫描确定,可以达到实时检测框选的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
parameter   ROW_CNT = 640;
parameter COL_CNT = 480;

wire flag ;//开始本帧数据
assign flag = (cnt_x == 1 && cnt_y == 1)? 1'b1:1'b0;
//-------------------------------------------------------
//x_min lag 2clk
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
x_min <= ROW_CNT;
end
else if(flag)
x_min <= ROW_CNT;
else if(per_frame_clken && per_img_Bit == 1 && x_min > cnt_x)
x_min <= cnt_x;
else
x_min <= x_min;
end
//x_max
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
x_max <= 0;
end
else if(flag)
x_max <= 0;
else if(per_frame_clken && per_img_Bit == 1 && x_max < cnt_x)
x_max <= cnt_x;
else
x_max <= x_max;
end
//y_min
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
y_min <= COL_CNT;
end
else if(flag)
y_min <= COL_CNT;
else if(per_frame_clken && per_img_Bit == 1 && y_min > cnt_y)
y_min <= cnt_y;
else
y_min <= y_min;
end
//y_max
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
y_max <= 0;
end
else if(flag)
y_max <= 0;
else if(per_frame_clken && per_img_Bit == 1 && y_max < cnt_y)
y_max <= cnt_y;
else
y_max <= y_max;
end

显示区域四个顶点的坐标确定后,将坐标值传输给VGA显示模块,行扫描和场扫描计数器进行对比,根据四个顶点的值,来画出一个矩形框。其他情况输出原始未处理的视频流数据,即可达到人脸框选的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
always @(*)begin
if(lcd_y == y_min && lcd_x >= x_min && lcd_x <= x_max)
lcd_data <= `GREEN;
else if(lcd_y == y_max && lcd_x >= x_min && lcd_x <= x_max)
lcd_data <= `GREEN;
else if(lcd_x == x_min && lcd_y >= y_min && lcd_y <= y_max)
lcd_data <= `GREEN;
else if(lcd_x == x_max && lcd_y >= y_min && lcd_y <= y_max)
lcd_data <= `GREEN;
else if(disp_en)
lcd_data <= dina;
else
lcd_data <= 0;
end

image

image

关于整个系统的仿真

最后再谈一下整个系统的仿真,在对于实时的视频流处理,最需要注意的视频流行场信号与像素数据的同步问题,不然就会出现“断片”的这种现象。所以在下板前充分仿真时很有必要的。

image

拿一帧是640x480的像素举例,在代码中可以将分辨率设置成16x4,模拟摄像头时序输出行场信号和像素数据,输入给系统,主要观察整个系统视频流的流动过程是否有数据丢失,和信号的同步问题。细小的问题只能是通过仿真查找问题,光瞪着代码看是很难找到问题的。

仿真通过了但是实际下板也不一定能跑出来,组合逻辑运算延时过长,改成用寄存器寄存,用时钟沿输出。还有在复杂的系统中赢尽量采用异步复位、同步释放来处理复位信号,避免复位信号扇出过大。关于时序问题工具也可以之间检查出来,这个问题后面再看情况学习研究。

最后

这个工程师基于QuartusII开发的,需要本设计的整个工程的朋友,在我的订阅号后台回复图像处理,即可获得工程的获得方式。

image

image