学fpga(hls之图像处理)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】

        本身hls为了推广,寻找了一些适合hls开发的领域。图像处理就是其中一个方向。图像处理的应用十分广泛,本身又需要进行短时间的大数据量处理,十分适合hls的使用。所以,xilinx官方提供了hls的函数库,同时为了进行testbench的测试,这些接口本身也兼容opencv已有的接口。

1、头文件引用

#include <hls_video.h>

#define MAX_HEIGHT 800
#define MAX_WIDTH 1280

typedef hls::stream<ap_axiu<24,1,1,1> > AXI_STREAM;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC3> RGB_IMAGE;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC1> GRAY_IMAGE;

typedef unsigned char uchar;
typedef hls::Scalar<1, uchar> GRAY_PIXEL;

        头文件引用的部分比较简单,本身只要包含hls_video.h文件即可,剩下来的就是对stream、Mat、Scalar进行类型定义操作。因为在计算的过程中涉及到大量的模板处理,所以编写代码的时候最好选用cpp文件。

2、灰化处理

void rgb2gray(AXI_STREAM& input_stream,
		AXI_STREAM& output_stream,
		int rows,
		int cols)
{

#pragma HLS INTERFACE s_axilite port=cols
#pragma HLS INTERFACE s_axilite port=rows
#pragma HLS INTERFACE axis port=output_stream
#pragma HLS INTERFACE axis port=input_stream
#pragma HLS INTERFACE ap_ctrl_none port=return
#pragma HLS DATAFLOW

	RGB_IMAGE img_0(rows, cols);
	GRAY_IMAGE img_1(rows, cols);
	RGB_IMAGE img_2(rows, cols);

	hls::AXIvideo2Mat(input_stream, img_0);
	hls::CvtColor<HLS_RGB2GRAY, HLS_8UC3, HLS_8UC1>(img_0, img_1);
	hls::CvtColor<HLS_GRAY2RGB, HLS_8UC1, HLS_8UC3>(img_1, img_2);
	hls::Mat2AXIvideo(img_2, output_stream);
}

        灰化处理是图像处理的第一步,最大的好处就是可以减少计算的数据量。这里的hls::CvtColor都是官方提供的函数。之所以会有两个函数,是因为数据格式的问题,毕竟最后还要回归到AXI_STREAM的形式。

3、高斯滤波

void rgb2gray(AXI_STREAM& input_stream,
		AXI_STREAM& output_stream,
		int rows,
		int cols)
{

#pragma HLS INTERFACE s_axilite port=cols
#pragma HLS INTERFACE s_axilite port=rows
#pragma HLS INTERFACE axis port=output_stream
#pragma HLS INTERFACE axis port=input_stream
#pragma HLS INTERFACE ap_ctrl_none port=return
#pragma HLS DATAFLOW

	RGB_IMAGE img_0(rows, cols);
	GRAY_IMAGE img_1(rows, cols);
	GRAY_IMAGE img_2(rows, cols);
	RGB_IMAGE img_3(rows, cols);

	hls::AXIvideo2Mat(input_stream, img_0);
	hls::CvtColor<HLS_RGB2GRAY, HLS_8UC3, HLS_8UC1>(img_0, img_1);
	hls::GaussianBlur<3,3>(img_1, img_2);
	hls::CvtColor<HLS_GRAY2RGB, HLS_8UC1, HLS_8UC3>(img_2, img_3);
	hls::Mat2AXIvideo(img_3, output_stream);
}

        高斯滤波是实际场景中经常使用到的算法。<3,3>模板也是可以选择的,一般3、5、7都是用的比较多的模板,可以根据实际情况灵活进行配置。

4、直方图增强

