首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

FPGA实现MPEG2视频压缩 提供工程源码和技术支持

  • 25-03-04 15:08
  • 3843
  • 10656
blog.csdn.net

1、前言

MJPEG、MPEG2、MPEG4、H264 是流行且兼容性很高的 4 种视频编码格式。其中 MJPEG 对每帧独立进行 JPEG 图象压缩,而不利用帧间相关性,因此压缩效果较差。 MPEG2、MPEG4 和 H264 会进行帧间压缩,但后两者更复杂,效果也更好。MPEG2 虽然比较老 (1994年),但在低压缩率的条件下与 MPEG4 和 H264 没有明显劣势,因此 MPEG2 在高质量、低压缩率要求的应用场合仍然可堪一用。。。

为了说明这一点,下图 展示了用 MJPEG、MPEG4、H264、MPEG2 对一个分辨率为1440x704的mp4 文件进行编码的对比,该视频的宽和高为 1440x704 ,具有11帧。 表中压缩率越高越好,质量 (信噪比) 越高越好。可以看出综合考虑质量和压缩率,本模块远强于 MJPEG ,且不显著差于 MPEG4 和 H264 。。。
在这里插入图片描述
本设计使用system verilog语言设计了一个MPEG2视频压缩加速器,输入数据为YUV 444 原始像素,输出数据为端口宽度为 256 bit ,每周期可能输出 32 字节的完整 MPEG2 码流,集成了4种量化级别,越大则压缩率越高,但输出视频质量越差,可通过顶层参数配置,实用性和灵活性很高;一并提供了加速器的仿真源文件,可通过vivado或其他软件进行仿真,文章后面有详细的仿真教程和输出演示视频,请耐心看到最后;

本文详细描述了FPGA实现MPEG2视频压缩加速器及其仿真的设计方案,工程代码可综合编译上板调试,但目前只做到了仿真层面,可直接项目移植,适用于在校学生、研究生项目开发,也适用于在职工程师做项目开发,可应用于医疗、军工等行业的数字成像和图像压缩领域;
提供完整的、跑通的工程源码和技术支持;
工程源码和技术支持的获取方式放在了文章末尾,请耐心看到最后;

免责声明

本工程及其源码即有自己写的一部分,也有网络公开渠道获取的一部分(包括CSDN、Xilinx官网、Altera官网等等),若大佬们觉得有所冒犯,请私信批评教育;基于此,本工程及其源码仅限于读者或粉丝个人学习和研究,禁止用于商业用途,若由于读者或粉丝自身原因用于商业用途所导致的法律问题,与本博客及博主无关,请谨慎使用。。。

2、我这里已有的视频图像编解码方案

我这里有图像的JPEG解压缩、JPEG-LS压缩、H264解码以及其他方案,后续还会出更多方案,我把他们整合在一个专栏里面,会持续更新,专栏地址:
直接点击前往

3、MPEG2视频压缩理论

MPEG2视频压缩算法介绍可以百度一下或者csdn或者知乎搜一下看看,专业的讲解我不擅长,我只擅长用fpga实现算法,专业讲原理的大佬比我讲得好,这里可以推荐一篇阅读量很大的文章:直接点击前往

4、MPEG2视频压缩性能介绍

1:每周期可输入 4 个像素,每个像素包含 8-bit Y (明度) 、8-bit U (蓝色度) 、8-bit V (红色度) ;
2:在 Xilinx Kintex-7 XC7K325TFFG676-2 上最大时钟频率为 67MHz;
3:吞吐率达到 67*4= 268 MPixels/s ,对 1920x1152 的视频的编码帧率为 121 帧/秒;
4:无需外部存储器,典型配置在 Xilinx 7 系列 FPGA 上消耗 134k LUT, 125 个 DSP48E, 405个 BRAM36K;
5:静态可调参数 (综合前确定,部署后不可调)
5.1:视频的最大宽度、最大高度:越大则消耗 BRAM 越多;
5.2:动作估计的搜索范围:越大则压缩率越高,但消耗 LUT 越多;
5.3:量化级别 :越大则压缩率越高,但输出视频质量越差;
6:动态可调参数 (可在运行时针对每个视频序列进行调整)
6.1:视频的宽度和高度可调;
6.2:I帧间距 (两个I帧之间P帧的数量) 可调:越大则压缩率越高;
7:本MPEG2视频压缩器缺点:
对 LUT 和 DSP 资源的优化还需要优化,因为该模块目前消耗资源还不够小,一般只适用于 Xilinx Kintex-7 这种规模 (或更大规模) 的 FPGA,后续优化后,希望能部署在中等规模的 Artix-7 上。。。

