之前一直很好奇图片和视频是如何压缩的,由于视频格式会更复杂,所以先从JPEG下手

因为网上资料太难找太分散,有些又看不太懂,所以根据自己的理解整理了一下

JPEG简介

JPEG(Joint Photographic Experts Group),全称为联合图像专家小组,同时也是我们生活中很常见的一种图像格式,一般以.jpg作扩展名。JPEG采用了多种方法来对图像进行有损压缩,从而使得观感相差不大的情况下减小了较多的文件体积


压缩流程

常见的JPEG图像数据按照如下流程进行压缩:

flowchart TD
原始图像 --> 8x8分块 --> DCT变换 --> 量化 --> Z字形扫描 --> 对系数编码 --> 熵编码 --> 压缩数据
subgraph 对系数编码
对AC系数游程编码
对DC系数差分脉冲编码
end

1.分块

JPEG中会首先对图像以8x8的块为单位划分,不足8x8的会填充剩余部分。不过具体填充什么我没研究过,但是解码显示出来的时候会忽略掉这部分,所以应该是无所谓的。

对图像进行8x8分块也是为了方便下一步进行DCT变化

2.DCT变换

DCT(Discrete Cosine Transform,离散余弦变换)是一种把数据从时域转化到频域的数学方法,把图像数据转换到频域之后,我们就可以从中分离出各种频率的信息。

DCT变换过后数据按照不同频率进行划分,得到的数据实际上是不同频率余弦波的系数,把这些系数乘上对应频率的余弦函数就能得到原来的数据

但要注意的是DCT变换过后数据的大小也不会发生改变,相当于把数据用另一种方式表示,就好像同一句话你用普通话和方言讲出来就是两种表达方式。

3.量化

DCT变换后我们就能区分不同频率的信息了,其中高频信息是图像中的一些细节,将变换后的高频部分系数置0则可以去除这些细节。因为人眼对高频信息不是很敏感,所以观感差距不会很大,就像你有轻度的近视,但不影响你看清一个物体。

比如下图

我们会发现,假如只保留DCT变换后左上角的低频部分,还原出来的图像其实和原图差距不大;而如果看保留高频后还原出来的图像,仔细看会发现只留下了物体边缘等细节部分的一些痕迹(也就是高频部分)

此时选择去除哪些频率的信息就成了关键点,我们要给图片选择一个“近视程度”来让减少高频信息。好在JPEG标准已经准备好了几种量化表,对应了不同的图像质量,我们只需要把DCT变换后的数据除以量化表中的对应项就完成了量化操作。

4.Z字形扫描

对数据进行过量化之后仍然没有减小体积,因为去除的高频系数只是被替换成了0,占用的空间还是不变,这时候就轮到Z字形扫描(Zig-Zag Scan)出场了。

Z字形扫描就如同字面意思一样,按照上图顺序重新排序顺序。因为高频信息聚集在右下角,按照这种方式扫描后就可以把被替换成0的高频系数连续排列了

顺带一提,知名音视频编解码工具FFmpeg的图标就来源于Z字形扫描

5.对DC和AC系数编码

什么是DC系数和AC系数?

简单地说,DC系数就是一个块中第一个数,而AC系数就是剩余的数



(图源网络)

上图中1就是DC系数,2-9就是AC系数

对AC系数编码

前面的Z字形扫描完成了准备操作,真正压缩则是交给游程编码(Run Length Encode, RLE)

游程编码以两个数为一组,第一个数表示第二个数的重复次数,对于压缩经过Z字形扫描过后,存在大量连续的0的数据来说再合适不过了

假如直接舍弃后面一半的信息,对画质影响不大的情况下,减少了差不多一半的数据量!

对DC系数编码

对于DC系数,采用的是一种叫差分脉冲编码调制(Differential Pulse code modulation,DPCM)的编码方式

听起来很高深,实际上就是把DC系数换成这一个块和上一个块的DC系数的差值。比如上一个块的DC系数是20,这一个块的DC系数是10,那就写入-10。如果是第一个块,那就不用变(或者也可以看作前面有一个不存在的DC系数为0)。

