FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
登录
首页-技术文章/快讯-工程案例-正文

FPGA实现人脸检测

二牛学FPGA二牛学FPGA
工程案例
10个月前
0
0
509

一、肤色提取

  首先我们需要把肤色从外界环境提取出来,在肤色识别算法中,常用的颜色空间为YCbCr,Y 代表亮度,Cb 代表蓝色分量,Cr 代表红色分量。肤色在 YCbCr 空间受亮度信息的影响较小,本算法直接考虑 YCbCr 空间的CbCr 分量,映射为两维独立分布的 CbCr 空间。在 CbCr 空间下,肤色类聚性好,利用人工阈值法将肤色与非肤色区域分开,形成二值图像。

  根据经验,对肤色进行提取的条件场用如下不等式:

  77 < Cb < 127,133 < Cr < 173.

  代码基于 RGB_YCbCr_Gray,在最后本该输出灰度数据时,修改为输出肤色数据:

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        face_data &lt;= 'h0;
    end
    else if( (Cb2 > 77) &amp;&amp; (Cb2 &lt; 127) &amp;&amp; (Cr2 > 133) &amp;&amp; (Cr2 &lt; 173) ) begin
        face_data &lt;= 16'hffff;
    end
    else begin
        face_data &lt;= 'h0;
    end
end

  用一副图片试试,原图如下所示:

  肤色提取后结果如下所示:

二、滤波处理

  图片还好,如果是摄像头数据会有很多噪声,针对噪声,我们可以用之前整理过的中值滤波、高斯滤波等处理。

  此外人脸内部还会有些黑点,包括人脸外的环境可能有些地方也会被误检测为人脸,造成实验失败,因此可以加入形态学处理:腐蚀、膨胀、开运算、闭运算等,这些之前都整理过,不展开说了。

三、人脸框选

  现在我们要用一个框将人脸框住,达到人脸检测的目的。

//========================================================================== //== 行列划分 //========================================================================== always @(posedge clk or negedge rst_n) begin if(!rst_n) x <= 10'd0; else if(add_x) begin if(end_x) x <= 10'd0; else x <= x + 10'd1; end end assign add_x = face_de; assign end_x = add_x && x== COL-10'd1; always @(posedge clk or negedge rst_n) begin if(!rst_n) y <= 10'd0; else if(add_y) begin if(end_y) y <= 10'd0; else y <= y + 10'd1; end end assign add_y = end_x; assign end_y = add_y && y== ROW-10'd1; //========================================================================== //== 前一帧:人脸框选 //========================================================================== always @(posedge clk or negedge rst_n) begin if(!rst_n) begin x_min <= COL; end else if(pos_vsync) begin x_min <= COL; end else if(face_data==16'hffff && x_min > x) begin x_min <= x; end end //--------------------------------------------------- always @(posedge clk or negedge rst_n) begin if(!rst_n) begin x_max <= 0; end else if(pos_vsync) begin x_max <= 0; end else if(face_data==16'hffff && x_max < x) begin x_max <= x; end end //--------------------------------------------------- always @(posedge clk or negedge rst_n) begin if(!rst_n) begin y_min <= ROW; end else if(pos_vsync) begin y_min <= ROW; end else if(face_data==16'hffff && y_min > y) begin y_min <= y; end end //--------------------------------------------------- always @(posedge clk or negedge rst_n) begin if(!rst_n) begin y_max <= 0; end else if(pos_vsync) begin y_max <= 0; end else if(face_data==16'hffff && y_max < y) begin y_max <= y; end end //========================================================================== //== 前一帧结束:保存坐标值 //========================================================================== always @(posedge clk or negedge rst_n) begin if(!rst_n) begin x_min_r <= 0; x_max_r <= 0; y_min_r <= 0; y_max_r <= 0; end else if(neg_vsync) begin x_min_r <= x_min; x_max_r <= x_max; y_min_r <= y_min; y_max_r <= y_max; end end //========================================================================== //== 当前帧:数据输出 //========================================================================== always @(posedge clk or negedge rst_n) begin if(!rst_n) begin TFT_data <= 16'b0; end else if(TFT_y == y_min_r && TFT_x >= x_min_r && TFT_x <= x_max_r) begin TFT_data <= 16'b00000_111111_00000; end else if(TFT_y == y_max_r && TFT_x >= x_min_r && TFT_x <= x_max_r) begin TFT_data <= 16'b00000_111111_00000; end else if(TFT_x == x_min_r && TFT_y >= y_min_r && TFT_y <= y_max_r) begin TFT_data <= 16'b00000_111111_00000; end else if(TFT_x == x_max_r && TFT_y >= y_min_r && TFT_y <= y_max_r) begin TFT_data <= 16'b00000_111111_00000; end else begin TFT_data <= RGB_data; end end

  x 和 y为图像的实时坐标值,TFT_x 和 TFT_y 为 TFT_driver 生成的坐标值,这两个是不一样的。如果二者一样,最后的图像会有偏移。框的四个顶点坐标代码挺有意思,一开始很难理解,带几个数去看看就明白了,这段代码挺巧妙的,也挺简洁的。总体的思想和直方图拉伸很像,分两帧来处理,第一帧得到顶点坐标,当前帧的输出则实时的使用这个顶点坐标,因为两帧图像的差别很小,所以这么做比较方便。

  要注意的是每次扫描一帧后,顶点坐标要变回初始值,否则会出错,这点在图片的处理上体会不到什么,感觉不出bug,但是移植到摄像头视频数据时,不变回初始值就会有问题。这里的时序挺有意思,一开始我以为要打拍什么的,后面发现其实得到的是一个坐标值,坐标值本身在一帧结束到下一帧结束这段时间里是固定的,没必要打拍,但是要寄存住,和直方图拉伸一样。

  最终输出的结果是一个绿色的矩形框,非矩形框区域则输出原始视频数据,效果如下所示:

  板卡坏了哈,本来颜色很好的。