5、MPEG2视频压缩接口时序介绍

首先看看MPEG2视频压缩加速器顶层接口:
在这里插入图片描述
注意仔细看每个接口对应的注释,看懂了接口才能理解接口时序;

接口参数

顶层接口的4个参数解释如下:
在这里插入图片描述
在使用本模块前,需要根据你想要压缩的视频的尺寸来决定 XL 和 YL ,例如如果你想编码 1920x1152 的视频,则应该取 XL=YL=7 。如果你想编码 640x480 的视频,当然也可以取 XL=YL=7 ,但为了节省 BRAM 资源,可以取 XL=6, YL=5 ;

输入像素

要使用本模块编码一个视频序列 (sequence, 即一系列帧,一般保存为一个独立的视频文件) ,需要按照逐帧、逐行、逐列的顺序把像素输入模块。每个时钟周期可以输入同一行内相邻的4个像素。当一个时钟周期时需要输入4个像素时,需要让i_en=1,同时让 i_Y0~i_Y3 i_U0~i_U3 和 i_V0~i_V3 上分别出现4个像素的 Y, U 和 V 分量。
例如,一个 64x64 的视频序列如下。其中 (Yijk, Uijk, Vijk) 代表第i帧、第j行、第k列的像素的 Y, U, V 值:

第0帧:
  第0行: (Y000, U000, V000), (Y001, U001, V001), (Y002, U002, V002), (Y003, U003, V003), (Y004, U004, V004), (Y005, U005, V005), (Y006, U006, V006), (Y007, U007, V007), (Y008, U008, V008), (Y009, U009, V009), ..., (Y0063, U0063, V0063)
  第1行: (Y010, U010, V010), (Y011, U011, V011), (Y012, U012, V012), (Y013, U013, V013), (Y014, U014, V014), (Y015, U015, V015), (Y016, U016, V016), (Y017, U017, V017), (Y018, U018, V018), (Y019, U019, V019), ..., (Y0163, U0163, V0163)
  ...
第1帧:
  第0行: (Y100, U100, V100), (Y101, U101, V101), (Y102, U102, V102), (Y103, U103, V103), (Y104, U104, V104), ...
  第1行: (Y110, U110, V110), (Y111, U111, V111), (Y112, U112, V112), (Y113, U113, V113), (Y114, U114, V114), ...
  ...
...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

则第一个时钟周期应该输入第0帧第0行的前4个像素:

第 1 个时钟周期:
i_Y0 = Y000
i_Y1 = Y001
i_Y2 = Y002
i_Y3 = Y003
i_U0 = U000
i_U1 = U001
i_U2 = U002
i_U3 = U003
i_V0 = V000
i_V1 = V001
i_V2 = V002
i_V3 = V003
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

然后,下一周期应该输入:

第 2 个时钟周期:
i_Y0 = Y004
i_Y1 = Y005
i_Y2 = Y006
i_Y3 = Y007
i_U0 = U004
i_U1 = U005
i_U2 = U006
i_U3 = U007
i_V0 = V004
i_V1 = V005
i_V2 = V006
i_V3 = V007
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

以此类推,需要花费 64/4=16 个时钟周期来输入第0帧第0行,然后第 17 个时钟周期应该输入第0帧第1行的前4个像素:

第 17 个时钟周期:
i_Y0 = Y010
i_Y1 = Y011
i_Y2 = Y012
i_Y3 = Y013
i_U0 = U010
i_U1 = U011
i_U2 = U012
i_U3 = U013
i_V0 = V010
i_V1 = V011
i_V2 = V012
i_V3 = V013
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