JPEG中DC系数和AC系数的存储格式:

在实际存储的数据中,一个系数分成两部分

第一部分长度为一个byte。

对于AC系数来说采用了游程编码,低四位表示第二部分的长度(以bit为单位),高四位表示这一数据前0的个数;

对于DC系数来说这一整个byte就表示第二部分数据的长度(也是以bit为单位)

第二部分的第1位是符号位,0表示负数,1表示正数。如果是正数,无需处理;如果是负数,则是对原数包括符号位在内的每一位取反的结果

6.熵编码

最后是对所有的数据进行熵编码,一般用的是范式霍夫曼编码(Canonical Huffman Code)。

范式霍夫曼编码基于霍夫曼编码,霍夫曼编码核心思想是为出现频率更高的数据分配更短的编号,为出现频率低的数据分配更长的编号,这样就可以实现无损压缩数据。而由于霍夫曼编码以bit为单位,长度又不确定,读取时无法区分,所以采用了范式霍夫曼编码。

JPEG中范式霍夫曼编码的存储格式:

首先用一个大小为16个字节的数组存储编码后数据中对应长度为1bit~16bit的原始数据的种类数,后面按同样的顺序紧跟所有种类的原始数据

而编码后的数据要通过如下方式得到:

数从长度为1bit的0开始,长度相同时每编码一个数就+1,长度每增加1bit就要左移1位

当然,JPEG标准中也给出了参考的范式霍夫曼编码的对照表

参考资料

JPEG解码系列博客:多媒体-编解码 - 随笔分类 - OnlyTime_唯有时光 - 博客园 (cnblogs.com)

JPEG标准:Microsoft Word - T081E.DOC (w3.org)

白话文理解DCT离散余弦变换 - 乂墨EMO - 博客园 (cnblogs.com)

一个Rust写的JPEG解码器:MROS/jpeg_tutorial: 跟我寫 JPEG 解碼器 (Write a JPEG decoder with me) (github.com)

友情链接

我学习过程中写的JPEG图片查看器:Ryan1202/my-tiny-jpeg-viewer: A Tiny Jpeg Viewer (github.com)

JPEG格式研究——(1)压缩原理及流程的更多相关文章

  1. JPEG格式 介绍

    JPEG格式 jpeg是有损压缩格式, 将像素信息用jpeg保存成文件再读取出来,其中某些像素值会有少许变化.在保存时有个质量参数可在[0,100]之间选择,参数越大图片就越保真,但图片的体积也就越大 ...

  2. 图像JPEG格式介绍

    1 JPG格式介绍 JPEG (Joint PhotographicExperts GROUP)是由国际标准组织和国际电话电报咨询委员会为静态图像所建立的第一个国际数字图像压缩标准,也是至今一直在使用 ...

  3. Java 详解 JVM 工作原理和流程

    Java 详解 JVM 工作原理和流程 作为一名Java使用者,掌握JVM的体系结构也是必须的.说起Java,人们首先想到的是Java编程语言,然而事实上,Java是一种技术,它由四方面组成:Java ...

  4. Linux可插拔认证模块(PAM)的配置文件、工作原理与流程

    PAM的配置文件: 我们注意到,配置文件也放在了在应用接口层中,他与PAM API配合使用,从而达到了在应用中灵活插入所需鉴别模块的目的.他的作用主要是为应用选定具体的鉴别模块,模块间的组合以及规定模 ...

  5. InnoDB 数据表压缩原理与限制

    http://liuxin1982.blog.chinaunix.net/uid-24485075-id-3523032.html 压缩理念 通过提高CPU利用率和节约成本,降低数据库容量及I/O负载 ...

  6. hadoop深入研究:(七)——压缩

    转载请标明出处:hadoop深入研究:(七)——压缩 文件压缩主要有两个好处,一是减少了存储文件所占空间,另一个就是为数据传输提速.在hadoop大数据的背景下,这两点尤为重要,那么我现在就先来了解下 ...

  7. 嵌入式Linux基于framebuffer的jpeg格式本地LCD屏显示

    在基于Linux的视频监控采集系统中,摄像头采集到的一帧视频图像数据一般都是经过硬件自动压缩成jpeg格式的,然后再保存到摄像头设备的缓冲区.如果要把采集到的jpeg格式显示在本地LCD屏上,由于我们 ...

  8. HTTP协议:从原理到流程|乐字节

    这次给大家带来的是HTTP协议:从原理到流程的详解 一.HTTP 协议 HTTP 协议(Hypertext Transfer Protocol, 超文本传输协议),是一个客户端请求和回应的 标准协议, ...

  9. C++ 图片格式转化和压缩

    在做人脸识别底库图片导入的时候,需要支持主流的图片的格式,如jpeg.bmp.png等格式.所以需要对图片进行格式转化.图片过大的话,还有进行缩放等.本文介绍的是利用cximage开源库,来进行对图片 ...

  10. 【5】肿瘤DNA甲基化数据分析原理及流程

    目录 导论 DNA甲基化基本概论 检测DNA甲基化的方法 DNA甲基化数据分析流程及方法 DNA甲基化在肿瘤研究中的应用 导论 表观遗传:非DNA决定的基因表达,或表型改变中可遗传因素的研究 DNA水 ...

