【HLS】图像高斯平滑滤波实现

接上篇文章,课程已经结束且出分,接着写后面几次任务内容实现。本次任务是使用HLS实现图像平滑滤波:
任务描述
同样的,本文不涉及图像高斯平滑滤波的理论部分,只对HLS实现进行说明。

模块实现

图像高斯平滑

本次实验中使用高斯平滑方法,使用高斯平滑滤波核对输入图像进行卷积,为了方便计算与节省资源,对5×5高斯平滑滤波核先进行定点化,且输入图像不进行边缘零填充。
定点化后5×5高斯平滑滤波核

模块整体框架

为了实现任务中”每个周期可以处理一个像素点”的要求,突破访存瓶颈,本模块使用多个Stream流作为LineBuffer对输入图像行进行缓存,使得输入图像的多行数据可以对齐输入至卷积窗口中。结构类似下图:
LineBuffer结构

卷积窗口处理

当卷积窗口前的LineBuffer都“流”满了数据即可以开始进行卷积操作,此处使用“UNROLL”优化指令配合模块整体的“PIPELINE”优化指令使得25个乘项计算并行,且其结果形成流水线加法树完成卷积乘加操作。
卷积窗口实现
与上一次实验类似,需要考虑乘加法对数据位宽的扩宽,适当添加移位与截位操作。

模块接口处理

本模块中对输入图像与输出图像的读写最大频率都是每周期一个像素,为了节省资源以及最大化模块带宽,使用AXI4-Stream接口作为本模块输入输出接口。
模块接口实现
注意此处,输入为512×512的8位单通道图像,输出为508×508的8位单通道图像,长宽的减少是因为没有对输入图像进行边缘补0操作。

模块综合结果

可以看到,模块内层Interval为1,符合设计任务需求。模块综合结果

模块仿真实现

输入图像

使用图像处理领域经典图像“Lena”作为输入图像。
输入图像

输出图像

输出图像
通过对比输入图像与输出图像,可以很明显的看出输出图像更模糊一些,模块实现平滑效果。

联合仿真

联合仿真局部波形截图1
联合仿真局部波形截图2
如截图1所示,输出接口上能够每一时钟周期都输出一个有效结果,达到设计要求;如截图2所示,输出结果有效在时间上比第一个有效输入数据存在滞后,这是因为需要等待输入数据填充满LineBuffer。

实现代码

HLS实现

#include "image_conv.h"

void Conv2d(ap_uint<8> img_in[512 * 512], ap_uint<8> img_out[508 * 508])
{
	#pragma HLS INTERFACE axis port = img_in
	#pragma HLS INTERFACE axis port = img_out

	hls::stream<ap_uint<8>> LineBuf1, LineBuf2, LineBuf3, LineBuf4;
	#pragma HLS STREAM depth=513 variable=LineBuf1
	#pragma HLS STREAM depth=513 variable=LineBuf2
	#pragma HLS STREAM depth=513 variable=LineBuf3
	#pragma HLS STREAM depth=513 variable=LineBuf4

	ap_uint<8> W[5][5] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
	#pragma HLS ARRAY_PARTITION variable=W complete dim=0

	ap_uint<12> length = 512;
	ap_uint<12> lineIdx[4] = {0, 0, 0, 0};
	ap_uint<24> addr = 0;
	ap_uint<8> kernel[5][5] = {
			1 , 4 , 6 , 4 , 1 ,
			4 , 16, 24, 16, 4 ,
			6 , 24, 36, 24, 6 ,
			4 , 16, 24, 16, 4 ,
			1 , 4 , 6 , 4 , 1};

	for(ap_uint<24> i = 0; i < length * length; i++)
	{
		#pragma HLS PIPELINE
		W[0][4] = W[0][3];
		W[0][3] = W[0][2];
		W[0][2] = W[0][1];
		W[0][1] = W[0][0];
		W[0][0] = img_in[i];

		if(lineIdx[0] == length)
		{
			W[1][4] = W[1][3];
			W[1][3] = W[1][2];
			W[1][2] = W[1][1];
			W[1][1] = W[1][0];
			W[1][0] = LineBuf1.read();
			lineIdx[0] -= 1;
		}
		if(lineIdx[1] == length)
		{
			W[2][4] = W[2][3];
			W[2][3] = W[2][2];
			W[2][2] = W[2][1];
			W[2][1] = W[2][0];
			W[2][0] = LineBuf2.read();
			lineIdx[1] -= 1;
		}
		if(lineIdx[2] == length)
		{
			W[3][4] = W[3][3];
			W[3][3] = W[3][2];
			W[3][2] = W[3][1];
			W[3][1] = W[3][0];
			W[3][0] = LineBuf3.read();
			lineIdx[2] -= 1;
		}
		if(lineIdx[3] == length)
		{
			W[4][4] = W[4][3];
			W[4][3] = W[4][2];
			W[4][2] = W[4][1];
			W[4][1] = W[4][0];
			W[4][0] = LineBuf4.read();
			lineIdx[3] -= 1;
		}
		LineBuf1.write(W[0][0]);
		lineIdx[0] += 1;
		LineBuf2.write(W[1][0]);
		lineIdx[1] += 1;
		LineBuf3.write(W[2][0]);
		lineIdx[2] += 1;
		LineBuf4.write(W[3][0]);
		lineIdx[3] += 1;

    	if((i > 4 * length + 1) && ((i - 4) % length < length - 4))
    	{
    		ap_uint<16> tmp = 0;
    		for(ap_uint<8> r = 0; r < 4; r++)
    		{
				#pragma HLS UNROLL
    			for(ap_uint<8> c = 0; c < 4; c++)
    			{
    				 tmp += W[r][c] * kernel[r][c];
    			}
    	    }
        	img_out[addr] = (ap_uint<8>)(tmp >> 8);
        	addr += 1;
    	}
	}
}

仿真实现

#include "image_conv.h"
#include "opencv2/opencv.hpp"

int main(void)
{
	cv::Mat a = cv::imread("Lena.bmp", 0);
	cv::Mat b = cv::Mat::zeros(512 - 4, 512 - 4, CV_8U);
	Conv2d((ap_uint<8> *)a.data, (ap_uint<8> *)b.data);
	cv::imwrite("out.jpg", b);
	return 0;
}

仿真代码需要配置opencv运行环境。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值