【三 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

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

Cb > 77 && Cb < 127 
Cr > 133 && Cr < 173

二、代码实现

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

#include "top.h"
#include <string.h>

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);
}

文件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

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

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

#include "top.h"
#include "hls_opencv.h"
#include "iostream"
#include <time.h>

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;


}

另外,在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);

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

#include "top.h"
#include <string.h>

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);
}

在这里插入图片描述

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

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

	--晓凡 202294日于南宁书
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值