音视频——压缩原理

news2024/11/24 14:27:01

H264视频压缩算法现在无疑是所有视频压缩技术中使用最广泛,

最流行的。随着 x264/openh264以及ffmpeg等开源库的推出,大多数使用者无需再对H264的细节做过多的研究,这大降低了人们使用H264的成本。

但为了用好H264,我们还是要对H264的基本原理弄清楚才行。今天我们就来看看H264的基本原理。

H264概述

在这里插入图片描述

H264压缩技术主要采用了以下几种方法对视频数据进行压缩。包括:

  1. 帧内预测压缩,解决的是空域数据冗余问题。
  2. 帧间预测压缩(运动估计与补偿),解决的是时域数据冗徐问题。
  3. 整数离散余弦变换(DCT),将空间上的相关性变为频域上无关的数据然后进行量化。
  4. CABAC压缩。

经过压缩后的帧分为:I帧,P帧和B帧:

  • I帧:关键帧,采用帧内压缩技术。
  • P帧:向前参考帧,在压缩时,只参考前面已经处理的帧。采用帧音压缩技术。
  • B帧:双向参考帧,在压缩时,它即参考前而的帧,又参考它后面的帧。采用帧间压缩技术。

除了I/P/B帧外,还有图像序列GOP。

GOP:两个I帧之间是一个图像序列,在一个图像序列中只有一个I帧。如下图所示:
在这里插入图片描述

下面我们就来详细描述一下H264压缩技术。

H264压缩技术

H264的基本原理其实非常简单,下我们就简单的描述一下H264压缩数据的过程。通过摄像头采集到的视频帧(按每秒 30 帧算),被送到 H264 编码器的缓冲区中。编码器先要为每一幅图片划分宏块。

以下面这张图为例:

在这里插入图片描述

划分宏块

H264默认是使用 16X16 大小的区域作为一个宏块,也可以划分成 8X8 大小。

在这里插入图片描述划分好宏块后,计算宏块的象素值。

在这里插入图片描述

以此类推,计算一幅图像中每个宏块的像素值,所有宏块都处理完后如下面的样子。

在这里插入图片描述

划分子块

H264对比较平坦的图像使用 16X16 大小的宏块。但为了更高的压缩率,还可以在 16X16 的宏块上更划分出更小的子块。子块的大小可以是 8X16、 16X8、 8X8、 4X8、 8X4、 4X4非常的灵活。

在这里插入图片描述

上幅图中,红框内的 16X16 宏块中大部分是蓝色背景,而三只鹰的部分图像被划在了该宏块内,为了更好的处理三只鹰的部分图像,H264就在 16X16 的宏块内又划分出了多个子块。

在这里插入图片描述

这样再经过帧内压缩,可以得到更高效的数据。下图是分别使用mpeg-2和H264对上面宏块进行压缩后的结果。其中左半部分为MPEG-2子块划分后压缩的结果,右半部分为H264的子块划压缩后的结果,可以看出H264的划分方法更具优势。

在这里插入图片描述

宏块划分好后,就可以对H264编码器缓存中的所有图片进行分组了。

帧分组

对于视频数据主要有两类数据冗余,一类是时间上的数据冗余,另一类是空间上的数据冗余。其中时间上的数据冗余是最大的。下面我们就先来说说视频数据时间上的冗余问题。

为什么说时间上的冗余是最大的呢?假设摄像头每秒抓取30帧,这30帧的数据大部分情况下都是相关联的。也有可能不止30帧的的数据,可能几十帧,上百帧的数据都是关联特别密切的。

对于这些关联特别密切的帧,其实我们只需要保存一帧的数据,其它帧都可以通过这一帧再按某种规则预测出来,所以说视频数据在时间上的冗余是最多的。

为了达到相关帧通过预测的方法来压缩数据,就需要将视频帧进行分组。那么如何判定某些帧关系密切,可以划为一组呢?我们来看一下例子,下面是捕获的一组运动的台球的视频帧,台球从右上角滚到了左下角。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5eMeFcWG-1690276172009)(img/40.png)]

在这里插入图片描述

H264编码器会按顺序,每次取出两幅相邻的帧进行宏块比较,计算两帧的相似度。如下图:
在这里插入图片描述

通过宏块扫描与宏块搜索可以发现这两个帧的关联度是非常高的。进而发现这一组帧的关联度都是非常高的。因此,上面这几帧就可以划分为一组。其算法是:在相邻几幅图像画面中,一般有差别的像素只有10%以内的点,亮度差值变化不超过2%,而色度差值的变化只有1%以内,我们认为这样的图可以分到一组。

