当前位置:   article > 正文

FPGA HLS图像缩放,双线性插值任意比例缩放,提供4套工程源码和技术支持_图像缩放 fpga

图像缩放 fpga

1、双线性插值算法简介

已知的红色数据点与待插值得到的绿色点。
在这里插入图片描述
假如我们想得到未知函数 f 在点 P= (x,y) 的值,假设我们已知函数 f 在 Q11 = (x1,y1)、Q12 = (x1,y2),Q21 = (x2,y1) 以及 Q22 = (x2,y2) 四个点的值。首先在 x 方向进行线性插值,得到 R1 和 R2,然后在 y 方向进行线性插值,得到 P。这样就得到所要的结果 f(x,y)。其中红色点 Q11,Q12,Q21,Q22 为已知的 4 个像素点

第一步:X 方向的线性插值,在 Q12,Q22 中插入蓝色点 R2,Q11,Q21 中插入蓝色点R1;
第二步:Y 方向的线性插值,通过第一步计算出的 R1 与 R2 在 y 方向上插值计算出 P点。
线性插值的结果与插值的顺序无关。首先进行 y 方向的插值,然后进行 x 方向的插值,所得到的结果是一样的。双线性插值的结果与先进行哪个方向的插值无关。如果选择一个坐标系统使得四个已知点坐标分别为 (0, 0)、(0, 1)、(1, 0) 和 (1, 1),那
么插值公式就可以化简为f(x,y)=f(0,0)(1-x)(1-y)+f(1,0)x(1-y)+f(0,1)(1-x)y+f(1,1)xy,具体的双线性插值算法描述如下:

对于一个目标像素,通过反向变换得到的浮点坐标为(i+u,j+v) (其中 i、j 均为浮点坐标的整数部分,u、v 为浮点坐标的小数部分,是取值[0,1)区间的浮点数),则这个像素值 f(i+u,j+v) 可由原图像中坐标为 (i,j)、(i+1,j)、(i,j+1)、(i+1,j+1)所对应的周围四个像素的值决定,即:f(i+u,j+v) = (1-u)(1-v)f(i,j) + (1-u)vf(i,j+1) + u(1-v)f(i+1,j) + uvf(i+1,j+1);其中 f(i,j)表示源图像(i,j)处的的像素值,以此类推。比如,刚才的例子,现在假定目标图的像素坐标为(1,1),那么反推得到的对应于源图的坐标是(0.75 , 0.75), 这其实只是一个概念上的虚拟像素,实际在源图中并不存在这样一个像素,那么目标图的像素(1,1)的取值不能够由这个虚拟像素来决定,而只能由源图的这四个像素共同决定:(0,0)(0,1)(1,0)(1,1),而由于(0.75,0.75)离(1,1)要更近一些,那么(1,1)所起的决定作用更大一些,这从公式 1 中的系数 uv=0.75×0.75就可以体现出来,而(0.75,0.75)离(0,0)最远,所以(0,0)所起的决定作用就要小一些,公式中系数为(1-u)(1-v)=0.25×0.25 也体现出了这一特点。

2、双线性插值算法的HLS实现与IP封装

建立HLS工程,粘贴复制源码编译即可,然后封装IP,HLS源码在文章末尾。
封装后的IP如下图:
在这里插入图片描述
IP不需要点击配置,直接连线即可使用。

3、本缩放方案的优缺点及性能

1:最大分辨率支持1920x1080@60Hz,如果需要更大分辨率,需要更改HLS代码,很简单;
2:输入输出接口为AXI4-Stream,适用于Xilinx官方推荐的基于VDMA的凸显缓存方案,注意,输入输出接口不是VGA;
3:支持任意比例、任意尺寸的图像缩放,也就是常说的无极缩放;
4:HLS方式实现,即Xilinx的C++语言实现,然后由HLS工具翻译为verilog代码,所以verilog代码的阅读性不强;
5:IP需要配置才能使用,需要zynq平台,如果使用7系列FPGA,则需要调用MicroBlaze软核模拟zynq使用;
6:对于不习惯使用zynq或者不喜欢调用MicroBlaze软核或者所使用的FPGA逻辑资源有限的用户,可以使用我的另一款图像缩放方案,该方案的视频输入输出为VGA时序,不需要配置,占用资源很少,也支持任意比例、任意尺寸的图像缩放,博客及其源码下载链接:点击直接前往

