jpeg学习

news2025/2/3 23:59:47

相关最全的一篇文章链接:https://www.cnblogs.com/wtysos11/p/14089482.html

YUV基础知识

Y表示亮度分量:如果只显示Y的话,图像看起来会是一张黑白照。
U(Cb)表示色度分量:是照片蓝色部分去掉亮度(Y)。
V(Cr)表示色度分量:是照片红色部分去掉亮度(Y)。

YUV Formats分成两个格式:

紧缩格式(打包格式)(packed formats):将Y、U、V值储存成Macro Pixels阵列,和RGB的存放方式类似。
平面格式(planar formats):将Y、U、V的三个分量分别存放在不同的矩阵中。

YUV422平面:

在这里插入图片描述

YUV420平面格式:

在这里插入图片描述

YUV420内存布局:

在这里插入图片描述

转到YCbCr色彩空间后,就可以将 Cb(U) 和 Cr(V) 这两个通道进行降采样,这里一般是将 22 个像素变为 11 个像素,虽然分辨率下降到了四分之一,但对于人眼来说差别是不大的。

YUV422sp(Semi-Planar Semi)半平面模式:

在这里插入图片描述

RGB24一帧的大小size=width×heigth×3 B,RGB32的size=width×heigth×4,YUV标准格式4:2:0 的数据量是 size=width×heigth×1.5 B

平面格式与交错格式内存存储:

在这里插入图片描述

YUV与RGB变换

在这里插入图片描述

YUV编码例子

https://zhuanlan.zhihu.com/p/106355033

Level Offset 零电平偏置下移

该步骤的作用是,图像内容平均亮度较高,将0电平移到中间,平均亮度降低, 便于DCT变换量化后直流的系数大大降低,也就降低了数据量。

将灰度级 2 n 2^n 2n的像素值,全部减去 2 n − 1 2^{n-1} 2n1 ,数据形式由无符号数变为有符号数(补码),单极性数据变为双极性数据。

Z形编码

在这里插入图片描述

DCT变换

将图像分为8×8的像块;对于宽(高)不是8的整数倍的图像,使用图像边缘像素填充,以不改变频谱分布。然后对每一个子块进行DCT(Discrete Cosine Transform,离散余弦变换)

在这里插入图片描述
其中,C是8x8的DCT变换二维核矩阵,F ( u , v ) 是原始的数据。由于DCT变换是一个正交变换,故 C T = C − 1 C^T = C^{-1} CT=C1

变换核矩阵如下所示:

在这里插入图片描述

量化

JPEG系统分别规定了亮度分量和色度分量的量化表,色度分量相应的量化步长比亮度分量大。

对亮度和色度分量的DCT系数进行量化,使用如下量化表,该量化表是从广泛的实验中得出的。当然,也可以自定义量化表。

//标准亮度分量量化表
static const unsigned int std_luminance_quant_tbl[DCTSIZE2] = {
    16,  11,  10,  16,  24,  40,  51,  61,
    12,  12,  14,  19,  26,  58,  60,  55,
    14,  13,  16,  24,  40,  57,  69,  56,
    14,  17,  22,  29,  51,  87,  80,  62,
    18,  22,  37,  56,  68, 109, 103,  77,
    24,  35,  55,  64,  81, 104, 113,  92,
    49,  64,  78,  87, 103, 121, 120, 101,
    72,  92,  95,  98, 112, 100, 103,  99
};

//标准色度分量量化表
static const unsigned int std_chrominance_quant_tbl[DCTSIZE2] = {
    17,  18,  24,  47,  99,  99,  99,  99,
    18,  21,  26,  66,  99,  99,  99,  99,
    24,  26,  56,  99,  99,  99,  99,  99,
    47,  66,  99,  99,  99,  99,  99,  99,
    99,  99,  99,  99,  99,  99,  99,  99,
    99,  99,  99,  99,  99,  99,  99,  99,
    99,  99,  99,  99,  99,  99,  99,  99,
    99,  99,  99,  99,  99,  99,  99,  99
};

对DCT变换进行量化后,得到的量化结果,会出现大量的0,使用Z形扫描,可以将大量的0连到一起,减小编码后的大小。越偏离左上方,表示频率越高,通过量化,将图像的高频信息干掉了。

编码

直流编码:DPCM + VLC 可变长熵编码(采用Huffman)

