• 【三 HLS】HLS实现肤检测


    一、肤色检测的原理

    利用颜色信息分割的理论依据:肤色因为人种的不同有不同的差异,呈现出不同的颜色,但是排除亮度和视觉环境对肤色的影响之后,皮肤的色调基本一致。
    肤色识别中常用的颜色空间为YCbCr颜色空间,其中Y代表亮度,Cb代表蓝色分量,Cr代表红色分量,Cb和Cr两者的结合称之为色彩分量。YCbCr颜色空间具有将色度和亮度分离的特点。YCbCr颜色空间中,肤色的聚类性比较好,而且是两维独立分布,能够很好的限制肤色的分布区域。RGB颜色空间和YCbCr颜色空间相对比,当光强发生改变时,RGB颜色空间中(R,G,B)会同时发生改变,而YCbCr颜色空间受光强相对独立,彩色分量受光强影响不大,YCbCr颜色空间更为适合用于肤色识别。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
    
    • 1
    • 2
    • 3

    对肤色进行判断的条件使用如下条件:

    Cb > 77 && Cb < 127 
    Cr > 133 && Cr < 173
    
    • 1
    • 2

    二、代码实现

    新建工程,在source下添加下面代码文件:
    文件1:top.cpp

    #include "top.h"
    #include 
    
    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)
    {
    LOOp_ROWS:for(int row=0;row<rows;row++)
    	{
    
    		LOOp_COLS:for(int col = 0; col < cols; col++)
    		{
    			//变量定义
    			RGB_PIXEL src_data;
    			RGB_PIXEL pix;
    			RGB_PIXEL dst_data;
    			bool skin_region;
    
    			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颜色空间转换
    			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;
    			uchar temp0=(skin_region==1)?(uchar)255:B;
    			uchar temp1=(skin_region==1)?(uchar)255:R;
    			uchar temp2=(skin_region==1)?(uchar)255:R;
    
    			dst_data.val[0]=temp0;
    			dst_data.val[1]=temp1;
    			dst_data.val[2]=temp2;
    
    			dst<<dst_data;
    		}
    	}
    }
    
    void ImgeProcess_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)
    {
    	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
    • 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
    • 55
    • 56
    • 57
    • 58
    • 59

    文件2:top.h

    #ifndef _TOP_H_
    #define _TOP_H_
    
    #include "hls_video.h"
    #define MAX_WIDTH 1920
    #define MAX_HEIGHT 1082
    
    typedef unsigned char uchar;
    
    // i/o image setings
    #define INPUT_IMAGE	"test_1080p.bmp"
    #define OUTPUT_IMAGE	"result_1080p.bmp"
    #define OUTPUT_IMAGE_GOLDEN	"result_1080p_golden.bmp"
    
    
    //typedef video library core structures
    typedef hls::stream<ap_axiu<24,1,1,1> > 			AXI_STREAM;
    typedef hls::Mat<MAX_HEIGHT,MAX_WIDTH,HLS_8UC3> RGB_IMAGE;
    typedef hls::Scalar<3,unsigned char>			RGB_PIXEL;
    
    //hls命名空间。函数声明,用到了命名空间,这就是一个虚拟文件夹的意思
    namespace hls
    {
    	void 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);
    }
    
    void ImgeProcess_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);
    #endif
    
    
    • 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

    有以上两个文件已经可以实现代码综合了。综合报告:
    在这里插入图片描述

    接下来是仿真,需要编写仿真文件在Test Bench中添加测试文件,tb.cpp:

    #include "top.h"
    #include "hls_opencv.h"
    #include "iostream"
    #include 
    
    using namespace std;
    using namespace cv;
    
    int main(int argc,char** argv)
    {
    	//IplImage* src =cvLoadImage(INPUT_IMAGE);
    	IplImage* src = cvLoadImage("test_img1.jpg");
    	IplImage* dst=cvCreateImage(cvGetSize(src),src->depth,src->nChannels);
    
    	AXI_STREAM src_axi,dst_axi;
    	IplImage2AXIvideo(src,src_axi);
    
    	ImgeProcess_Top(src_axi,dst_axi,src->height,src->width,0,255,75,125,131,185);
    	AXIvideo2IplImage(dst_axi,dst);
    	cvShowImage("src",src);
    	cvShowImage("dst_hls",dst);
    	waitKey(0);//参数<=0时等待按键事件发生,按下键的话返回按键的值, 否则返回-1;
    
    	return 0;
    
    
    }
    
    
    • 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

    另外,在Test Bench中添加test_img1.jpg测试图片,点击仿真按钮就可以得到结果:
    在这里插入图片描述
    左边是原图,右边是结果。

    三、代码优化

    官网手册为:how_to_accelerate_opencv_applicationns_using_vivado_hls.pdf
    这里面详细描述了hLS代码优化。
    在这里插入图片描述
    视频接口的约束如下:将src和dst指定为“INPUT_STREAM"命名的AXI4 Stream,将控制口分配到AXI4接口,指定”rows"可通过AXI4-Lite接口进行访问并且声明在函数执行过程中“rows”不会改变,其实这几优化语句中最关键的一条指令为启用数据流优化:#pragma HLS dataflow,它使得任务之间以流水线的方式执行。即

    hls:AXIvideo2Mat(src,img_0);
    hls::skin_detect(img_0,img_1,rows,cols,cb_lower,cb_upper,cr_lower,cr_upper);
    hls::Mat2AXIvideo(img_1,dst);
    
    • 1
    • 2
    • 3

    这三个函数之间为流线方式。
    将top.cpp文件添加约束语句:

    #include "top.h"
    #include 
    
    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)
    {
    LOOp_ROWS:for(int row=0;row<rows;row++)
    	{
    
    		LOOp_COLS:for(int col = 0; col < cols; col++)
    		{
    #pragma HLS PIPELINE II=1 off
    			//变量定义
    			RGB_PIXEL src_data;
    			RGB_PIXEL pix;
    			RGB_PIXEL dst_data;
    			bool skin_region;
    
    			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颜色空间转换
    			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;
    			uchar temp0=(skin_region==1)?(uchar)255:B;
    			uchar temp1=(skin_region==1)?(uchar)255:R;
    			uchar temp2=(skin_region==1)?(uchar)255:R;
    
    			dst_data.val[0]=temp0;
    			dst_data.val[1]=temp1;
    			dst_data.val[2]=temp2;
    			//复制处理完成后输出图像
    			dst<<dst_data;
    		}
    	}
    }
    
    void ImgeProcess_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 variable=rows core=AXI_SLAVE metadata="-bus_bundle CONTROL_BUS"
    #pragma HLS RESOURCE variable=cols core=AXI_SLAVE metadata="-bus_bundle CONTROL_BUS"
    #pragma HLS RESOURCE variable=y_lower core=AXI_SLAVE metadata="-bus_bundle CONTROL_BUS"
    #pragma HLS RESOURCE variable=y_upper core=AXI_SLAVE metadata="-bus_bundle CONTROL_BUS"
    #pragma HLS RESOURCE variable=cb_lower core=AXI_SLAVE metadata="-bus_bundle CONTROL_BUS"
    #pragma HLS RESOURCE variable=cb_upper core=AXI_SLAVE 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
    • 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
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83

    在这里插入图片描述

    优化之后的综合报告:
    在这里插入图片描述

    对比两次的综合报告发现优化后的代码使用的dsp还是6个,但是触发器由原来的987变为518,查找表由原来的1454变为1193。
    此时需要消耗六个周期(标号0到5)才处理完毕。
    在这里插入图片描述
    优化后得到的结果:
    在这里插入图片描述
    查看代码我们可以发现我们使用了 for 循环(LOOP),下面对循环 LOOP 控制的优化指令进行说明:
    Unroll:展开循环,用于创建多个独立的操作,而不是对单个操作进行整合。
    loop_Merge:合并连续的循环,降低总延迟,提高共享和优化。
    loop_Flatten:允许将带有改善延迟和逻辑优化的嵌套循环,整理成一个单个的循环。
    Dataflow:允许顺序循环,并发的操作。
    Pipeline:通过执行并发的操作,提高吞吐量。
    在这里插入图片描述

    	--晓凡 202294日于南宁书
    
    • 1
  • 相关阅读:
    【前端设计模式】之观察者模式
    ubuntu命令
    牛客小白月赛60-D-游戏购买!
    Python 命令行参数:Argparse 与 Click
    OPC UA协议报文,基础介绍+Hello报文解析
    《rabbitMQ学习》一 rabbitMQ部署篇
    学习笔记:C++ 11新特性
    Pgsql 一个表中的字段like另一个表中的字段
    使用自功率谱、互功率谱估计滤波器幅频特性
    【软考】系统集成项目管理工程师(九)项目成本管理
  • 原文地址:https://blog.csdn.net/xzs520xzs/article/details/126686961