4、vivado工程介绍

FPGA型号:zynq7100,可移植到其他FPGA型号,7系列FPGA可调用MicroBlaze实现zynq功能;
开发环境:vivado2019.1;
输入:OV5640 摄像头;
不缩放测试:OV5640 摄像头720P分辨率不缩放正常输出;
放大测试:将 720P 分辨率图像放大到 1080P输出;
缩小测试:将 720P 分辨率图像缩小到 640x480输出;
本章实现的缩放 IP 主要用于功能验证,可以在此基础上,对数据流进行进一步处理,这里不在赘述。

5、OV5640摄像头720P原分辨率输出实验

此工程的目的是为了对比缩放的效果;
输入:OV5640 摄像头,分辨率720P;
输出:HDMI显示背景1080P,有效图像区720P;
BD工程如下:
在这里插入图片描述
SDK源码如下:

#include "I2C_16bit.h"
#include "xiicps.h"
#include "xil_io.h"
#include "xparameters.h"
#include "color.h"

#define VDMA_BASEADDR	XPAR_AXI_VDMA_0_BASEADDR

#define VIDEO_BASEADDR0 0x01000000
#define VIDEO_BASEADDR1 0x02000000
#define VIDEO_BASEADDR2 0x03000000

#define H_ACTIVE	1920
#define V_ACTIVE	1080
#define H_STRIDE	1920

XIicPs	Iic;

u32 i=0;
//#define SUM   8294400 //背景写黑  1920*1080*4
#define SUM   H_ACTIVE*V_ACTIVE*4 //背景写黑  1920*1080*4

void main()
{
	// Initialize OV5640 regesiter
	I2C_config_init();
	//设置内存中的背景
	for(i=0;i<SUM;i++){
		Xil_Out16((VIDEO_BASEADDR0 + i), 0x00);
		Xil_Out16((VIDEO_BASEADDR1 + i), 0x00);
		Xil_Out16((VIDEO_BASEADDR2 + i), 0x00);
	}
	//VDMA_WRITE
	Xil_Out32((VDMA_BASEADDR + 0x030), 0x108B);// enable circular mode
	Xil_Out32((VDMA_BASEADDR + 0x0AC), VIDEO_BASEADDR0);	// start address
	Xil_Out32((VDMA_BASEADDR + 0x0B0), VIDEO_BASEADDR1);	// start address
	Xil_Out32((VDMA_BASEADDR + 0x0B4), VIDEO_BASEADDR2);	// start address
	Xil_Out32((VDMA_BASEADDR + 0x0A8), (H_STRIDE*3));		// h offset (H_STRIDE* 3) bytes
	Xil_Out32((VDMA_BASEADDR + 0x0A4), (H_ACTIVE*3));		// h size (H_ACTIVE * 3) bytes
	Xil_Out32((VDMA_BASEADDR + 0x0A0), V_ACTIVE);			// v size (V_ACTIVE)
	//VDMA_READ
	Xil_Out32((VDMA_BASEADDR + 0x000), 0x8B); 		// enable circular mode
	Xil_Out32((VDMA_BASEADDR + 0x05c), VIDEO_BASEADDR0); 	// start address
	Xil_Out32((VDMA_BASEADDR + 0x060), VIDEO_BASEADDR1); 	// start address
	Xil_Out32((VDMA_BASEADDR + 0x064), VIDEO_BASEADDR2); 	// start address
	Xil_Out32((VDMA_BASEADDR + 0x058), (H_STRIDE*3)); 		// h offset (H_STRIDE * 3) bytes
	Xil_Out32((VDMA_BASEADDR + 0x054), (H_ACTIVE*3)); 		// h size (H_ACTIVE * 3) bytes
	Xil_Out32((VDMA_BASEADDR + 0x050), V_ACTIVE); 			// v size (V_ACTIVE)
	while (1) ;
}
  • 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