8×8图像块经过DCT变换之后得到的DC直流系数有两个特点:

  1. 系数的数值比较大
  2. 相邻8×8图像块的DC系数值变化不大,冗余

根据这个特点,JPEG算法使用了差分脉冲调制编码(DPCM)技术,对相邻图像块之间量化DC系数的差值DIFF进行编码:

在这里插入图片描述

对DPCM后算出的DIFF差值使用Huffman编码。所以,DC系数会产生一张长度为16的Huffman码表。

在这里插入图片描述

交流:ZigZag Scan + Run Length Encoding+VLC

对于量化后的数据,我们将其分为两路进行处理。一路是AC通路,一路是DC通路。

ZigZag Scan+RLE是用于AC通路的,这是因为AC分量出现较多的0。JPEG采用对0系数的游程长度编码。而对非0值,则要保存所需数和实际值。
在编码之前,需要把二维的变换系数矩阵转换为一维序列,由于量化之后右下角高频系数大部分为零,采用ZigZag Scan读取可以制造较长的零游程,提高编码效率。在扫描中,如果后续的系数全部为零,则用“EOB”表示块结束。

在这里插入图片描述

RLE编码的过程:

例:例如,现有一个字符串,如下所示:
57,45,0,0,0,0,23,0,-30,-8,0,0,1,000…
经过RLE之后,将呈现出以下的形式:
(0,57) ; (0,45) ; (4,23) ; (1,-30) ; (0,-8) ; (2,1) ; (0,0)
注意,如果AC系数之间连续0的个数超过16,则用一个扩展字节(15,0)来表示16连续的0。

在这里插入图片描述

所以,最后总共有4张Huffman码表(亮度DC,亮度AC,色度DC,色度AC)。

那么,这些码表如何存储?源数据又放在哪里?针对这些未解之谜,接下来我们就分析JPEG的存储结构。

JPEG编码示例

1.1 分块

某个图象的一个8*8方块的亮度值:

在这里插入图片描述

1.2 Level Offset

Level Offset 后(每个点值减去128):

在这里插入图片描述

1.3 DCT变换

DCT变换后:

在这里插入图片描述

1.4 量化

在这里插入图片描述

其中,参照的量化表是:

在这里插入图片描述

量化时,使用量化前的值除以量化表中相应位置的值。具体计算公式如下:

B [ i ] = B [ i ] + 0.5 Q [ i ] B[i] = \frac{B[i] + 0.5}{Q[i]} B[i]=Q[i]B[i]+0.5

在可以损失一部分精度的情况下,如何用更少的空间存储这些浮点数?答案是使用量子化( Quantization ),简称量化。比如把要量化值分成16个区间,用0到16这样的整数来表示,这样只用4个bit就足够了。

随后,对于这个8*8方块的亮度量化后的数据分别进行AC和DC两路的编码。

量化过程可以参看如下网址的说明:
https://blog.csdn.net/weixin_43876729/article/details/121733344

1.5 编码

1.1 建立哈夫曼树

1)第一个码字必定为0。
如果第一个码字位数为1,则码字为0;
如果第一个码字位数为2,则码字为00;
如此类推。

2)从第二个码字开始,
如果它和它前面的码字位数相同,则当前码字为它前面的码字加1;
如果它的位数比它前面的码字位数大,则当前码字是前面的码字加1后再在后边添若干个0,直至满足位数长度为止。

由于没有1位的码字,所以第一个码字的位数为2,即码字为00;
由于2位的码字有两个,所以第二个码字位数仍为2,即码字为00+1=01;
第三个码字为3位,比第二个码字长1位,所以第三个码字为:01+1=10,然后再添1个“0”,得100;

如此类推,最后得到这个哈夫曼树如下:
在这里插入图片描述

哈夫曼表ID和表类型

这个字节的值为一般只有四个0x00、0x01、0x10、0x11。
0x00表示DC直流0号表;
0x01表示DC直流1号表;
0x10表示AC交流0号表;
0x11表示AC交流1号表。

在这里插入图片描述

查阅标记SOF0,可以得到图像不同颜色分量的采样因子,即Y、Cr、Cb三个分量各自的水平和垂直采样因子。

大多图片的采样因子为4:1:1或1:1:1。

4:1:1 即(22):(11):(1*1)

1:1:1 即(11):(11):(1*1)