继续以此类推,需要花费 64*64/4=1024 个时钟周期来输入完第0帧,然后第 1025 个时钟周期应该输入第1帧第0行的前4个像素:

第 1025 个时钟周期:
i_Y0 = Y100
i_Y1 = Y101
i_Y2 = Y102
i_Y3 = Y103
i_U0 = U100
i_U1 = U101
i_U2 = U102
i_U3 = U103
i_V0 = V100
i_V1 = V101
i_V2 = V102
i_V3 = V103
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

本模块可以连续每周期都输入4个像素而不需要任何等待 (没有输入反压握手),但当发送者没有准备好像素时,也可以断续地输入像素 (可以随时插入气泡) ,也即让 i_en=0;

输入配置

当输入一个视频序列的最前面的4个像素 (也即第0帧第0行的前4个像素) 的同时,需要让 i_xsize16, i_ysize16 , i_pframes_count 有效,其中:
i_xsize16 :视频宽度/16 。例如对于 640x480 的视频,应该取 i_xsize16 = 640/16 = 40 。注意i_xsize16 取值范围为 4~(2^XL) ;
i_ysize16 :视频宽度/16 。例如对于 640x480 的视频,应该取 i_xsize16 = 480/16 = 30 。注意 i_ysize16 取值范围为 4~(2^YL);
i_pframes_count: 决定了相邻两个 I 帧之间 P 帧的数量,可以取 0~255 ,越大则压缩率越高,推荐的取值是 23;

本模块只支持宽和高都为 16 的倍数的视频,例如 1920x1152 。如果视频的宽和高不为 16 ,则应该填充为 16 的倍数后再送入本模块。例如 1910x1080 的视频应该填充为 1920x1088;
本模块不支持宽和高小于 64 的视频,因此 i_xsize16 和 i_ysize16 的最小合法取值是 4;

视频帧控制信号

结束当前视频序列:
当输入若干帧后,如果你想结束当前视频序列 (一个视频序列的帧的数量不限),需要向模块发送“结束当前视频序列”的请求,具体方法是让 i_sequence_stop=1 保持一个周期,以下两种方式均可:

在输入该视频序列的最后4个像素的同时让 i_sequence_stop=1 ;
在输入该视频序列的最后4个像素后的若干周期后再让 i_sequence_stop=1 ;
然后需要等待模块完成对该视频序列的收尾工作,具体方法是检测 o_sequence_busy 信号, o_sequence_busy=1 代表模块正在编码一个序列;o_sequence_busy=0 代表模块处于空闲状态 。当你发送“结束当前视频序列”的请求后,应该等待 o_sequence_busy 从 1 变为 0 ,这才代表着该视频序列的编码工作已经完全结束。

开始输入下一个视频序列:
当上一个视频序列完全结束 ( o_sequence_busy 从 1 变为 0 ) 后,才可以开始输入下一个视频序列 (也即输入下一个视频序列的最前面的4个像素,同时让 i_xsize16, i_ysize16 , i_pframes_count 有效) 。

输出 MPEG2 码流

o_en, o_last, o_data 这三个信号负责输出编码后的 MPEG2 码流。
当 o_en=1 时, o_data 上会出现 32 字节的 MPEG2 码流数据。如果 o_en=1 的同时 o_last=1 ,说明这是该视频序列输出的最后一个数据,下一次 o_en=1 时就输出的是下一个视频序列的第一个数据了。
注意!! o_data 是 小端序 (Little Endian) ,也即 o_data[7:0] 是最靠前的字节, o_data[15:8] 是第二个字节, … o_data[255:248] 是最后一个字节。之所以要用小端序,是因为大多数总线也是小端序 (例如 AXI 总线) 。
注意!! o_en=1 的同时必然有 o_sequence_busy=1 。当模块空闲 (也即 o_sequence_busy=0 ) 时,它不可能输出数据 (不可能出现 o_en=1);

MPEG2视频压缩接口时序

