FPGA verilog 临近插值任意比例视频缩小代码(多像素并行,能支持8K60)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_46621272/article/details/127193475


FPGA verilog 临近插值任意比例视频缩小代码(多像素并行,能支持8K60)


前言

  • 采用一个时钟处理多个像素的并行算法,能支持高达 8k@60 的视频信号。
  • 视频分割算法,视频拼接算法。
  • 图像分割算法,图像拼接算法。
  • 临近插值图像视频缩放模块。
  • 支持水平缩小、垂直缩小。支持任意比列的缩小算法。
  • 不到 200 行代码,占用FPGA资源极少。
  • 在 XILINX Artix-7 FPGA 上轻松实现 8 路 1080P60 视频分割。
  • 在 XILINX Artix-7 FPGA 上轻松实现 4k@30,4k@60,8k@30,8k@60 视频缩小算法。
  • 非常适合做动态视频监控中的多画面分割。
  • 由于临近算法的先天不足,不适用 PPT、地图、医学影像等静态视频图像的应用。
  • Syetem Verilog 源代码

简介

  • 临近缩放实现简介
  • 临近缩小,就是将合适的像素保留,不合适的像素舍弃。
  • 算法实现可以参见 “用 C 语言编写的临近缩放算法” https://blog.csdn.net/qq_46621272/article/details/126459136

效果图片

缩小算法 480x270 原图
在这里插入图片描述
缩小,479x269 图片
在这里插入图片描述
缩小,241x136 图片
在这里插入图片描述
缩小,159x89 图片
在这里插入图片描述

临近插值任意比例视频缩放代码,多像素并行 video_scale_down_near_mp.sv

  • 这个代码,主要是阐述多像素并行算法的实现。代码在仿真测试中出现一些 BUG,有些分辨率的输出会出错
  • System verilog