记三个分量中水平采样因子最大值为Hmax,垂直采样因子最大值为Vmax,那么单个MCU矩阵的宽就是Hmax8像素,高就是Vmax8像素。

JPEG的压缩方法与BMP文件有所不同,它不是把每个像素的颜色分量连续存储在一起的,而是把图片分成Y,Cr,Cb三张子图,然后分别压缩。

解压缩实现细节:https://www.cnblogs.com/leaven/archive/2010/04/06/1705846.html

JPEG文件

JPEG的文件格式一般有两种文件扩展名:.jpg和.jpeg。严格来讲,JPEG的文件扩展名应该为.jpeg,由于DOS时代的8.3文件名命名原则,就使用了.jpg的扩展名,这种情况类似于.htm和.html的区别。

1992年颁布了JPEG File Interchange Format(JFIF),目前在互联网上用的最多的jpeg格式,接着又出现了EXIF格式,主要用于数码产品,记录了媒体的时间地点信息。

JPEG文件由一系列字段组成,每个字段都有marker(标记),由0xff开头。

在这里插入图片描述

(1)SOF marker(Start of Frame),这个字段定义了文件的起始

在这里插入图片描述
颜色分量信息字段解析
分量ID:1=Y, 2=Cb, 3=Cr, 4=I, 5=Q
水平垂直采样因子:0-3位:垂直采样系数,4-7位:水平采样系数。采样系数是实际采样方式与最高采样系数之比,而最高采样系数一般=0.5(分数表示为1/2)。比如说,垂直采样系数=2,那么2×0.5=1,表示实际采样方式是每个点采一个样,也就是逐点采样;如果垂直采样系数=1,那么:1×0.5=0.5(分数表示为1/2),表示每2个点采一个样
量化表ID:即使用的量化表的序号

(2)APP0(Application-specific),这个字段定义了JFIF格式

在这里插入图片描述

注意:
如果“缩略图X像素数目”和“缩略图Y像素数目”的值均>0,才有下面的
“RGB缩略图”,并且大小为 3×"缩略图X像素数目"×"缩略图Y像素数目"

(3)APPn(Application-specific),定义了其它格式,如APP1表示exif格式

在这里插入图片描述

(4)DQT(Define Quantization Table(s)),定义了量化表

在这里插入图片描述

(4)哈夫曼表

在这里插入图片描述

一般有4个表:亮度的DC和AC,色度的DC和AC。
表ID和类型:0-3位是HT号,4位是HT类型(0=DC表,1=AC表),5-7位必须为0。一般值为00,10,11,01

在这里插入图片描述

在这里插入图片描述

紫线16个字节的含义是:1-16bit长度的每个哈夫曼节点的个数,比如上图中1-16bit的哈夫曼节点个数分别是1,0,1,3,5,1,1,0,0,0,0,0,0,0,0,0
也就是说,1bit的节点个数是1,2bit的节点个数是0,3bit节点个数是1,4bit节点个数是3, …
黄线字节分别解释了上述范式哈夫曼编码的权重。
此处难以理解的是,此处的哈夫曼编码是范式哈夫曼编码,具有固定格式,因此可以使用此处的描述找到相应的编码。

范式哈夫曼编码:

  1. 最小编码长度的第一个编码必须从 0 开始。
  2. 相同长度编码必须是连续的。
  3. C j = 2 ( C j − 1 + 1 ) C_j=2(C_{j-1} + 1) Cj=2(Cj1+1)
Symbol (十六进制) |  Code  (二进制)
-----------------+---------------
0x01             | 00
0x02             | 01
0x00             | 100
0x03             | 101
0x04             | 1100
0x11             | 1101
0x21             | 11100
0x05             | 111010
...              | ...
0xf0             | 111111111110
0x24             | 11111111111100
0x62             | 11111111111101
0xf1             | 11111111111110

编码是按照固定格式规定的,具体编码可以查看如下网址:https://blog.csdn.net/xiaoyafang123/article/details/120370880

以下图为例,介绍哈夫曼编码的生成过程。

在这里插入图片描述

规则:

  1. 第一个编码的数字必定为0;如果第一个编码的位数为1,就被编码为0;如果第一个编码的位数为2,就被编码为00;如果第一个编码的位数为3,就被编码为000
  2. 从第二个编码开始,如果它和它前面编码具有相同的位数,则当前编码是它前面的编码加1;如果它的编码位数比它前面的编码位数大,则当前编码时它前面的编码加1之后再在后面添加若干个0,直到满足编码位数的长度为止。