在这样一组帧中,经过编码后,我们只保留第一帖的完整数据,其它帧都通过参考上一帧计算出来。我们称第一帧为IDR/I帧,其它帧我们称为P/B帧,这样编码后的数据帧组我们称为GOP

运动估计与补偿

在H264编码器中将帧分组后,就要计算帧组内物体的运动矢量了。还以上面运动的台球视频帧为例,我们来看一下它是如何计算运动矢量的。

H264编码器首先按顺序从缓冲区头部取出两帧视频数据,然后进行宏块扫描。当发现其中一幅图片中有物体时,就在另一幅图的邻近位置(搜索窗口中)进行搜索。如果此时在另一幅图中找到该物体,那么就可以计算出物体的运动矢量了。下面这幅图就是搜索后的台球移动的位置。

在这里插入图片描述

抖音 ----》视频 1M 9M

通过上图中台球位置相差,就可以计算出台图运行的方向和距离。H264依次把每一帧中球移动的距离和方向都记录下来就成了下面的样子。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CFeLZgaf-1690276172012)(img/44.png)]

运动矢量计算出来后,将相同部分(也就是绿色部分)减去,就得到了补偿数据。我们最终只需要将补偿数据进行压缩保存,以后在解码时就可以恢复原图了。压缩补偿后的数据只需要记录很少的一点数据。如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G5ngbg4Z-1690276172013)(img/45.png)]

我们把运动矢量与补偿称为帧间压缩技术,它解决的是视频帧在时间上的数据冗余。除了帧间压缩,帧内也要进行数据压缩,帧内数据压缩解决的是空间上的数据冗余。下面我们就来介绍一下帧内压缩技术。

帧内预测

人眼对图象都有一个识别度,对低频的亮度很敏感,对高频的亮度不太敏感。所以基于一些研究,可以将一幅图像中人眼不敏感的数据去除掉。这样就提出了帧内预测技术。

H264的帧内压缩与JPEG很相似。一幅图像被划分好宏块后,对每个宏块可以进行 9 种模式的预测。找出与原图最接近的一种预测模式。
在这里插入图片描述

下面这幅图是对整幅图中的每个宏块进行预测的过程。

在这里插入图片描述

帧内预测后的图像与原始图像的对比如下:

在这里插入图片描述

然后,将原始图像与帧内预测后的图像相减得残差值。

在这里插入图片描述

再将我们之前得到的预测模式信息一起保存起来,这样我们就可以在解码时恢复原图了。效果如下:

在这里插入图片描述

经过帧内与帧间的压缩后,虽然数据有大幅减少,但还有优化的空间。

对残差数据做DCT

可以将残差数据做整数离散余弦变换,去掉数据的相关性,进一步压缩数据。如下图所示,左侧为原数据的宏块,右侧为计算出的残差数据的宏块。

在这里插入图片描述

将残差数据宏块数字化后如下图所示:

在这里插入图片描述

将残差数据宏块进行 DCT 转换。
在这里插入图片描述

去掉相关联的数据后,我们可以看出数据被进一步压缩了。

在这里插入图片描述

做完 DCT 后,还不够,还要进行 CABAC 进行无损压缩。

DCT原理大白话

这是第一帧画面:P1(我们的参考帧)
在这里插入图片描述

这是第二帧画面:P2(需要编码的帧)

在这里插入图片描述

从视频中截取的两张间隔1-2秒的画面,和实际情况类似,下面我们进行几次运动搜索:

这是一个演示程序,鼠标选中P2上任意16x16的Block,即可搜索出P1上的 BestMatch 宏块。虽然车辆在运动,从远到近,但是依然找到了最接近的宏块坐标。

在这里插入图片描述

这是一个演示程序,鼠标选中P2上任意16x16的Block,即可搜索出P1上的 BestMatch 宏块。虽然车辆在运动,从远到近,但是依然找到了最接近的宏块坐标。

搜索演示2:空中电线交叉位置(上图P1,下图P2)

在这里插入图片描述

在这里插入图片描述

同样顺利在P1中找到最接近P2里海报的宏块位置。

图片全搜索:根据P1和运动矢量数据(在P2中搜索到每一个宏块在P1中最相似的位置集合)还原出来的P2’,即完全用P1各个位置的宏块拼凑出来最像P2的图片P2’,效果如下:

在这里插入图片描述

仔细观察,有些支离破碎对吧?肯定啊,拼凑出来的东西就是这样,现在我们用P2`和P2像素相减,得到差分图 D2 = (P2’ - P2) / 2 + 0x80:

在这里插入图片描述

这就是之前支离破碎的 P2` 加上误差 D2之后变成了清晰可见的样子,基本还原了原图P2。

