xilinx 暑期学校项目(二) 通过HLS实现二维傅里叶变换(2Dfft)及图像数据读入读出方法

要进行维纳滤波,首先就是要将图像矩阵通过二维fft变换到频域。

但是hls_fft.h中只有一维fft的函数,因此我们采用对行逐行进行一维fft,再对列逐行进行一维fft来实现二维fft的效果。

文章目录

  • 自带的fft例程
  • 二维fft的实现
  • 数据的读入和读出
    • 读入
    • 读出
  • 维纳滤波操作

自带的fft例程

自带的fft例程中包括有如下四个函数

void dummy_proc_fe(
    bool direction,
    config_t* config,
    cmpxData in[FFT_LENGTH],
    cmpxData out[FFT_LENGTH])

void dummy_proc_middle(
    config_t* config_in,
    config_t* config_out,
    status_t* st_in,
    cmpxData in[FFT_LENGTH],
    cmpxData out[FFT_LENGTH])

void dummy_proc_be(
    status_t* status_in,
    bool* ovflo,
    cmpxData in[FFT_LENGTH],
    cmpxData out[FFT_LENGTH])

void fft_top(
    bool direction,
    cmpxData in[FFT_LENGTH],	//xn1 -> in
    cmpxData out[FFT_LENGTH],	//out -> xk1
    bool* ovflo)

其中void dummy_proc_fevoid dummy_proc_middlevoid dummy_proc_be是对fft的前后处理,我们大可不必管他,照搬就是
唯一需要主要的就是在void dummy_proc_middle中有一句bool ovflw= st_in->getOvflo();

这个ovflw顾名思义就是overflow,是溢出,而void dummy_proc_fe中有一个config->setSch(0x2AB);是设置系数,我们需要调整好这个系数来防止计算的溢出,可以参考一位大佬的文章

接下来还有一个问题,就是我们想用这个fft函数来进行fft计算,而不是单独做一个模块,所以需要去除函数开头的一些硬件接口的预定义,接下来就可以放到我们的主程序来调用了。

#pragma HLS interface ap_hs port=direction
#pragma HLS interface ap_fifo depth=1 port=ovflo
#pragma HLS interface ap_fifo depth=1024 port=in,out
#pragma HLS data_pack variable=in
#pragma HLS data_pack variable=out

二维fft的实现

接下来要在主程序中调用这个fft函数来实现二维fft。
首先来看下函数接口,我们需要复数类型的数据输入和输出。

typedef std::complex<float> cmpxData;

因此我们定义二维复数数组来获取图像矩阵数值。
这个复数是使用的c++标准库complex,所以其赋值方法是

cmpx x;
cmpx y;
x = y;
/******************or********************/
x.real(1);
x.imag(1);
/******************or**********************/
x = (1,1);

接下来进行fft2d,此处需要主要的是我们的数组要用静态数组,static

/***********get FFT_array(2D FFT)**************/
	bool ovflo;
	//傅里叶正变换(行)
	for(int r = 0; r < rows; r++)
	{
	#pragma HLS pipeline
		for(int c = 0; c < cols; c++)
		{
			in1[c].real(xn1[r][c].real());
			in1[c].imag(xn1[r][c].imag());
			//in[c] = xn1[r][c];
			//printf("in %d num %f \n",c,in[c].real());
		}
		fft_top(direction,in1,out1,&ovflo);
		for(int c = 0; c < cols; c++)
		{
			middle[r][c].real(out1[c].real()/256);
			middle[r][c].imag(out1[c].imag()/256);
			//middle[r][c] = out[c];
			//printf("in	%d	real: %f	image: %f \n",r,out[c].real(),out[c].imag());
		}
	}
	//傅里叶正变换(列)
	for(int c = 0; c < col; c++)
	{
	#pragma HLS pipeline
		for(int r = 0; r < row; r++)
		{
			in1[r].real(middle[r][c].real());
			in1[r].imag(middle[r][c].imag());
			//in[r] = middle[r][c];
		}
		fft_top(direction,in1,out1,&ovflo);
		for(int r = 0; r < row; r++)
		{
			xk1[r][c].real(out1[r].real()/256);
			xk1[r][c].imag(out1[r].imag()/256);
			//xk1[r][c] = out[r];
			//printf("xk1	%d	real: %f	image: %f \n",r,xk1[r][c].real(),xk1[r][c].imag());
		}
	}