哈夫曼表值:哈夫曼编码 :权值
00::
01:00 : 00
05:010,011,100,101,110 : 01,02,03,04,05
01:1110: 06
01:11110: 07
01:111110: 08
01:1111110: 09
01:11111110: 0a
01:111111110: 0b

最终生成的哈夫曼树:

在这里插入图片描述

在这里插入图片描述

后面的权值字段,因为长度为1的编码为0,所以必然以0开始。

(5)SOS(start of scan) 扫描行

标志:FF DA
长度:12
扫描行内组件长度:3

接下来的组件结构体:

struct Part{
byte id;		//组件ID
byte tn;		//Huffman表号
};

剩余3个字节:00 3F 00

图像数据:紫色线框内的数据

注意:

  1. 组件ID值:1 = Y, 2 = Cb, 3 = Cr, 4 = I, 5 = Q
  2. 哈夫曼表号:0-3位:AC表号 (其值=0-3),4-7位:DC表号(其值=0-3)
  3. 最后3个字节00 3F 00用途不明,忽略

在这里插入图片描述

使用哈夫曼表,实现对数据的解析。

以下图为例。

在这里插入图片描述

紫色线框中的二进制,解析为:

11110 0101111 0101 0110 1010

首先从DC的哈夫曼表中,读出11110,权值为7,接着从AC的哈夫曼表中读7位值2F,作为DC值。接着从AC表中,读出。

需要注意的问题

  1. jpeg文件使用网络字节顺序
  2. 段之间的"FF"可以有无限多个

参考链接

  1. https://blog.csdn.net/weixin_44874766/article/details/117444843
  2. https://blog.csdn.net/yun_hen/article/details/78135122

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

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

相关文章

内部类(3)

大家好,今天我们继续来看看内部类,今天我们来学习一下内部类的分类,我们来看看一共有几种,它们有什么作用,那么话不多说,我们直接开始。 9.1 内部类的分类 先来看下,内部类都可以在一个类的哪些位置进行定…

你还在用rand()生成随机数?

1. rand() 的缺陷 伪随机数生成器使用数学算法来产生具有良好统计特性的数字序列,但这些数字并非真正随机。 C 标准库中的 rand() 函数并不保证所生成的随机序列的质量。某些 rand() 实现生成的数字周期较短,且这些数字是可以预测的。对于有强伪随机数…

基于FPGA的2ASK+帧同步系统verilog开发,包含testbench,高斯信道,误码统计,可设置SNR

目录 1.算法仿真效果 2.算法涉及理论知识概要 2.1 2ASK调制解调 2.2 帧同步 3.Verilog核心程序 4.完整算法代码文件获得 1.算法仿真效果 vivado2019.2仿真结果如下(完整代码运行后无水印): 设置SNR8db 设置SNR20db 整体波形效果&…

RT-Thread中堆和栈怎么跟单片机内存相联系

现在RT-ThreadMCU的应用方式越来越普遍,RT-Thread需要配置MCU中的RAM到的系统中,进入系统内存管理,才能提供给基于实时系统的应用程序使用,比如给应用程序提供malloc、free等函数调用功能。在嵌入式软件开发中,我们经常…

2、Bert论文笔记

Bert论文 1、解决的问题2、预训练微调2.1预训练微调概念2.2深度双向2.3基于特征和微调(预训练下游策略) 3、模型架构4、输入/输出1.输入:2.输出:3.Learned Embeddings(学习嵌入)1. **Token Embedding**2. **Position Embedding**3…

TiDB 的MPP架构概述

MPP架构介绍: 如图,TiDB Server 作为协调者,首先 TiDB Server 会把每个TiFlash 拥有的region 会在TiFlash上做交换,让表连接在一个TiFlash上。另外 TiFlash会作为计算节点,每个TiFlash都负责数据交换,表连接…

3、redis的高可用

主从复制 主从复制:这是redis高可用的基础。哨兵模式和集群都是建立在此基础之上。 主从模式和数据库的主从模式是一样的,主负责写入,然后把写入的数据同步到从,从节点只能读不能写。read only。 不能做高可用的切换&#xff…

【架构-38】如何选择通信协议和数据格式