在这里插入图片描述

由于D2仅仅占5KB,加上压缩过后的运动矢量不过7KB,所以参考P1我们只需要额外 7KB的数据量就可以完整表示P2了,而如果独立将P2用质量尚可的有损压缩方式独立压缩,则至少要去到50-60KB,这一下节省了差不多8倍的空间,正就是所谓运动编码的基本原理。

实际在使用中,参考帧并不一定是前面一帧,也不一定是同一个GOP的I帧,因为GOP间隔较长时,后面的图片离I帧变化可能已经很大了,因此常见做法是最近15帧中选择一帧误差最小的作为参考帧,虽然彩色画面有YUV三个分量,但是大量的预测工作和最有选择通常是根据Y分量的灰度帧进行判断的。

再者误差我们保存的是(P2-P2’)/2 + 0x80,实际使用时我们会用更有效率的方式,比如让[-64,64]之间的色差精度为1,[-255,-64], [64, 255] 之间的色差精度为2-3,这样会更加真实一些。

同时上文很多地方用的是直接lzma2进行简单存储,实际使用时一般会引入熵编码,对数据进行一定层次的整理然后再压缩,性能会好不少。

CABAC

上面的帧内压缩是属于有损压缩技术。也就是说图像被压缩后,无法完全复原。而CABAC属于无损压缩技术。

无损压缩技术大家最熟悉的可能就是哈夫曼编码了,给高频的词一个短码,给低频词一个长码从而达到数据压缩的目的。MPEG-2中使用的VLC就是这种算法,我们以 A-Z 作为例子,A属于高频数据,Z属于低频数据。看看它是如何做的。

在这里插入图片描述

CABAC也是给高频数据短码,给低频数据长码。同时还会根据上下文相关性进行压缩,这种方式又比VLC高效很多。其效果如下:

在这里插入图片描述

现在将 A-Z 换成视频帧,它就成了下面的样子。

在这里插入图片描述

从上面这张图中明显可以看出采用 CACBA 的无损压缩方案要比 VLC 高效的多。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/786480.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

X - Transformer

回顾 Transformer 的发展 Transformer 最初是作为机器翻译的序列到序列模型提出的,而后来的研究表明,基于 Transformer 的预训练模型(PTM) 在各项任务中都有最优的表现。因此,Transformer 已成为 NLP 领域的首选架构&…

面试题——当实体类中的属性名和表中的字段名不一样,如何将查询的结果封装到指定 pojo?

在使用MyBatis的时候,应该注意实体类的属性名尽量和表的字段名尽量相同,如果不同将会导致MyBatis无法完成数据的封装,但是在软件开发过程中,数据库的创建和软件环境的搭建不可能是同一个人,实体类属性名和数据库的字段…