随机推荐

  1. C语言输出格式工整的日历——2乘6样式(详见本文)

    本篇博客有更新!!!更新后效果图如下: 文章末尾的完整代码如不能在Dev-C++上完好运行,出现如下问题: E:\Dev-Cpp\源代码\万年历.c [Error] 'for' loop initia ...

  2. ansible部署jdk source /etc/profile 不起作用?

    问题: ansible调用playbook远程mvn执行打包时发现执行出错,找不到JAVA_HOME.我们的exporter JAVA_HOME=/usr/java/jdk1.8.0写在/etc/pr ...

  3. 使用 `Roslyn` 分析器和修复器 对异步方法规范化返回Async结尾

    之前写过一篇使用修复器帮助添加头部注释文本的功能,今天使用Roslyn的代码修复器对异步返回方法规范化的功能 实现分析器 首先需要实现分析器,使用RegisterSyntaxNodeAction,分析 ...

  4. CSS & JS Effect – Show More

    效果 show more 是很常被使用的效果, 因为空间总是不够的丫. 比起 scroll, show more 的体验通常会好一些, 尤其在手机, 它有更好的引导. 实现思路   1. 卡片需要一个 ...

  5. HTML – Native Form 原生表单功能集

    前言 以前写过 form 表单, 但很不齐全, 这篇想做一个大整理. 主要讲讲在网站中使用原生 Form 的功能, 不足和扩展. 前端是原生的 HTML/JS, 后端是 ASP.NET Core Ra ...

  6. C++ STL queue容器——队列

    queue容器 基本概念 queue是一种**先进先出的数据结构,它有两个出口,queue容器允许从一端新增元素,从另一端移除元素. queue容器没有迭代器,所有元素进出都必须符合"先进先 ...

  7. vscode废掉了,跳转不到函数定义,无法自动补全,重装也没用的解决办法

    1. 先卸载掉所有已安装的插件 2. 卸载vscode 3. 删除个人配置和插件配置,涉及两个文件夹 4. 需要安装C/C++组件,下载对应的vsix文件 下载地址: https://github.c ...

  8. 76.最小覆盖子串 Golang实现

    题目描述: 给你一个字符串 s .一个字符串 t .返回 s 中涵盖 t 所有字符的最小子串.如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" . 注意: 对于 t ...

  9. 《Vue.js 设计与实现》读书笔记 - 第13章、异步组件与函数式组件

    第13章.异步组件与函数式组件 13.1 异步组件要解决的问题 用户可以简单通过 import 异步导入组件. <template> <component :is="asy ...

  10. 左值 <->右值

    左值引用指向左值 右值引用指向右值 int a = 5; int &ref_a = a; // 左值引用指向左值,编译通过 int &ref_a = 5; // 左值引用指向了右值,会 ...