四、基于 OV7670 的人脸检测工程

  算法方面直接移植即可,注意的是,OV7670摄像头很差劲,噪声很多,需要进行一定的滤波处理,否则效果很差劲。

  我一开始直接移植写好的图像版本的主要代码,结果基本没有效果,以为是程序出错,检查了半天没找到毛病。后面将显示改成“框+二值图”,终于发现了屏幕上全是椒盐噪声,难怪没法成功,而如果用 OV7725 或 OV5640 等摄像头,这样的问题应该没这么严重。设备不给力,算法来使劲,我在肤色提取后连续进行了三次中值滤波去噪,然后用了一次腐蚀算法,最终的效果才勉强成功。

  视频演示如下所示,不想上镜,人脸改成手来测试:

  由于摄像头太差,加上板卡出现问题,颜色失真,导致看起来不漂亮,但总的结果还是对的。

五、基于OV7725的人脸检测工程

  回学校修好了板卡,换了 OV7725 摄像头,效果好多了。

标签:
本文原创,作者:二牛学FPGA,其版权均为FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训所有。
如需转载,请注明出处:https://z.shaonianxue.cn/9626.html
分享:
5天零基础做的第一个工程,赞
5天零基础做的第一个工程,赞上一篇
Modelsim联合Matlab搭建FPGA图像仿真平台下一篇
Modelsim联合Matlab搭建FPGA图像仿真平台
相关文章
总数:42

ZYNQ_MINI_REVB原理图.pdf

ZYNQ_MINI_REVB原理图.pdf
二牛学FPGA二牛学FPGA
工程案例, 资源分享
10个月前
0
0
442
0
源码系列:基于FPGA的自动售货机设计(附源工程)

源码系列:基于FPGA的自动售货机设计(附源工程)

设计要求一听饮料需要2.5美元,规定只能投入一美元,0.5美元的硬币。设计架构设计框架图:设计代码…
FPGA小白FPGA小白
工程案例
1年前
1
0
393
1
2024年夏令营学员项目代码展示(基于FPGA的广告点阵屏)

2024年夏令营学员项目代码展示(基于FPGA的广告点阵屏)

2024年夏令营学员项目代码展示(基于FPGA的广告点阵屏)
二牛学FPGA二牛学FPGA
工程案例, 技术分享
10个月前
0
0
428
1
成电国芯FPGA冬令营来啦

成电国芯FPGA冬令营来啦

利用假期偷偷学习一门硬核技术,明年参加大赛、为找工作做准备吧!成电国芯FPGA冬令营火热开启!这个冬天,让我们一起相约成电国芯,…
成小芯成小芯
工程案例
1年前
0
0
675
0
最简单的LED流水灯(含源码)

最简单的LED流水灯(含源码)

一、文档实现功能介绍本文档实现在 VIVADO 下面新建一个 FPGA 开发工程,然后采用时序和计数实现 LED的流水显示, 工程新建…
FPGA小白FPGA小白
工程案例, 技术分享
1年前
0
0
528
4
VIVADO仿真的使用之呼吸灯(含工程源码)

VIVADO仿真的使用之呼吸灯(含工程源码)

(基于 ZYNQMINI开发板)本文档实现对设计代码进行仿真,使用VIVADO自身的工具进行仿真,通过本教程学习如何使用VIVADO软…
FPGA小白FPGA小白
工程案例
1年前
0
0
661
1
评论表单游客 您好,欢迎参与讨论。
加载中…
评论列表
总数:0
FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
没有相关内容