真正理解红黑树,真正的(Linux内核里大量用到的数据结构

作为一种数据结构,红黑树可谓不算朴素,因为各种宣传让它过于神秘,网上搜罗了一大堆的关于红黑树的文章,不外乎千篇一律,介绍概念,分析性能,贴上代码,然后给上罪恶的一句话&#xff0…

芯片制造详解.从沙子到晶圆.学习笔记(一)

刚入行半导体行业,很多知识需要系统的学习,想从入门通俗易懂的知识开始学起,于是在导师的帮助下,找到了这门课程,那就从这门课程开始打开我的半导体之旅吧。 我只是对视频内容的提炼,和自己的学习心得&…

hack the box—Lame

扫描 还是老方法nmapfscan得到开放的端口和服务 nmap -sV -sC -sT -v -T4 10.10.10.3 看到开了445,先来波ms17-010,发现失败。 这里还开个21,并且可以知道版本号,直接搜索ftp漏洞 msf正好有对应的模块 设置好参数后进行攻击&…

从iOS App启动速度看如何为基础性能保驾护航 | 京东物流技术团队

1 前言 启动是App给用户的第一印象,一款App的启动速度,不单单是用户体验的事情,往往还决定了它能否获取更多的用户。所以到了一定阶段App的启动优化是必须要做的事情。App启动基本分为以下两种 1.1 冷启动 App 点击启动前,它的…

uniapp---app端人脸识别组件(宽屏版1280*800组件,需手动截屏拍人脸识别,踩坑,成长)

一、首先记录下踩到的坑 我这边做的是一个挂在门口的门牌机,可以用于扫脸签到,扫码签到,扫脸实现用的是live-pusher组件(代码很长,放在最下面,不能直接用,需要根据实际情况修改)去做…

处理多维特征的输入

数据的每一列称为:特征/字段 x的数据变为8列,维数8 step one:构建数据集 x_data;y_data:创建两个Tensor step two:定义模型 step three:构造损失和优化器 step four:训练 else 激活函数: 代码更改部分: 转->大佬笔…

基于AutoEncoder自编码器的人脸识别matlab仿真

目录 1.算法理论概述 2.部分核心程序 3.算法运行软件版本 4.算法运行效果图预览 5.算法完整程序工程 1.算法理论概述 人脸识别是计算机视觉领域的重要研究方向,其目标是从图像或视频中准确地识别和识别人脸。传统的人脸识别方法通常基于特征提取和分类器&#…

Java虚拟机——后端编译与优化

编译器无论在何时、何种状态下将Class文件转换成与本地基础设施相关的二进制机器码,它都可以视为整个编译过程的后端。即时编译一直是绝对主流的编译形式,不过提前编译也逐渐被主流JDK支持。 1 即时编译器 目前两款主流的Java虚拟机(HotSpo…

【MATLAB绘图】

MATLAB绘图函数:Plot函数详解 介绍 MATLAB是一种常用的科学计算和数据可视化工具,它提供了强大的绘图函数,使用户能够创建各种类型的图表和图形。 基本语法 plot函数的基本语法如下: plot(x, y)其中,x和y是长度相…

HDFS的文件块大小(重点)

HDFS 中的文件在物理上是分块存储 (Block ) , 块的大小可以通过配置参数( dfs.blocksize)来规定,默认大小在Hadoop2.x/3.x版本中是128M,1.x版本中是64M。 如果一个文件文件小于128M,该文件会占…

Git 命令行教程:如何在 GitLab 中恢复已删除的分支

在软件开发过程中,版本控制是一个至关重要的环节。Git 是最流行的分布式版本控制系统之一,它能够帮助团队高效地管理代码。然而,有时候会发生意外,例如代码误合、错误的删除等情况,导致重要的开发分支本地和远程不慎被…

vue2踩坑之项目:v-if/else branches must use unique keys.

error: v-if/else branches must use unique keys. if 与 else 的 key 不能相同&#xff0c;在else标签里的key逐步加1 // if 与 else 的 key 不能相同 <div v-for"(item, index) in menuList" :key"item.id"><div v-if"item.name" :k…

js 操作符 in 的使用

1、判断对象中是否有某个属性&#xff0c;返回true/false let person {name: "小明", age: 18, year: 1998}; if ("age" in person){ //属性名必须是字符串形式&#xff0c;因为age不是一个变量console.log("person对象中含有age属性") } els…

(六)如何使用FLUX语言的文档

以下内容来自 尚硅谷&#xff0c;写这一系列的文章&#xff0c;主要是为了方便后续自己的查看&#xff0c;不用带着个PDF找来找去的&#xff0c;太麻烦&#xff01; 第 6 章 如何使用FLUX语言的文档 6.1 如何查看函数文档 1、这是FLUX语言的文档 https://docs.influxdata.com…

(七)FLUX查询InfluxDB

以下内容来自 尚硅谷&#xff0c;写这一系列的文章&#xff0c;主要是为了方便后续自己的查看&#xff0c;不用带着个PDF找来找去的&#xff0c;太麻烦&#xff01; 第 7 章 FLUX查询InfluxDB 7.1 前言 1、本节内容较为重要 7.2 FLUX查询InfluxDB的语法 1、使用FLUX语言查询…

AbstractMethodError: com.xx.serviceImpl.xxx method is abstract

今日一语&#xff1a; 其实我们没有必要和其他人进行比较&#xff0c;我们要比较的是昨天的自己 只有这样才能得到救赎和解脱&#xff0c;否则就是泥潭中跳跃&#xff0c;山谷中呐喊 在翻阅其他博主的博客时&#xff0c;似乎大多分享的是因为数据源驱动或者中间件的版本太高或者…

SpringCloud - 狂神学习笔记

1. 常见面试题 1.1 什么是微服务&#xff1f; 1.2 微服务之间是如何独立通讯的&#xff1f; 1.3 SpringCloud 和 Dubbo有那些区别&#xff1f; 1.4 SpringBoot 和 SpringCloud&#xff0c;请谈谈你对他们的理解 1.5 什么是服务熔断&#xff1f;什么是服务降级&#xff1f;…

微调模型来完成热狗识别的图像分类任务

我们来实践一个具体的例子&#xff1a;热狗识别。将基于一个小数据集对在ImageNet数据集上训练好的ResNet模型进行微调。该小数据集含有数千张热狗或者其他事物的图像。我们将使用微调得到的模型来识别一张图像中是否包含热狗。 首先&#xff0c;导入实验所需的工具包。 impo…