FPGA双线性插值图像缩放详细讲解,上板验证稳定通过,提供两套工程源码

开局直接放大招:提供源码及工程;
重点讲解双线性插值图像缩放;
此功能模块使用HLS实现,并已封装导出IP,可在工程中添加并使用,可提供HLS工程源码;
若是用verilog实现双线性插值图像缩放,是一项及其复杂的事情,且不能做到任意比例,网上也有源码,不信你可以去下载并验证,肯定做不到任意比例缩放;这样一来,HLS则成为了更好的选项,偷偷告诉你,HLS几行代码就搞定了;关于这个ip可以看我前面的文章HLS实现双线性插值图像缩放
前面文章里的IP适用于zynq系列机器,这里讲的这个IP适用于7系列机器;
前面废话太多了,直接上工程:
工程1:1080P缩小至720P;
开发板:Xilinx Kintex7 开发板;
开发环境:vivado2019.1;
输入:HDMI-1080P;
输出:HDMI-720P;
工程架构如下:
FPGA双线性插值图像缩放详细讲解,上板验证稳定通过,提供两套工程源码_第1张图片
用电脑输出1080P视频模拟输入;
输入HDMI视频经IT6802解码为VGA格式的RGB视频流,关于IT6802的芯片解读和寄存器配置,请参考我前面的文章IT6802的芯片解读和寄存器配置
然后用Xilinx官方IP将VGA格式的RGB视频流转为AXIS视频流;
经自定义双线性插值图像缩放IP VIDEO SCALER将原视频缩小至720P;
后面就是Xilinx的图像处理套路了,什么VDMA之类的,这里就不多说了;
BD工程如下:
FPGA双线性插值图像缩放详细讲解,上板验证稳定通过,提供两套工程源码_第2张图片
代码架构如下:
FPGA双线性插值图像缩放详细讲解,上板验证稳定通过,提供两套工程源码_第3张图片
SDK软件文件如下:
FPGA双线性插值图像缩放详细讲解,上板验证稳定通过,提供两套工程源码_第4张图片
VDMA配置c代码如下:

#include "xil_io.h"
#include "xparameters.h"
#include "helai_vdma.h"

#define VDMA_BASEADDR	XPAR_AXI_VDMA_0_BASEADDR
#define VIDEO_BASEADDR0 0x80000000
#define VIDEO_BASEADDR1 0x81000000
#define VIDEO_BASEADDR2 0x82000000

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

u32 i=0;
#define SUM   H_ACTIVE*V_ACTIVE*4 //color back 1920*1080*4

void helai_vdma(){
	//set color back to black
	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)
}

主函数c代码如下:

#include 
#include "xgpio.h"
#include "oak_iic.h"
#include "unistd.h"
#include "helai_vdma.h"
#include "helai_color_back.h"
#include "xvideo_scaler.h"

XVideo_scaler K7_XVideo_scaler;
XGpio_Config *XGpioCfg;
XGpio led_gpio;
#define	AXI_GPIO_DEVICE_ID	XPAR_GPIO_0_DEVICE_ID

int main(){
	XGpioCfg = XGpio_LookupConfig(AXI_GPIO_DEVICE_ID);
	XGpio_CfgInitialize(&led_gpio, XGpioCfg, XGpioCfg->BaseAddress);
	XGpio_SetDataDirection(&led_gpio, 1, 0);	//output
	XGpio_DiscreteWrite(&led_gpio, 1, 0);
	oak_i2c_init(IT6802_IIC_BASEADDR, 100000, 0x90>>1, IIC_REG_LEN8, IIC_DATA_LEN8);
	IT6802_Init(IT6802_IIC_BASEADDR);
	XVideo_scaler_Initialize(&K7_XVideo_scaler, XPAR_VIDEO_SCALER_0_DEVICE_ID);
	XHls_video_scaler_setup(1080,1920,540,960);
	helai_vdma();
	while(1){
		usleep(500000);
		XGpio_DiscreteWrite(&led_gpio, 1, 1);
		usleep(500000);
		XGpio_DiscreteWrite(&led_gpio, 1, 0);
	}
}