// video_scale_down_near_mp.sv
// 支持每个时钟多个像素并行处理。比如 4K@30 模式可以用双像素实现,4K@608K@308K@60 可以用四、八、十六像素实现。
// 简化版临近插值视频缩放模块。只支持水平缩小、垂直缩小。支持任意比列的缩小算法。代码非常少,占用FPGA资源也很少。
// 非常适合做动态视频监控中的多画面分割。由于临近算法的先天不足,不适用 PPT、地图、医学影像等静态视频图像的应用。
// 免责申明:本代码仅供学习、交流、参考。本人不保证代码的完整性正确性。由于使用本代码而产生的各种纠纷本人不负担任何责任。
// 708907433@qq.com
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
module video_scale_down_near_mp	#
(
	parameter	iPN		= 1,				//每个时钟像素数量
	parameter	iDN		= 8,				//像素颜色深度
	parameter	iCN		= 3					//颜色数
)
(
	input										vin_clk,
	input										rst_n,
	input										frame_sync_n,	//输入视频帧同步复位,低有效
	input	[iPN-1:0][iCN-1:0][iDN-1:0]			vin_dat,		//输入视频数据
	input										vin_valid,		//输入视频数据有效
	output										vin_ready,		//输入准备好
	output	logic[iPN-1:0][iCN-1:0][iDN-1:0]	vout_dat,		//输出视频数据
	output	logic								vout_valid,			//输出视频数据有效
	input										vout_ready,			//输出准备好
	input	[15:0]								vin_xres,			//输入视频水平分辨率
	input	[15:0]								vin_yres,			//输入视频垂直分辨率
	input	[15:0]								vout_xres,			//输出视频水平分辨率
	input	[15:0]								vout_yres			//输出视频垂直分辨率
);
	logic	[15:0]		vin_xres_xz;			//输入视频水平分辨率,对齐矫正值
	logic	[15:0]		vout_xres_xz;			//输出视频垂直分辨率,对齐矫正值
	logic	[31:0]		scaler_height	= 0;	//垂直缩放系数,[31:16]高16位是整数,低16位是小数
	logic	[31:0]		scaler_width	= 0;	//水平缩放系数,[31:16]高16位是整数,低16位是小数
	logic	[15:0]		vin_x			= 0;	//输入视频水平计数
	logic	[15:0]		vin_y			= 0;	//输入视频垂直计数
	logic	[iPN-1:0][31:0]	vout_x		= 0;	//输出视频水平计数,定浮点数,[31:16]高16位是整数部分
	logic	[iPN-1:0][15:0]	vout_x_int;
	logic	[iPN-1:0]		valid		= 0;
	logic	[31:0]		vout_y			= 0;	//输出视频垂直计数,定浮点数,[31:16]高16位是整数部分
	logic	[10:0]		cnt = 0;
	genvar			n;
	assign	vin_ready			= vout_ready;	//流控信号

	always@(posedge vin_clk)
	begin
		if(rst_n == 0)
			cnt <= 0;						//延时计数器,在帧同步脉冲到来后,延时一段时间再输出视频流数据
		else if(frame_sync_n == 1)
			cnt <= 0;
		else if( cnt != '1 )
			cnt <= cnt + 1;
	end
	
	always@(posedge	cnt[10])
	begin
		scaler_width	<= #1 ((vin_xres << 16 )/vout_xres) + 1;	//视频水平缩放比例,2^16*输入宽度/输出宽度
		scaler_height	<= #1 ((vin_yres << 16 )/vout_yres) + 1;	//视频垂直缩放比例,2^16*输入高度/输出高度
		vin_xres_xz		<= #1 (vin_xres +(iPN-1))&(16'hfff<< $clog2(iPN));
		vout_xres_xz	<= #1 (vout_xres +(iPN-1))&(16'hfff<< $clog2(iPN));
	end

	always@(posedge	vin_clk)
	begin	//输入视频水平计数和垂直计数,按像素个数计数。
		if(frame_sync_n == 0 || rst_n == 0)begin
			vin_x			<= #1 0;
			vin_y			<= #1 0;
		end
		else if (vin_valid == 1 && vout_ready == 1)begin		//当前输入视频数据有效
			if(vin_x < vin_xres_xz -iPN)begin					//vin_xres = 输入视频宽度				begin
				vin_x		<= #1 vin_x + iPN;
			end
			else begin
				vin_x		<= #1 0;
				vin_y		<= #1 vin_y + 1;
			end
		end
	end	//always
	
	always@(posedge	vin_clk)
	begin	//临近缩小算法,就是计算出要保留的像素保留,其他的像素舍弃。保留像素的水平坐标和垂直坐标
		if(frame_sync_n == 0 || rst_n == 0)begin
			vout_y			<= #1 0;
		end
		else if (vin_valid == 1 && vout_ready == 1)	begin	//当前输入视频数据有效
			if(vin_x < vin_xres_xz -iPN)begin				//vin_xres = 输入视频宽度				begin
				if (vout_y[31:16] <= vin_y)					//[31:16]高16位是整数部分
					vout_y	<= #1 vout_y + scaler_height;	//vout_y 需要保留的像素的 y 坐标
			end
		end
	end	//	always

	logic	[iPN-1:0][31:0]	step_scaler_width_start = 0;	//计数器初始值
	logic	[iPN-1:0][31:0]	step_scaler_width = 0;			//计数器步长

	parameter	BITS  = $clog2(iPN);
	parameter	BITSx = BITS == 0 ? 0:BITS-1;
	
	for( n = 0; n < iPN; n=n+1 )
	begin:for_scan_x
		assign	vout_x_int[n] = vout_x[n][31:16];
		
		always@(posedge	vin_clk)
		begin
			step_scaler_width[n]		<= #1 scaler_width * iPN; //scaler_width * ( iPN + n); 
			step_scaler_width_start[n]	<= #1 n * scaler_width; 
		end

		always@(posedge	vin_clk)
		begin
			if(frame_sync_n == 0 || rst_n == 0)begin
				vout_x[n]			<= #1 step_scaler_width_start[n];
			end
			else if (vin_valid == 1 && vout_ready == 1)begin	//当前输入视频数据有效
				if(vin_x < vin_xres_xz -iPN)begin				//vin_xres = 输入视频宽度
					if (vout_x_int[iPN-1][15:BITS] <= vin_x[15:BITS])begin
						vout_x[n]		<= #1 vout_x[n] + step_scaler_width[n];		//vout_x 需要保留的像素的 x 坐标
					end
				end
				else begin
					vout_x[n]		<= #1 step_scaler_width_start[n];
				end
			end
		end		//always_ff
	end:for_scan_x

	//vin_x,vin_y 一直在变化,随着输入视频的扫描,一线线一行行的变化
	//当 vin_x == vout_x && vin_y == vout_y 该点像素保留输出,否则舍弃该点像素。
	logic	[iPN-1:0][BITSx:0]	ct;
	logic	[iPN-1:0][iCN-1:0][iDN-1:0]	vin_dat_s;
	logic	[iPN-1:0][iCN-1:0][iDN-1:0]	vout_dat_x;
	
	for( n = 0; n < iPN; n=n+1 )
	begin:for_vout
		assign	ct[n] = vout_x_int[n][BITSx:0];
		always@(posedge	vin_clk)
		begin
			if(frame_sync_n == 0 || rst_n == 0)begin
				vout_dat_x[n]		<= #1 0;
				vin_dat_s[n]	<= #1 0;
				valid[n]		<= #1 0;
			end
			else if (vout_ready == 1)begin	//当前输入视频数据有效
				vin_dat_s[n]	<= #1 vin_dat[n];
			
				if(vout_y[31:16] == vin_y )	begin
					if (vout_x_int[n][15:BITS] == vin_x[15:BITS])begin		//[31:16]高16位是整数部分,判断是否保留该像素
						valid[n]			<= #1 vin_valid;				//置输出有效
						if(iPN == 1)
							vout_dat_x[n]	<= #1 vin_dat[0];				//该点像素保留输出
						else
							vout_dat_x[n]	<= #1 vin_dat[ct[n]];			//该点像素保留输出
					end
					else if (iPN > 1 && valid[n] == 0 && vout_x_int[n][15:BITS] == vin_x[15:BITS]-1)
					begin													//[31:16]高16位是整数部分,判断是否保留该像素
						valid[n]			<= #1 vin_valid;				//置输出有效
						vout_dat_x[n]		<= #1 vin_dat_s[ct[n]];			//该点像素保留输出
					end 
					else begin
						if( n == iPN-1) begin
							if( vin_x ==vin_xres_xz -iPN)
								valid[iPN-1]	<= #1 1;
							else
								valid[iPN-1]	<= #1 0;
						end
						else
							valid[n]			<= #1 0;				//置输出无效,舍弃该点像素。
					end
				end
				else begin
					valid[n]		<= #1 0;				//置输出无效,舍弃该点像素。
				end
			end	
		end	//	always
	end:for_vout
	
	for( n = 0; n < iPN; n=n+1 )
	begin:for_valid
		always@(posedge	vin_clk)
		begin
			if(frame_sync_n == 0 || rst_n == 0)begin
				vout_dat[n]		<= #1 0;
			end
			else if (vout_ready == 1)begin
				if(valid[n] == 1) begin
					vout_dat[n]	<= #1 vout_dat_x[n];
				end
			end
		end
	end:for_valid

	always@(posedge	vin_clk)
	begin
		if(frame_sync_n == 0 || rst_n == 0)begin
			vout_valid	<= #1 0;
		end
		else if (vout_ready == 1)begin
			vout_valid <= valid[iPN-1];
		end
	end
endmodule

仿真测试 video_scale_near_testbench.sv

  • System verilog
  • 框图
  • 在这里插入图片描述

本文中的一些没贴出的模块代码函数代码在连接中能找到

视频缩放相关文章

  • 《FPGA verilog 临近插值任意比例视频缩放代码》
  • 《System Verilog 视频缩放图像缩放 vivado 仿真》

猜你喜欢

转载自blog.csdn.net/qq_46621272/article/details/127193475