输出显示如下:
在这里插入图片描述
请注意看原图视野,以便和后面的缩放做对比;

6、OV5640摄像头720P缩小到640X480输出实验

此工程的目的是为了对比缩小的效果;
输入:OV5640 摄像头,分辨率720P;
输出:HDMI显示640X480P分辨率;
BD工程如下:
在这里插入图片描述
其中输出分辨率为640X480P,如下:
在这里插入图片描述
SDK源码如下:

#include "I2C_16bit.h"
#include "xiicps.h"
#include "xil_io.h"
#include "xparameters.h"
#include "color.h"
#include "xhls_video_scaler_top.h"

#define VDMA_BASEADDR	XPAR_AXI_VDMA_0_BASEADDR
#define XPAR_HLS_VIDEO_SCALER_TOP_DEVICE_ID   XPAR_HLS_VIDEO_SCALER_TOP_0_DEVICE_ID
#define VIDEO_BASEADDR0 0x01000000
#define VIDEO_BASEADDR1 0x02000000
#define VIDEO_BASEADDR2 0x03000000
//输出分辨率640X480P
#define H_ACTIVE	640
#define V_ACTIVE	480
#define H_STRIDE	640

XHls_video_scaler_top   XHls_video_scaler;
XIicPs	Iic;
u32 i=0;
#define SUM   H_ACTIVE*V_ACTIVE*4 //背景写黑  1920*1080*4

void main()
{
	// Initialize OV5640 regesiter
	I2C_config_init();
	//设置内存中的背景
	for(i=0;i<SUM;i++){
		Xil_Out16((VIDEO_BASEADDR0 + i), 0x00);
		Xil_Out16((VIDEO_BASEADDR1 + i), 0x00);
		Xil_Out16((VIDEO_BASEADDR2 + i), 0x00);
	}
	
	//scaler720P-->480X640
	XHls_video_scaler_top_Initialize(&XHls_video_scaler, XPAR_HLS_VIDEO_SCALER_TOP_DEVICE_ID);
	XHls_video_scaler_setup(720,1280,480,640);
	//VDMA_WRITE
	Xil_Out32((VDMA_BASEADDR + 0x030), 0x108B);// enable circular mode
	Xil_Out32((VDMA_BASEADDR + 0x0AC), VIDEO_BASEADDR0);	// start address
	Xil_Out32((VDMA_BASEADDR + 0x0B0), VIDEO_BASEADDR1);	// start address
	Xil_Out32((VDMA_BASEADDR + 0x0B4), VIDEO_BASEADDR2);	// start address
	Xil_Out32((VDMA_BASEADDR + 0x0A8), (H_STRIDE*3));		// h offset (H_STRIDE* 3) bytes
	Xil_Out32((VDMA_BASEADDR + 0x0A4), (H_ACTIVE*3));		// h size (H_ACTIVE * 3) bytes
	Xil_Out32((VDMA_BASEADDR + 0x0A0), V_ACTIVE);			// v size (V_ACTIVE)
	//VDMA_READ
	Xil_Out32((VDMA_BASEADDR + 0x000), 0x8B); 		// enable circular mode
	Xil_Out32((VDMA_BASEADDR + 0x05c), VIDEO_BASEADDR0); 	// start address
	Xil_Out32((VDMA_BASEADDR + 0x060), VIDEO_BASEADDR1); 	// start address
	Xil_Out32((VDMA_BASEADDR + 0x064), VIDEO_BASEADDR2); 	// start address
	Xil_Out32((VDMA_BASEADDR + 0x058), (H_STRIDE*3)); 		// h offset (H_STRIDE * 3) bytes
	Xil_Out32((VDMA_BASEADDR + 0x054), (H_ACTIVE*3)); 		// h size (H_ACTIVE * 3) bytes
	Xil_Out32((VDMA_BASEADDR + 0x050), V_ACTIVE); 			// v size (V_ACTIVE)
	while (1) ;
}
  • 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

输出显示如下:
在这里插入图片描述

7、OV5640摄像头720P放大到1080P输出实验

此工程的目的是为了对比放大的效果;
输入:OV5640 摄像头,分辨率1080P;
输出:HDMI显示1080P分辨率;
BD工程如下:
在这里插入图片描述
其中输出分辨率为1080P,如下:
在这里插入图片描述
SDK源码如下:

#include "I2C_16bit.h"
#include "xiicps.h"
#include "xil_io.h"
#include "xparameters.h"
#include "color.h"
#include "xhls_video_scaler_top.h"

#define VDMA_BASEADDR	XPAR_AXI_VDMA_0_BASEADDR
#define XPAR_HLS_VIDEO_SCALER_TOP_DEVICE_ID   XPAR_HLS_VIDEO_SCALER_TOP_0_DEVICE_ID
#define VIDEO_BASEADDR0 0x01000000
#define VIDEO_BASEADDR1 0x02000000
#define VIDEO_BASEADDR2 0x03000000
//输出分辨率:1080P
#define H_ACTIVE	1920
#define V_ACTIVE	1080
#define H_STRIDE	1920

XHls_video_scaler_top   XHls_video_scaler;
XIicPs	Iic;

u32 i=0;
#define SUM   H_ACTIVE*V_ACTIVE*4 //背景写黑  1920*1080*4

void main()
{
	// Initialize OV5640 regesiter
	I2C_config_init();
	//设置内存中的背景
	for(i=0;i<SUM;i++){
		Xil_Out16((VIDEO_BASEADDR0 + i), 0x00);
		Xil_Out16((VIDEO_BASEADDR1 + i), 0x00);
		Xil_Out16((VIDEO_BASEADDR2 + i), 0x00);
	}	
	scaler720P-->1080P
	XHls_video_scaler_top_Initialize(&XHls_video_scaler, XPAR_HLS_VIDEO_SCALER_TOP_DEVICE_ID);
	XHls_video_scaler_setup(720,1280,720,1280);
	//VDMA_WRITE
	Xil_Out32((VDMA_BASEADDR + 0x030), 0x108B);// enable circular mode
	Xil_Out32((VDMA_BASEADDR + 0x0AC), VIDEO_BASEADDR0);	// start address
	Xil_Out32((VDMA_BASEADDR + 0x0B0), VIDEO_BASEADDR1);	// start address
	Xil_Out32((VDMA_BASEADDR + 0x0B4), VIDEO_BASEADDR2);	// start address
	Xil_Out32((VDMA_BASEADDR + 0x0A8), (H_STRIDE*3));		// h offset (H_STRIDE* 3) bytes
	Xil_Out32((VDMA_BASEADDR + 0x0A4), (H_ACTIVE*3));		// h size (H_ACTIVE * 3) bytes
	Xil_Out32((VDMA_BASEADDR + 0x0A0), V_ACTIVE);			// v size (V_ACTIVE)
	//VDMA_READ
	Xil_Out32((VDMA_BASEADDR + 0x000), 0x8B); 		// enable circular mode
	Xil_Out32((VDMA_BASEADDR + 0x05c), VIDEO_BASEADDR0); 	// start address
	Xil_Out32((VDMA_BASEADDR + 0x060), VIDEO_BASEADDR1); 	// start address
	Xil_Out32((VDMA_BASEADDR + 0x064), VIDEO_BASEADDR2); 	// start address
	Xil_Out32((VDMA_BASEADDR + 0x058), (H_STRIDE*3)); 		// h offset (H_STRIDE * 3) bytes
	Xil_Out32((VDMA_BASEADDR + 0x054), (H_ACTIVE*3)); 		// h size (H_ACTIVE * 3) bytes
	Xil_Out32((VDMA_BASEADDR + 0x050), V_ACTIVE); 			// v size (V_ACTIVE)
	while (1) ;
}
  • 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

输出显示如下:
在这里插入图片描述

8、福利:工程源码获取

福利:工程代码的获取
代码太大,无法邮箱发送,以某度网盘链接方式发送,
资料获取方式:私,或者文章末尾的V名片。
网盘资料如下:
在这里插入图片描述
此外,有很多朋友给本博主提了很多意见和建议,希望能丰富服务内容和选项,因为不同朋友的需求不一样,所以本博主还提供以下服务:
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/569221
推荐阅读
相关标签
  

闽ICP备14008679号