草庐IT

FPGA图像处理学习——基于肤色的人脸识别

Fighting_XH 2023-04-12 原文

本文针对咸鱼FPGA的FPGA实现人脸检测以及直方图拉伸进行原理学习。


一、人脸识别大致流程

获取人脸图像——肤色提取(Ycbcr+阈值)——滤波处理(中值、腐蚀膨胀)——人脸框选——显示

肤色提取:顾名思义,将肤色从外界环境中提取出。在肤色识别算法中,常用YCbCr颜色空间(亮度、蓝色、红色分量),因为肤色在 YCbCr 空间受亮度信息的影响较小,从而肤色类聚性好,由此,在Ycbcr空间基础上,我们用人工阈值法将肤色与非肤色区域分开,最终形成二值图像,实现肤色的提取。

滤波处理:人脸内部可能存在黑点、人脸外的某些地方也可能会被误检测为人脸,这些情况都会造成识别失败,因此加入中值滤波以及腐蚀、膨胀,这些之前都整理过,不展开说了。


1、人脸肤色识别

原理:先进行Ycbcr空间转换得到亮度、蓝色、红色分量,给cb和cr设置阈值,即可将肤色提取出来。(共采用四级流水线)

1.1 Ycbcr三级流水线

首先三级流水线后,可得到三分量如下:

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        Y2  <= 8'd0;
        Cb2 <= 8'd0;
        Cr2 <= 8'd0;
    end
    else begin
        Y2  <= Y1[15:8];  
        Cb2 <= Cb1[15:8];
        Cr2 <= Cr1[15:8];
    end
end

1.2 第四级流水线进行肤色识别

Cb和Cr设置阈值:Cb:77 ~ 127 ;Cr:133~173;(前人大量研究得到的经验值),最终输出的结果是二值化结果,目的是减少运算量!

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

RTL图如下:
可看到,输入是RGB565原图数据,内部进行Ycbcr转换,分别得到8位的Y,Cb,Cr分量,后根据蓝红分量的阈值得到16位的二值化肤色数据face_data,阈值内为白色,阈值外为黑色;

2、滤波处理

检测出肤色后,为提高图像质量,进行中值滤波、腐蚀膨胀处理。

3、人脸框选(人脸框和原图一起输出)

通过肤色检测出人脸后,我们用行列坐标画框,将人脸框选出来,最终人脸框和图像数据同时输出,原图图像数据是16位,因此前面肤色数据face_data也用的16位。

RGB信号:原图数据、使能以及行场有效信号。
face信号:人脸肤色提取后的图像数据、使能以及行场有效信号。

3.1 人脸框的四个顶点坐标

如何得到人脸框的四个顶点坐标?

因为两帧图像差别较小,因此我们将人脸肤色图像分两帧来处理,通过这两帧图像得到人脸框的坐标,这样可防止图像结果偏移的情况出现。其中第一帧得到框的四个顶点坐标,当前帧的输出即可实时的使用人脸框的四个顶点坐标。

1、既然要用连续的两帧肤色图像,我们就要对图像延迟一拍。

always @(posedge clk) begin
    face_vsync_r <= face_vsync;
end

2、通过连续两帧图像的场有效信号得到最终的边沿信号,上升沿为人脸肤色图像开始标志,下降沿为结束标志。

assign pos_vsync =  face_vsync && ~face_vsync_r;
assign neg_vsync = ~face_vsync &&  face_vsync_r;

3.2 人脸图像横纵坐标

3、利用显示驱动生成的行场计数器,得到人脸图像的横纵坐标

parameter COL               = 11'd640              ; //图片长度
parameter ROW               = 11'd480              ; //图片高度
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        face_x <= 10'd0;
    else if(add_face_x) begin //人脸肤色数据有效
        if(end_face_x) //数据有效且一行640像素计数完成
            face_x <= 10'd0;
        else
            face_x <= face_x + 10'd1; //显示驱动生成的横坐标
    end
end

assign add_face_x = face_de;
assign end_face_x = add_face_x && face_x== COL-10'd1;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        face_y <= 10'd0;
    else if(add_face_y) begin//一行数据计数完成
        if(end_face_y)//一行数据完成且480场计数完成(一帧图像完成)
            face_y <= 10'd0;
        else
            face_y <= face_y + 10'd1;//显示驱动生成的纵坐标
    end
end

assign add_face_y = end_face_x;
assign end_face_y = add_face_y && face_y== ROW-10'd1;

3.3 人脸框选

3中得到了图像的横纵坐标,从而可确定出框的四个顶点坐标,然后利用延迟后的一帧图像来将人脸框和图像数据同时实时输出。

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 > face_x && face_de) begin //有肤色数据,且框x最小坐标>肤色处x坐标
        x_min <= face_x; //当前肤色x坐标就是框的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 < face_x && face_de) begin//框x最大坐标<肤色x坐标,那肤色x坐标就是框x最大值
        x_max <= face_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 > face_y && face_de) begin //同理
        y_min <= face_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 < face_y && face_de) begin//同理
        y_max <= face_y;
    end
end

5、实时顶点坐标值的保存