经过前面的接口解释,最后放出接口时序应该好理解一些:
在这里插入图片描述
最开始, o_sequence_busy=0 说明模块当前空闲,可以输入一个新的视频序列。
让 i_en=1 ,输入一个视频序列的最前面的4个像素,同时在 i_xsize16, i_ysize16 上输入该视频的宽、高信息;在 i_pframes_count 上输入你想要的 I 帧间距。
此后继续向该模块输入像素 (连续输入和断续输入均可),直到该视频序列的最后4个像素输入完为止。
让 i_sequence_stop=1 一个周期,结束该视频序列。
等待 o_sequence_busy 从 1 变成 0 ,然后才可以输入下一个视频序列。
在以上过程的同时, o_en 会断续出现 1 。在 o_en=1 时从 o_data 上拿到 MPEG2 输出流。当该视频序列的最后一个数据输出的同时 o_last=1 。

6、MPEG2视频压缩设计详解

在这里插入图片描述
模块输入YUV444视频流,首先经过YUV444转YUV420模块降低数据量;
再根据前后2帧或几帧的图像差异进行动作预估,进而进行预测,如果连续到来的3帧图像差异很小,则认为这3帧相似度很高,可以视为1帧,这样就减少了2帧的数据量,以此为前提,则可进行新的预估帧图像的变、量化,到这里还要进行负反馈,对这个判断做出再次确认;然后是数据重新排序以及编码,最后输出数据为端口宽度为 256 bit ,每周期可能输出 32 字节的完整 MPEG2 码流;

7、vivado工程源码仿真综合

开发板FPGA型号:Xilinx xc7k325tffg676-2;
开发环境:Vivado2019.1;
输入:YUV RAW 文件;
输出:.m2v 文件(可通过 VLC播放器播放);

工程代码架构如下:
在这里插入图片描述
我们的 testbench 需要给 helai_MPEG2_EC.sv 模块输入视频的原始像素,但电脑上的视频一般都是编码后的 (基本上不可能有以原始像素存储的视频),所以在进行仿真前,需要准备 YUV RAW 文件 (原始像素文件),然后 testbench 才能读取该文件并送入 helai_MPEG2_EC.sv 模块。

我提供了3个视频对应的 YUV RAW 文件,分别是 288x208.raw, 640x320.raw 和 1440x704.raw,它们实际上是 288x208.mp4, 640x320.mp4, 1440x704.mp4 这三个视频解码后得到的,位置如下:
在这里插入图片描述
仿真流程为:
第一步:
添加源码并开启行为仿真:
在这里插入图片描述
运行中可能会出现如下信息导致仿真失败:
在这里插入图片描述
解决办法:在Tcl中输入以下指令并回车:
set_property display_limit 33554432 [current_wave_config]
在这里插入图片描述
如果把我提供的三个视频都仿真完,需要3个小时左右,为了节省时间,在仿真代码里我只加入了一个视频做仿真,你可以修改了仿真三个,需要修改的地方如下:
在这里插入图片描述
第二步:
点击开始仿真:
在这里插入图片描述
288x208的视频共有84帧,需要仿真时间半小时左右,等仿真完成后可以看到打印信息:
在这里插入图片描述
FPGA的型号为 Xilinx Kintex-7 XC7K325TFFG676-2 。这些配置下的最大时钟频率均为 67MHz,不同压缩率下的综合后的资源消耗如下:
在这里插入图片描述
仿真后的输出文件位置如下:可打开播放视频:
在这里插入图片描述
打开播放截图如下:
在这里插入图片描述

8、验证演示效果视频

综合仿真流程即输出结果验证的演示视频如下:

FPGA实现MPEG2视频压缩

9、福利:工程代码的获取

福利:工程代码的获取
代码太大,无法邮箱发送,以某度网盘链接方式发送,
资料获取方式:文章末尾的V名片。
网盘资料如下:
在这里插入图片描述
在这里插入图片描述

源码技术支持加我微信哦
微信名片
注:本文转载自blog.csdn.net的9527华安的文章"https://blog.csdn.net/qq_41667729/article/details/129998492"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top