void rgb2gray(AXI_STREAM& input_stream,
		AXI_STREAM& output_stream,
		int rows,
		int cols)
{

#pragma HLS INTERFACE s_axilite port=cols
#pragma HLS INTERFACE s_axilite port=rows
#pragma HLS INTERFACE axis port=output_stream
#pragma HLS INTERFACE axis port=input_stream
#pragma HLS INTERFACE ap_ctrl_none port=return
#pragma HLS DATAFLOW

	RGB_IMAGE img_0(rows, cols);
	GRAY_IMAGE img_1(rows, cols);
	GRAY_IMAGE img_2(rows, cols);
	RGB_IMAGE img_3(rows, cols);

	hls::AXIvideo2Mat(input_stream, img_0);
	hls::CvtColor<HLS_RGB2GRAY, HLS_8UC3, HLS_8UC1>(img_0, img_1);
	hls::EqualizeHist<HLS_8UC1, HLS_8UC1, MAX_HEIGHT, MAX_WIDTH>(img_1, img_2);
	hls::CvtColor<HLS_GRAY2RGB, HLS_8UC1, HLS_8UC3>(img_2, img_3);
	hls::Mat2AXIvideo(img_3, output_stream);
}

        直方图增强也是用的比较多的一种方法。它的基本原理就是计算出每个像素个数在整体像素里面的比例,据此依据这个比例来对像素值做出修正处理。

5、Sobel算子

void rgb2gray(AXI_STREAM& input_stream,
		AXI_STREAM& output_stream,
		int rows,
		int cols)
{

// sobel算子

#pragma HLS INTERFACE s_axilite port=cols
#pragma HLS INTERFACE s_axilite port=rows
#pragma HLS INTERFACE axis port=output_stream
#pragma HLS INTERFACE axis port=input_stream
#pragma HLS INTERFACE ap_ctrl_none port=return
#pragma HLS DATAFLOW

	RGB_IMAGE img_0(rows, cols);
	GRAY_IMAGE img_1(rows, cols);
	GRAY_IMAGE img_2(rows, cols);
	RGB_IMAGE img_3(rows, cols);

	hls::AXIvideo2Mat(input_stream, img_0);
	hls::CvtColor<HLS_RGB2GRAY, HLS_8UC3, HLS_8UC1>(img_0, img_1);
	hls::Sobel<1,0,3>(img_1, img_2);
	hls::CvtColor<HLS_GRAY2RGB, HLS_8UC1, HLS_8UC3>(img_2, img_3);
	hls::Mat2AXIvideo(img_3, output_stream);
}

        各种算子一般用来对图像进行边缘检测。算子计算式二值化之前的重要步骤。一般经过边缘检测之后,图像中物体的轮廓会变得非常明显,和周围的其他像素有着明显的区别。

6、二值化

void rgb2gray(AXI_STREAM& input_stream,
		AXI_STREAM& output_stream,
		int rows,
		int cols)
{

#pragma HLS INTERFACE s_axilite port=cols
#pragma HLS INTERFACE s_axilite port=rows
#pragma HLS INTERFACE axis port=output_stream
#pragma HLS INTERFACE axis port=input_stream
#pragma HLS INTERFACE ap_ctrl_none port=return
#pragma HLS DATAFLOW

	RGB_IMAGE img_0(rows, cols);
	GRAY_IMAGE img_1(rows, cols);
	GRAY_IMAGE img_2(rows, cols);
	RGB_IMAGE img_3(rows, cols);

	hls::AXIvideo2Mat(input_stream, img_0);
	hls::CvtColor<HLS_RGB2GRAY, HLS_8UC3, HLS_8UC1>(img_0, img_1);
	hls::Threshold(img_1, img_2, 150, 255, HLS_THRESH_BINARY);
	hls::CvtColor<HLS_GRAY2RGB, HLS_8UC1, HLS_8UC3>(img_2, img_3);
	hls::Mat2AXIvideo(img_3, output_stream);
}

         做完了算子计算,下面就是二值化。小于某个数值的像素全部置为0,大于某个数值的像素全部置为255。和灰化类似,通过二值化之后,像素中的信息更为集中和聚焦。

7、膨胀和腐蚀