前一帧到当前帧的间隙来保存坐标值,从而供当前帧来实时使用。

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

至此,得到了人脸框,接下来找该人脸框下对应的原图数据。

6、原图行列计数器

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        RGB_x <= 10'd0;
    else if(add_RGB_x) begin  //原图像数据有效
        if(end_RGB_x) //原图像数据有效且一行计数完成
            RGB_x <= 10'd0;
        else
            RGB_x <= RGB_x + 10'd1;
    end
end

assign add_RGB_x = RGB_de;
assign end_RGB_x = add_RGB_x && RGB_x== COL-10'd1;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        RGB_y <= 10'd0;
    else if(add_RGB_y) begin //一行计数完成
        if(end_RGB_y) //一行计数完成且一场计数完成
            RGB_y <= 10'd0;
        else
            RGB_y <= RGB_y + 10'd1;
    end
end

assign add_RGB_y = end_RGB_x;
assign end_RGB_y = add_RGB_y && RGB_y== ROW-10'd1;

7、人脸框和原图输出
用按键来控制识别效果,一种是原图的人脸检测,一种是二值化腐蚀膨胀后的人脸检测效果。

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
		key_num <= 1'b0;
	else if(key_vld)
		key_num <= ~key_num;
end

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
		TFT_de	  <= 1'b0;
		TFT_hsync <= 1'b0;
		TFT_vsync <= 1'b0;
		TFT_data  <= 16'b0;
	end
	else if(key_num==1'b0) begin //按键按下的时候得到白色方框和原图
		if((RGB_y >= y_min_r-1 && RGB_y <= y_min_r+1) && RGB_x >= x_min_r && RGB_x <= x_max_r) begin
			TFT_data <= 16'b11111_000000_00000;
		end
		else if((RGB_y >= y_max_r-1 && RGB_y <= y_max_r+1) && RGB_x >= x_min_r && RGB_x <= x_max_r) begin
			TFT_data <= 16'b11111_000000_00000;
		end
		else if((RGB_x >= x_min_r-1 && RGB_x <= x_min_r+1) && RGB_y >= y_min_r && RGB_y <= y_max_r) begin
			TFT_data <= 16'b11111_000000_00000;
		end
		else if((RGB_x >= x_max_r-1 && RGB_x <= x_max_r+1) && RGB_y >= y_min_r && RGB_y <= y_max_r) begin
			TFT_data <= 16'b11111_000000_00000;
		end
		else begin
			TFT_de    <= RGB_de;
			TFT_hsync <= RGB_hsync;
			TFT_vsync <= RGB_vsync;
			TFT_data  <= RGB_data;
		end
	end
    else if(key_num==1'b1) begin //按键释放的时候得到白色方框和二值化腐蚀膨胀后的图像数据
		if((face_y >= y_min_r-1 && face_y <= y_min_r+1) && face_x >= x_min_r && face_x <= x_max_r) begin
			TFT_data <= 16'b11111_000000_00000;
		end
		else if((face_y >= y_max_r-1 && face_y <= y_max_r+1) && face_x >= x_min_r && face_x <= x_max_r) begin
			TFT_data <= 16'b11111_000000_00000;
		end
		else if((face_x >= x_min_r-1 && face_x <= x_min_r+1) && face_y >= y_min_r && face_y <= y_max_r) begin
			TFT_data <= 16'b11111_000000_00000;
		end
		else if((face_x >= x_max_r-1 && face_x <= x_max_r+1) && face_y >= y_min_r && face_y <= y_max_r) begin
			TFT_data <= 16'b11111_000000_00000;
		end
		else begin
			TFT_de    <= face_de;
			TFT_hsync <= face_hsync;
			TFT_vsync <= face_vsync;
			TFT_data  <= face_data;
		end
	[添加链接描述](https://www.cnblogs.com/xianyufpga/p/12531569.html)end
end

本文主要学习:人脸肤色如何提取(为蓝红分量设置阈值),以及人脸框如何得到(延迟一拍得到连续两帧图像,从而实时地使用这四个顶点坐标,根据四个顶点的横纵坐标值赋予颜色,得到方框)。木有想到一个假期竟然只看了这………………


二、直方图拉伸

基于人脸识别获取顶点坐标的方法,对直方图拉伸的图像算法进行分析并编写verilg代码。

1、直方图拉伸的简单介绍

参考:在视频图像处理中,为了能够实时调节图像对比度,通常进行直方图拉伸,直方图拉伸是指将图像灰度直方图较窄的灰度级区间向两端拉伸,从而增强整幅图像像素的灰度级对比度,达到增强图像的效果。

2、直方图拉伸公式

A:Imin,表示最小灰度级
B:Imax,表示最大灰度级

和人脸框求顶点坐标相同,直方图拉伸也采用两帧图像进行处理。只有一帧图像流过之后,我们才能得到该图像的最大和最小灰度级,才能进行后续直方图拉伸公式的计算,由于连续两帧图像差别小,我们分成两帧图像。

方法:
1、帧延迟一拍,获得前一帧和当前帧
2、前一帧计算最小最大灰度级AB
3、在前一帧和当前帧的间隙,保存AB的值
4、在当前帧计算公式——公式用三级流水线来实现(分子分母,商,大括号)

2.1 帧延迟并获得AB

always @(posedge clk) begin
    face_vsync_r <= face_vsync;
end

获得一帧图像的开始结束标志

assign pos_vsync =  face_vsync && ~face_vsync_r;
assign neg_vsync = ~face_vsync &&  face_vsync_r;

前一帧获得AB
图像流经未开始或者结束的时候,最小像素值255,最大像素值0;当图像流过且像素有效时候,开始进行像素比较,得到一帧图像中的最小最大像素值。

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        max <= 8'd0;
        min <= 8'd255;
    end
    else if(Y_vsync && Y_de) begin     //像素有效时
        max <= (max > Y_data) ? max : Y_data;
        min <= (min < Y_data) ? min : Y_data;
    end
    else if(neg_Y_vsync) begin         //一帧图像结束时
        max <= 8'd0;
        min <= 8'd255;
    end
end

2.2 保存AB值

如何确定间隙?
nge_vsnyc高电平处为间隙,此时保存AB值即可。

2.3 拉伸公式计算

三级流水线:

第一级流水线:计算分子分母

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        mole <= 'd0; //分子
        deno <= 'd0; //分母
    end
    else begin
        mole <= (Y_data - Y_min) * 255;
        deno <=  Y_max  - Y_min;
    end
end

第二级流水线:分子分母除法计算

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        quot <= 'd0;
    end
    else begin
        quot <= mole / deno;
    end
end

第三级流水线:大括号的计算

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        hist_data <= 8'd0;
    end
    else if(Y_data < Y_min) begin
        hist_data <= 8'd0;
    end
    else if(Y_data > Y_max) begin
        hist_data <= 8'd255;
    end
    else begin
        hist_data <= quot[7:0];
    end
end

插曲:
有喜欢碎碎念的盆友们这里相见吧!

有关FPGA图像处理学习——基于肤色的人脸识别的更多相关文章

  1. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  2. ruby-on-rails - 添加回形针新样式不影响旧上传的图像 - 2

    我有带有Logo图像的公司模型has_attached_file:logo我用他们的Logo创建了许多公司。现在,我需要添加新样式has_attached_file:logo,:styles=>{:small=>"30x15>",:medium=>"155x85>"}我是否应该重新上传所有旧数据以重新生成新样式?我不这么认为……或者有什么rake任务可以重新生成样式吗? 最佳答案 参见Thumbnail-Generation.如果rake任务不适合你,你应该能够在控制台中使用一个片段来调用重新处理!关于相关公司

  3. 报告回顾丨模型进化狂飙,DetectGPT能否识别最新模型生成结果? - 2

    导读语言模型给我们的生产生活带来了极大便利,但同时不少人也利用他们从事作弊工作。如何规避这些难辨真伪的文字所产生的负面影响也成为一大难题。在3月9日智源Live第33期活动「DetectGPT:判断文本是否为机器生成的工具」中,主讲人Eric为我们讲解了DetectGPT工作背后的思路——一种基于概率曲率检测的用于检测模型生成文本的工具,它可以帮助我们更好地分辨文章的来源和可信度,对保护信息真实、防止欺诈等方面具有重要意义。本次报告主要围绕其功能,实现和效果等展开。(文末点击“阅读原文”,查看活动回放。)Ericmitchell斯坦福大学计算机系四年级博士生,由ChelseaFinn和Chri

  4. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  5. [Vuforia]二.3D物体识别 - 2

    之前说过10之后的版本没有3dScan了,所以还是9.8的版本或者之前更早的版本。 3d物体扫描需要先下载扫描的APK进行扫面。首先要在手机上装一个扫描程序,扫描现实中的三维物体,然后上传高通官网,在下载成UnityPackage类型让Unity能够使用这个扫描程序可以从高通官网上进行下载,是一个安卓程序。点到Tools往下滑,找到VuforiaObjectScanner下载后解压数据线连接手机,将apk文件拷入手机安装然后刚才解压文件中的Media文件夹打开,两个PDF图打印第一张A4-ObjectScanningTarget.pdf,主要是用来辅助扫描的。好了,接下来就是扫描三维物体。将瓶

  6. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  7. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  8. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  9. ruby-on-rails - 在 heroku 的 .fonts 文件夹中包含自定义字体,似乎无法识别它们 - 2

    Heroku支持人员告诉我,为了在我的Web应用程序中使用自定义字体(未安装在系统中,您可以在bash控制台中使用fc-list查看已安装的字体)我必须部署一个包含所有字体的.fonts文件夹里面的字体。问题是我不知道该怎么做。我的意思是,我不知道文件名是否必须遵循heroku的任何特殊模式,或者我必须在我的代码中做一些事情来考虑这种字体,或者如果我将它包含在文件夹中它是自动的......事实是,我尝试以不同的方式更改字体的文件名,但根本没有使用该字体。为了提供更多详细信息,我们使用字体的过程是将PDF转换为图像,更具体地说,使用rghostgem。并且最终图像根本不使用自定义字体。在

  10. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

随机推荐