一、通信协议选择 不同的协议适用于不同的应用场景,关键在于数据传输的需求,如:实时性、带宽、可靠性等。下面是几种常见通信协议的适用场景: WebSocket 适用场景:实时、双向数据传输、低延迟、持久连接 特点&#x…

SpringCloudAlibaba 技术栈—Sentinel

1、什么是sentinel? Sentinel是一个用于微服务架构的流量管理和控制系统,它通过限制和控制进入系统的流量,来保护系统免受过载和故障的影响,确保服务的稳定性。简而言之,它就是一个帮助微服务在高负载情况下也能稳定运行的工具。…

初学STM32 ---高级定时器互补输出带死区控制

互补输出,还带死区控制,什么意思? 带死区控制的互补输出应用之H桥 捕获/比较通道的输出部分(通道1至3) 死区时间计算 举个栗子(F1为例):DTG[7:0]250,250即二进制&#x…

RoboMIND:多体现基准 机器人操纵的智能规范数据

我们介绍了 RoboMIND,这是机器人操纵的多体现智能规范数据的基准,包括 4 个实施例、279 个不同任务和 61 个不同对象类别的 55k 真实世界演示轨迹。 工业机器人企业 埃斯顿自动化 | 埃夫特机器人 | 节卡机器人 | 珞石机器人 | 法奥机器人 | 非夕科技 | C…

Hadoop HA安装配置(容器环境),大数据职业技能竞赛模块A平台搭建,jdk+zookeeper+hadoop HA

HA概述 (1) 所谓HA(High Availablity),即高可用(7*24小时不中断服务)。 (2) 实现高可用最关键的策略是消除单点故障,HA严格来说应该分为各个组件的HA机制,H…

国产文本编辑器EverEdit - 如何让输出窗口的日志具有双击跳转到文件指定行的功能

1 开发参考:编写脚本时如何向输出窗口打印可跳转到文件位置的日志 1.1 应用场景 编写脚本时,有时对文本进行分析,需要将提示信息打印到输出窗口,同时希望将文本的行、列信息也打印在日志中, 最好是双击日志信息可以跳…

《云原生安全攻防》-- K8s安全配置:CIS安全基准与kube-bench工具

在本节课程中,我们来了解一下K8s集群的安全配置,通过对CIS安全基准和kube-bench工具的介绍,可以快速发现K8s集群中不符合最佳实践的配置项,及时进行修复,从而来提高集群的安全性。 在这个课程中,我们将学习…

3、redis的集群模式

主从模式 哨兵模式 集群 主从模式:这是redis高可用的基础,哨兵模式和集群都是建立在此基础之上。 主从模式和数据库的主从模式是一样的,主负责写入,然后把写入的数据同步到从, 从节点只能读不能写,rea…

计算机图形学知识点汇总

一、计算机图形学定义与内容 1.图形 图形分为“图”和“形”两部分。 其中,“形”指形体或形状,存在于客观世界和虚拟世界,它的本质是“表示”;而图则是包含几何信息与属性信息的点、线等基本图元构成的画面,用于表达…

自动化测试模型(一)

8.8.1 自动化测试模型概述 在自动化测试运用于测试工作的过程中,测试人员根据不同自动化测试工具、测试框架等所进行的测试活动进行了抽象,总结出线性测试、模块化驱动测试、数据驱动测试和关键字驱动测试这4种自动化测试模型。 线性测试 首先&#…

医疗数仓数据仓库设计

医疗数仓数据仓库设计 数据仓库构建流程数据调研明确数据域构建业务总线矩阵明确统计指标交易主题医生主题用户主题评价主题 维度模型设计汇总模型设计 数据仓库构建流程 数据仓库分层规划 优秀可靠的数仓体系,需要良好的数据分层结构。合理的分层,能够…

Go-知识 注释

Go-知识 注释 行注释块注释包注释结构体&接口注释函数&方法注释废弃注释文档 在 go 语言中注释有两种,行注释和块注释 行注释 使用双斜线 // 开始,一般后面紧跟一个空格。行注释是Go语言中最常见的注释形式,在标准包中,…

1230作业

思维导图 作业 将广播发送和接收端实现一遍&#xff0c;完成一个发送端发送信息&#xff0c;对应多个接收端接收 自实验 //广播发送端 #include <myhead.h> #define G_PORT 8765 #define G_IP "192.168.124.255" int main(int argc, const char *argv[]) {//…