由代码可见就是逐行逐列去进行一维的fft,而逆变换同理,有

//傅里叶逆变换(行)
	for(int r = 0; r < rows; r++)
	{
		for(int c = 0; c < cols; c++)
		{
			in1[c].real(xk1[r][c].real());
			in1[c].imag(xk1[r][c].imag());
			//in[c] = xk1[r][c];
			//printf("in %d num %f \n",c,in[c].real());
		}
		fft_top(~direction,in1,out1,&ovflo);
		for(int c = 0; c < cols; c++)
		{
			middle2[r][c].real(out1[c].real());
			middle2[r][c].imag(out1[c].imag());
			//middle2[r][c] = out[c];
			//printf("in %d num %f \n",c,out[c].real());
		}
	}
	//傅里叶逆变换(列)
	for(int c = 0; c < cols; c++)
	{
		for(int r = 0; r < rows; r++)
		{
			in1[r].real(middle2[r][c].real());
			in1[r].imag(middle2[r][c].imag());
			//in[r] = middle2[r][c];
		}
		fft_top(~direction,in1,out1,&ovflo);
		for(int r = 0; r < rows; r++)
		{
			xk2[r][c].real(out1[r].real());
			xk2[r][c].imag(out1[r].imag());
			//xk2[r][c] = out[r];
			//printf("xk2	%d	real: %f	image: %f \n",r,xk2[r][c].real(),xk2[r][c].imag());
		}
	}

数据的读入和读出

读入

最外层函数的硬件接口定义如下,其输入为数据流

