HLS学习入门(肤色检测)
本节通过设计一个肤色检测的算法对前面几章的内容进行了巩固和验证,通过一组仿真验证了整个算法的有
效性。
一、肤色检测原理
肤色作为人的体表显著特征之一,尽管人的肤色因为人种的不同有差异,呈现出不同的颜色,但是在排除了亮度
和视觉环境等对肤色的影响后,皮肤的色调基本一致,这就为利用颜色信息来做肤色分割提供了理论的依据。
在肤色识别中,常用的颜色空间为 YCbCr 颜色空间。在 YCbCr 颜色空间中,Y 代表亮度,Cb 和 Cr 分别代表蓝色分
量和红色分量,两者合称为色彩分量。YCbCr 颜色空间具有将色度与亮度分离的特点,在 YCbCr 色彩空间中,肤色的聚
类特性比较好,而且是两维独立分布,能够比较好地限制肤色的分布区域,并且受人种的影响不大。对比 RGB 颜色空
间和 YCbCr 颜色空间,当光强发生变化时,RGB 颜色空间中(R,G,B)会同时发生变化,而 YCbCr 颜色空间中受光强相对
独立,色彩分量受光强度影响不大,因此 YCbCr 颜色空间更适合用于肤色识别。
由于肤色在 YCbCr 空间受亮度信息的影响较小,本算法直接考虑 YCbCr 空间的 CbCr 分量,映射为两维独立分
布的 CbCr 空间。在 CbCr 空间下,肤色类聚性好,利用人工阈值法将肤色与非肤色区域分开,形成二值图像。
RGB 转 YCbCr 的公式为:
Y = 0.257*R+0.564*G+0.098*B+16
Cb= -0.148*R-0.291*G+0.439*B+128
Cr = 0.439*R-0.368*G-0.071*B+128
对肤色进行判定的条件常使用如下判定条件:
Cb > 77 && Cb < 127
Cr > 133 && Cr < 173
二、代码分析
1. void hls::hls_skin_dection()函数
对于不了解的数据类型,可以和其他IDE一样ctrl单击找到定义的地方,比如这里RGB_IMAGE可以在top.h里找到是mat类型的重定义。
关于LOOp(循环),在HLS中也是支持被综合的,但是有一些限制,比如可变循环边界是不允许的,更多可以参考官方ug902文档。
// void hls::hls_skin_dection(原图像,输出图像,图像范围、颜色,亮度分量极小极大值,
// 蓝色分量,红色分量极小极大值)
void hls::hls_skin_dection(RGB_IMAGE& src, RGB_IMAGE& dst,int rows, int cols,
int y_lower,int y_upper,int cb_lower,int cb_upper,int cr_lower,int cr_upper)
{
//整体是由两层循环组成,按rows,cols遍历图片
LOOp_ROWS:for(int row = 0; row < rows ; row++)
{
#pragma HLS PIPELINE II=1 off
LOOp_COLS:for(int col = 0; col < cols; col++)
{
//变量定义
RGB_PIXEL src_data;
RGB_PIXEL pix;
RGB_PIXEL dst_data;
//皮肤标志位
bool skin_region;
//如果在图片范围内,就把RGB_IMAGE格式图片信息赋值给RGB_PIXEL格式图片信息
if(row < rows && col < cols) {
src >> src_data;
}
//获取RGB通道数据
uchar B = src_data.val[0];
uchar G = src_data.val[1];
uchar R = src_data.val[2];
//RGB-->YCbCr颜色空间转换
//先扩大256倍,再右移8位变回。将小数的计算转换成整数的
uchar y = (76 * R + 150 * G + 29 * B) >> 8;
uchar cb = ((128*B -43*R - 85*G)>>8) + 128 ;
uchar cr = ((128*R -107*G - 21 * B)>>8)+ 128 ;
//肤色区域判定
if (y > y_lower && y < y_upper && cb > cb_lower && cb < cb_upper && cr > cr_lower && cr < cr_upper)
skin_region = 1;
else
skin_region = 0;
//在原图上作标记,如果是肤色区域用255(白色)进行替代
uchar temp0= (skin_region == 1)? (uchar)255: B;
uchar temp1= (skin_region == 1)? (uchar)255: G;
uchar temp2= (skin_region == 1)? (uchar)255: R;
dst_data.val[0] = temp0;//输出数据 B
dst_data.val[1] = temp1;//输出数据 G
dst_data.val[2] = temp2;//输出数据 R
dst << dst_data;
}
}
}
2. 顶层函数ImgProcess_Top()
没有什么特殊内容,定义输入输出图像,然后将输入图像转成矩阵类型,供上面的函数使用,然后输出图像。
void ImgProcess_Top(AXI_STREAM& input, AXI_STREAM& output,int rows, int cols,
int y_lower,int y_upper,int cb_lower,int cb_upper,int cr_lower,int cr_upper)
{
//端口约束
#pragma HLS RESOURCE variable=input core=AXIS metadata="-bus_bundle INPUT_STREAM"
#pragma HLS RESOURCE variable=output core=AXIS metadata="-bus_bundle OUTPUT_STREAM"
#pragma HLS RESOURCE core=AXI_SLAVE variable=rows metadata="-bus_bundle CONTROL_BUS"
#pragma HLS RESOURCE core=AXI_SLAVE variable=cols metadata="-bus_bundle CONTROL_BUS"
#pragma HLS RESOURCE core=AXI_SLAVE variable=y_lower metadata="-bus_bundle CONTROL_BUS"
#pragma HLS RESOURCE core=AXI_SLAVE variable=y_upper metadata="-bus_bundle CONTROL_BUS"
#pragma HLS RESOURCE core=AXI_SLAVE variable=cb_lower metadata="-bus_bundle CONTROL_BUS"
#pragma HLS RESOURCE core=AXI_SLAVE variable=cb_upper metadata="-bus_bundle CONTROL_BUS"
#pragma HLS RESOURCE core=AXI_SLAVE variable=cr_lower metadata="-bus_bundle CONTROL_BUS"
#pragma HLS RESOURCE core=AXI_SLAVE variable=cr_upper metadata="-bus_bundle CONTROL_BUS"
#pragma HLS RESOURCE core=AXI_SLAVE variable=return metadata="-bus_bundle CONTROL_BUS"
#pragma HLS INTERFACE ap_stable port=rows
#pragma HLS INTERFACE ap_stable port=cols
#pragma HLS INTERFACE ap_stable port=y_lower
#pragma HLS INTERFACE ap_stable port=y_upper
#pragma HLS INTERFACE ap_stable port=cb_lower
#pragma HLS INTERFACE ap_stable port=cb_upper
#pragma HLS INTERFACE ap_stable port=cr_lower
#pragma HLS INTERFACE ap_stable port=cr_upper
//定义输入输出图像
RGB_IMAGE img_0(rows, cols);
RGB_IMAGE img_1(rows, cols);
#pragma HLS dataflow
//输入数据转换成矩阵类型
hls::AXIvideo2Mat(input,img_0);
//调用上面定义函数
hls::hls_skin_dection(img_0,img_1,rows,cols,y_lower,y_upper,cb_lower,cb_upper,cr_lower,cr_upper);
hls::Mat2AXIvideo(img_1, output);
}
三、优化
1.循环的优化
在ug902中也可以看到LOOp循环是可综合和优化的,优化的手段有多种,这里采用了流水线的优化:
#pragma HLS PIPELINE II=1 off
PIPELINE:
Reduces the initiation interval by allowing the overlapped execution of operations within loop or function.
通过允许并发来优化
2.数据流的优化
根据官方手册 how_to_accelerate_opencv_applications_using_vivado_hls.pdf 中的描述:
前两个优化在综合的时候给出warning,在2019.2版本已经启用,去掉即可。
#pragma HLS RESOURCE variable=input core=AXIS metadata="-bus_bundle INPUT_STREAM"
#pragma HLS RESOURCE variable=output core=AXIS metadata="-bus_bundle OUTPUT_STREAM"
#pragma HLS RESOURCE core=AXI_SLAVE variable=rows metadata="-bus_bundle CONTROL_BUS"
#pragma HLS RESOURCE core=AXI_SLAVE variable=cols metadata="-bus_bundle CONTROL_BUS"
#pragma HLS RESOURCE core=AXI_SLAVE variable=y_lower metadata="-bus_bundle CONTROL_BUS"
#pragma HLS RESOURCE core=AXI_SLAVE variable=y_upper metadata="-bus_bundle CONTROL_BUS"
#pragma HLS RESOURCE core=AXI_SLAVE variable=cb_lower metadata="-bus_bundle CONTROL_BUS"
#pragma HLS RESOURCE core=AXI_SLAVE variable=cb_upper metadata="-bus_bundle CONTROL_BUS"
#pragma HLS RESOURCE core=AXI_SLAVE variable=cr_lower metadata="-bus_bundle CONTROL_BUS"
#pragma HLS RESOURCE core=AXI_SLAVE variable=cr_upper metadata="-bus_bundle CONTROL_BUS"
#pragma HLS RESOURCE core=AXI_SLAVE variable=return metadata="-bus_bundle CONTROL_BUS"
3.端口约束的优化
#pragma HLS INTERFACE ap_stable port=rows
#pragma HLS INTERFACE ap_stable port=cols
#pragma HLS INTERFACE ap_stable port=y_lower
#pragma HLS INTERFACE ap_stable port=y_upper
#pragma HLS INTERFACE ap_stable port=cb_lower
#pragma HLS INTERFACE ap_stable port=cb_upper
#pragma HLS INTERFACE ap_stable port=cr_lower
#pragma HLS INTERFACE ap_stable port=cr_upper
ap_stable: No protocol. The interface is a data port. Vivado HLS assumes the data port is
always stable after reset, which allows internal optimizations to remove unnecessary registers
四、综合以及仿真结果
从综合结果中可以看到资源的利用情况,触发器,查找表等。