版权声明:本文为博主原创文章,遵循 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@60、8K@30、8K@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
- 框图
本文中的一些没贴出的模块代码函数代码在连接中能找到
- System Verilog 视频缩放图像缩放 vivado 仿真 https://blog.csdn.net/qq_46621272/article/details/126439519
视频缩放相关文章
- 《FPGA verilog 临近插值任意比例视频缩放代码》
- 《System Verilog 视频缩放图像缩放 vivado 仿真》