工程2:1080P缩小至960x540并用video mixer二分屏显示;
开发板:Xilinx Kintex7 开发板;
开发环境:vivado2019.1;
输入:HDMI-1080P;
输出:HDMI-960x540二分屏显示;
工程架构如下:
FPGA双线性插值图像缩放详细讲解,上板验证稳定通过,提供两套工程源码_第5张图片

用电脑输出1080P视频模拟输入;
输入HDMI视频经IT6802解码为VGA格式的RGB视频流,关于IT6802的芯片解读和寄存器配置,请参考我前面的文章IT6802的芯片解读和寄存器配置
然后调用两个Xilinx官方IP将VGA格式的RGB视频流转为两路AXIS视频流;
调用两个自定义双线性插值图像缩放IP VIDEO SCALER将原视频缩小至720P;
后面就是Xilinx的图像处理套路了,什么VDMA之类的,这里就不多说了;这里是两路视频,所以IP要用两个
BD工程如下:
FPGA双线性插值图像缩放详细讲解,上板验证稳定通过,提供两套工程源码_第6张图片
代码架构如下:
FPGA双线性插值图像缩放详细讲解,上板验证稳定通过,提供两套工程源码_第7张图片
SDK软件文件如下:
FPGA双线性插值图像缩放详细讲解,上板验证稳定通过,提供两套工程源码_第8张图片
VDMA配置c代码如下:

#include "xil_io.h"
#include "xparameters.h"
#include "helai_vdma.h"

#define VDMA0_BASEADDR	XPAR_AXIVDMA_0_BASEADDR
#define VDMA1_BASEADDR	XPAR_AXIVDMA_1_BASEADDR

//1frame video = 960*540*3=0x17bb00
#define VIDEO0_BASEADDR0 0x80000000
#define VIDEO0_BASEADDR1 0x81000000
#define VIDEO0_BASEADDR2 0x82000000

#define VIDEO1_BASEADDR0 0x83000000
#define VIDEO1_BASEADDR1 0x84000000
#define VIDEO1_BASEADDR2 0x85000000

#define H_ACTIVE	960
#define V_ACTIVE	540
#define H_STRIDE	960