void WienerDeblur(wide_stream* in_stream, wide_stream* out_stream, ap_uint<32> rows, ap_uint<32> cols){
#pragma HLS INTERFACE axis port=in_stream bundle=INPUT
#pragma HLS INTERFACE axis port=out_stream bundle=OUTPUT

#pragma HLS INTERFACE s_axilite port=rows bundle=CONTROL_BUS offset=0x14 clock=AXI_LITE_clk
#pragma HLS INTERFACE s_axilite port=cols bundle=CONTROL_BUS offset=0x1C clock=AXI_LITE_clk
#pragma HLS INTERFACE s_axilite port=return bundle=CONTROL_BUS clock=AXI_LITE_clk

#pragma HLS INTERFACE ap_stable port=rows
#pragma HLS INTERFACE ap_stable port=cols

与常见的c++不太一样,这不能直接读一个二维数组进来,只能一个一个读,毕竟实际上是硬件。

接下来的读取可以用如下方式循环读出,将dat的32位数据分别写入src_bw中。

GRAY_IMAGE src_bw(rows, cols);
	for(int r = 0; r < packets; r++){
	#pragma HLS pipeline II=4
		ap_uint<32> dat = in_stream->data;
		src_bw.write(GRAY_PIXEL(dat.range(7,0)));
		src_bw.write(GRAY_PIXEL(dat.range(15,8)));
		src_bw.write(GRAY_PIXEL(dat.range(23,16)));
		src_bw.write(GRAY_PIXEL(dat.range(31,24)));
		++in_stream;
	}

读出

读出与读入同理,我们需要将我们的二维数组转化成数据流,然后逐一吐给外面的设备。
那么我们就先把ifft得到的复数矩阵逐一写到一个GRAY_IMAGE res(rows, cols);变量中,这个GRAY_IMAGE的定义为

typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC1>     GRAY_IMAGE;

是hls_video库中的内容。
接下来和读入数据一样,通过循环将数据写入out_stream

	for(int r = 0; r < rows; r++)
	{
		for(int c = 0; c < cols; c++)
		{
			element_pixel.val[0] = (abs(xk2[r][c].real()));
			element_pixel_imag.val[0] = (abs(xk2[r][c].imag()));
			res << element_pixel;
			res_imag << element_pixel_imag;
		}
	}


    for(int r = 0; r < rows; r++){
    #pragma HLS pipeline II=4
		for(int c = 0; c < col_packets; c++){
			ap_uint<32> dat;
			dat.range(7,0) = res.read().val[0];
			dat.range(15,8) = res.read().val[0];
			dat.range(23,16) = res.read().val[0];
			dat.range(31,24) = res.read().val[0];
			out_stream->data = dat;
			out_stream->user = (r == 0 && c == 0)? 1: 0;
			out_stream->last = (r == rows-1 && c == col_packets-1)? 1: 0;
			++out_stream;
		}
	}

维纳滤波操作

在完成了二维fft的实现后,维纳滤波就很简单了

其表达式为

F ^ ( u , v ) = [ 1 H ( u , b ) ∣ H ( u , v ) ∣ 2 ( ∣ H ( u , v ) ∣ 2 + k ) ] G ( u , v ) \hat F (u,v)=[\frac{1}{H(u,b)} \frac{ |H(u,v)|^2}{(|H(u,v)|^2+k)}]G(u,v) F^(u,v)=[H(u,b)1(H(u,v)2+k)H(u,v)2]G(u,v)

我们只需要将变换后的频域矩阵进行相应的点乘除即可。
可以定义相关的函数


void InnerProd(
	cmpxData in1[Kernel_Size][Kernel_Size],
	cmpxData in2[Kernel_Size][Kernel_Size],
	cmpxData out[Kernel_Size][Kernel_Size])
{
	for(int r = 0; r < Kernel_Size; r++)
#pragma HLS PIPELINE rewind
		for(int c = 0; c < Kernel_Size; c++ )
#pragma HLS UNROLL
			out[r][c] = in1[r][c]*in2[r][c];
}

void matrix_conj(
	cmpxData mat_in[Kernel_Size][Kernel_Size],
	cmpxData mat_out[Kernel_Size][Kernel_Size])
{
	for(int r = 0; r < Kernel_Size; r++)
		for(int c = 0; c < Kernel_Size; c++ )
		{
			mat_out[r][c].real( mat_in[r][c].real());
			mat_out[r][c].imag(-mat_in[r][c].imag());
		}
}

void matrix_modulus(
	cmpxData mat_in[Kernel_Size][Kernel_Size],
	cmpxData mat_out[Kernel_Size][Kernel_Size])
{
	for(int r = 0; r < Kernel_Size; r++)
		for(int c = 0; c < Kernel_Size; c++ )
		{
			mat_out[r][c].real(sqrt(pow(mat_in[r][c].real(),2) +
							        pow(mat_in[r][c].imag(),2)));
			mat_out[r][c].imag(0);
		}
}

void matrix_plus_SNR(
	cmpxData mat_in[Kernel_Size][Kernel_Size],
	cmpxData mat_out[Kernel_Size][Kernel_Size],
	data_t k)
{
	for(int r = 0; r < Kernel_Size; r++)
		for(int c = 0; c < Kernel_Size; c++ )
		{
			mat_out[r][c].real(mat_in[r][c].real() + k);
			mat_out[r][c].imag(mat_in[r][c].imag());
		}
}

void matrix_div(
	cmpxData mat_in1[Kernel_Size][Kernel_Size],
	cmpxData mat_in2[Kernel_Size][Kernel_Size],
	cmpxData mat_out[Kernel_Size][Kernel_Size])
{
	for(int r = 0; r < Kernel_Size; r++)
		for(int c = 0; c < Kernel_Size; c++ )
		{
			mat_out[r][c] = mat_in1[r][c] / mat_in2[r][c];
		}
}

然后调用函数完成计算。

	static cmpxData gauss_blur[Kernel_Size][Kernel_Size];

	InnerProd(xk1,fft_kernel,gauss_blur);

	static cmpxData fft_kernel_modu[Kernel_Size][Kernel_Size];

	matrix_modulus(fft_kernel,fft_kernel_modu);//get modulus of the kernel

	static cmpxData fft_kernel_modu2[Kernel_Size][Kernel_Size];

	InnerProd(fft_kernel_modu,fft_kernel_modu,fft_kernel_modu2);//get square modelus

	static cmpxData G1[Kernel_Size][Kernel_Size];
#pragma HLS interface ap_fifo depth=1024 port=G1,fft_kernel_modu2
	matrix_plus_SNR(fft_kernel_modu2,G1,0);

	matrix_div(fft_kernel_modu2,G1,G1);

	static cmpxData G[Kernel_Size][Kernel_Size];

	matrix_div(G1,fft_kernel,G);

	InnerProd(gauss_blur,G,xk1);

你可能感兴趣的:(xilinx暑期学校)