void rgb2gray(AXI_STREAM& input_stream,
		AXI_STREAM& output_stream,
		int rows,
		int cols)
{

#pragma HLS INTERFACE s_axilite port=cols
#pragma HLS INTERFACE s_axilite port=rows
#pragma HLS INTERFACE axis port=output_stream
#pragma HLS INTERFACE axis port=input_stream
#pragma HLS INTERFACE ap_ctrl_none port=return
#pragma HLS DATAFLOW

	RGB_IMAGE img_0(rows, cols);
	GRAY_IMAGE img_1(rows, cols);
	GRAY_IMAGE img_2(rows, cols);
	GRAY_IMAGE img_3(rows, cols);
	RGB_IMAGE img_4(rows, cols);

	hls::AXIvideo2Mat(input_stream, img_0);
	hls::CvtColor<HLS_RGB2GRAY, HLS_8UC3, HLS_8UC1>(img_0, img_1);
	hls::Dilate(img_1, img_2);
	hls::Erode(img_2, img_3);
	hls::CvtColor<HLS_GRAY2RGB, HLS_8UC1, HLS_8UC3>(img_3, img_4);
	hls::Mat2AXIvideo(img_4, output_stream);
}

        二值化之后的图像一般还会存在着空洞、藕断丝连的情况。这个时候可以通过进行开运算、或者是闭运算来解决。换言之,就是用膨胀、腐蚀的方法来解决。这也是图像处理的必要步骤。

8、轮廓提取

        轮廓提取一般还是通过cpu来完成,这部分不适合用fpga来进行加速处理。通常fpga做好了灰化、增强、滤波、sobel算子、二值化和膨胀腐蚀之后,会把图像交还给cpu继续进行处理,完成剩下来的操作,这部分式必不可少的。当然,做完了轮廓提取,还要通过周长、面积、圆度等特征做进一步的提取和分析。

9、IP接口部分

        这部分的算法只是完成了AXI_STREAM到AXI_STREAM的转换,如果要进行显示,或者返回到cpu运算,还需要其他IP核的协调核、帮助。但总体来说,这部分还是最重要的。一般来说,算法ip会从AXI Video DMA获得数据,处理结束的数据也会还给AXI Video DMA,这是最常用的方法。

10、自定义图像算法

void pixel_binary(GRAY_IMAGE& src, GRAY_IMAGE& dst, uchar binary)
{
	uchar value;

	GRAY_PIXEL src_data;
	GRAY_PIXEL dst_data;

	for(int i =0 ; i < MAX_HEIGHT; i++)
	{
		for(int j = 0; j < MAX_WIDTH; j++)
		{
#pragma HLS PIPELINE II=1
			src >> src_data;
			value = src_data.val[0];
			dst_data.val[0] = value > binary ? 255:0;
			dst << dst_data;
		}
	}
}

void rgb2gray(AXI_STREAM& input_stream,
		AXI_STREAM& output_stream,
		int rows,
		int cols,
		uchar binary)
{

#pragma HLS INTERFACE s_axilite port=cols
#pragma HLS INTERFACE s_axilite port=rows
#pragma HLS INTERFACE s_axilite port=binary
#pragma HLS INTERFACE axis port=output_stream
#pragma HLS INTERFACE axis port=input_stream
#pragma HLS INTERFACE ap_ctrl_none port=return
#pragma HLS DATAFLOW

	RGB_IMAGE img_0(rows, cols);
	GRAY_IMAGE img_1(rows, cols);
	GRAY_IMAGE img_2(rows, cols);
	RGB_IMAGE img_3(rows, cols);

	hls::AXIvideo2Mat(input_stream, img_0);
	hls::CvtColor<HLS_RGB2GRAY, HLS_8UC3, HLS_8UC1>(img_0, img_1);
	pixel_binary(img_1, img_2, binary);
	hls::CvtColor<HLS_GRAY2RGB, HLS_8UC1, HLS_8UC3>(img_2, img_3);
	hls::Mat2AXIvideo(img_3, output_stream);
}

        除了hls提供的标准算之外,很多时候用户还需要自定义自己的算法。比如10中的pixel_binary这个函数。binary是通过s_axilite传递的参数,参数直接传递给pixel_binary。在函数中,通过GRAY_PIXEL和binary比较的方法,直接将像素设定为0或者式255。当然,实际应用的场景可能会比这个复杂一点,到时候只要根据具体情况具体分析即可。

11、关于testbench

IplImage2AXIvideo
AXIvideo2IplImage

        做testbench的时候,需要把图像换成AXIvideo,等到做完之后,如果需要保存的话,还需要把AXIvideo转换成图像,这一点需要注意一下。有了这两个接口之后,就可以在***_csim.log里面看到对应的测试结果了。

  • 3
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式-老费

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值