void helai_vdma(){
	//VDMA0
	//VDMA_WRITE
	Xil_Out32((VDMA0_BASEADDR + 0x030), 0x108B);// enable circular mode
	Xil_Out32((VDMA0_BASEADDR + 0x0AC), VIDEO0_BASEADDR0);	// start address
	Xil_Out32((VDMA0_BASEADDR + 0x0B0), VIDEO0_BASEADDR1);	// start address
	Xil_Out32((VDMA0_BASEADDR + 0x0B4), VIDEO0_BASEADDR2);	// start address
	Xil_Out32((VDMA0_BASEADDR + 0x0A8), (H_STRIDE*3));		// h offset (H_STRIDE* 3) bytes
	Xil_Out32((VDMA0_BASEADDR + 0x0A4), (H_ACTIVE*3));		// h size (H_ACTIVE * 3) bytes
	Xil_Out32((VDMA0_BASEADDR + 0x0A0), V_ACTIVE);			// v size (V_ACTIVE)
	//VDMA_READ
	Xil_Out32((VDMA0_BASEADDR + 0x000), 0x8B); 		// enable circular mode
	Xil_Out32((VDMA0_BASEADDR + 0x05c), VIDEO0_BASEADDR0); 	// start address
	Xil_Out32((VDMA0_BASEADDR + 0x060), VIDEO0_BASEADDR1); 	// start address
	Xil_Out32((VDMA0_BASEADDR + 0x064), VIDEO0_BASEADDR2); 	// start address
	Xil_Out32((VDMA0_BASEADDR + 0x058), (H_STRIDE*3)); 		// h offset (H_STRIDE * 3) bytes
	Xil_Out32((VDMA0_BASEADDR + 0x054), (H_ACTIVE*3)); 		// h size (H_ACTIVE * 3) bytes
	Xil_Out32((VDMA0_BASEADDR + 0x050), V_ACTIVE); 			// v size (V_ACTIVE)

	//VDMA1
	//VDMA_WRITE
	Xil_Out32((VDMA1_BASEADDR + 0x030), 0x108B);// enable circular mode
	Xil_Out32((VDMA1_BASEADDR + 0x0AC), VIDEO1_BASEADDR0);	// start address
	Xil_Out32((VDMA1_BASEADDR + 0x0B0), VIDEO1_BASEADDR1);	// start address
	Xil_Out32((VDMA1_BASEADDR + 0x0B4), VIDEO1_BASEADDR2);	// start address
	Xil_Out32((VDMA1_BASEADDR + 0x0A8), (H_STRIDE*3));		// h offset (H_STRIDE* 3) bytes
	Xil_Out32((VDMA1_BASEADDR + 0x0A4), (H_ACTIVE*3));		// h size (H_ACTIVE * 3) bytes
	Xil_Out32((VDMA1_BASEADDR + 0x0A0), V_ACTIVE);			// v size (V_ACTIVE)
	//VDMA_READ
	Xil_Out32((VDMA1_BASEADDR + 0x000), 0x8B); 		// enable circular mode
	Xil_Out32((VDMA1_BASEADDR + 0x05c), VIDEO1_BASEADDR0); 	// start address
	Xil_Out32((VDMA1_BASEADDR + 0x060), VIDEO1_BASEADDR1); 	// start address
	Xil_Out32((VDMA1_BASEADDR + 0x064), VIDEO1_BASEADDR2); 	// start address
	Xil_Out32((VDMA1_BASEADDR + 0x058), (H_STRIDE*3)); 		// h offset (H_STRIDE * 3) bytes
	Xil_Out32((VDMA1_BASEADDR + 0x054), (H_ACTIVE*3)); 		// h size (H_ACTIVE * 3) bytes
	Xil_Out32((VDMA1_BASEADDR + 0x050), V_ACTIVE); 			// v size (V_ACTIVE)
}

主函数c代码如下:

#include 
#include "xgpio.h"
#include "oak_iic.h"
#include "unistd.h"
#include "helai_vdma.h"
#include "helai_color_back.h"
#include "xvideo_scaler.h"
#include "helai_mixer.h"

XVideo_scaler K7_XVideo_scaler0,K7_XVideo_scaler1;
XGpio_Config *XGpioCfg;
XGpio led_gpio;
#define	AXI_GPIO_DEVICE_ID	XPAR_GPIO_0_DEVICE_ID

int main(){
	XGpioCfg = XGpio_LookupConfig(AXI_GPIO_DEVICE_ID);
	XGpio_CfgInitialize(&led_gpio, XGpioCfg, XGpioCfg->BaseAddress);
	XGpio_SetDataDirection(&led_gpio, 1, 0);	//output
	XGpio_DiscreteWrite(&led_gpio, 1, 0);
	oak_i2c_init(IT6802_IIC_BASEADDR, 100000, 0x90>>1, IIC_REG_LEN8, IIC_DATA_LEN8);
	IT6802_Init(IT6802_IIC_BASEADDR);

	XVideo_scaler_Initialize(&K7_XVideo_scaler0, XPAR_VIDEO_SCALER_0_DEVICE_ID);
	XHls_video_scaler_setup(&K7_XVideo_scaler0,1080,1920,540,960);
	XVideo_scaler_Initialize(&K7_XVideo_scaler1, XPAR_VIDEO_SCALER_1_DEVICE_ID);
	XHls_video_scaler_setup(&K7_XVideo_scaler1,1080,1920,540,960);
	helai_vdma();
	helai_mixer();
	while(1){
		usleep(500000);
		XGpio_DiscreteWrite(&led_gpio, 1, 1);
		usleep(500000);
		XGpio_DiscreteWrite(&led_gpio, 1, 0);
	}
}

上板调试:
给出工程2的调试输出视频:

hdmi_scaler_mixer2

福利:提供源码及工程;

你可能感兴趣的:(菜鸟FPGA图像处理专题,fpga开发)