1. .jpg, .png, .gif

说到图片,我们首先会想到,几种常见图片格式,如:.jpg, .png, .gif 等。

但当我门在说图片的格式时,除了在说图片文件的后缀不同,还有什么不同呢?

事实上,图片的格式,在技术上,是指图片所遵循的压缩标准。更准确地说,是数字图像的压缩标准(计算机上的图片都是数字图像,即由 0 和 1 构成的二进制数字图像文件)。

可能会有人不明白,为什么图片的格式是压缩标准? 图片为什么要压缩? 难道存储在我们个人电脑的图片都是压缩的?

没错,不管是存储在我们个人电脑,手机,还是在网络上图片其实都是经过压缩后的图片数据。

那么,压缩前的原始图像数据又是什么样的? 以及为什么要对图像进行压缩?

2. 原始图像数据

不管是什么格式,或采用什么样的压缩标准,原始的图像数据其实都是一样的,而且也符合我门直观的理解。

例如,一张 4 × 4 (宽度和高度都是 4 个像素)的彩色图片,未压缩的的原始图像数据,就是一个 4 × 4 矩形网格,每一个网格代表一个像素。

而彩色图片的每一个像素,又是由 红,绿,蓝 三基色构成,如下图右边所示,红绿蓝,对应于 r g b 三个数值,也就是我常说的 RGB 色彩模式。

RGB,我们在计算机视觉领域,又称为颜色通道,彩色图像有三个通道值,每个颜色通道,都是一个 0~255 的整数值,占用一个字节(Byte)的存储空间。

因此,我们很容易计算上面这张 4×4 彩色图片占用的存储空间为 4 × 4 × 3 = 48 字节 (Bytes) 。换算成我们熟悉的 KB,就是 48 / 1024 = 0.046875 KB,不到 0.1 KB。

事实上,我们很少见到这么小的图片,甚至在我们的个人电脑和手机上,根本无法正常看到这么小的图片。这里为了方便理解和计算,做了技术上的处理,而不是真实看到的图片大小。

拓展:按照在电脑上常用的分辨率 72 ppi (Pixels Per Inch:像素每英寸),即 每 2.54 厘米 容纳 72 个像素,或者说,一个像素占用的屏幕尺寸是 0.35 毫米,那么上面 4 × 4 图片,在屏幕上 1:1 显示,占用屏幕的物理尺寸只有 1.4 × 1.4 毫米。显然,用肉眼是无法看清的。

在理解一张 4 × 4 的彩色图片占用存储空间大小,我们同样的方式计算如下,320 × 320 的彩色图片,这个大小在我们日常生活,也不算一张大图,相当于我们用作微信头像的大小。

我相信我们可以很快得出结果,320 × 320 × 3 = 300 KB ,相当于上面 4 × 4 图片的 6000 多倍。

可能大家觉得这张图片还不算大。我测算,用自己的 iPhone 8 Plus 正常拍摄一张手机照片,它的大小是 3024 × 4032 ,这样一张图片在未压缩的情况下,所占用的存储空间大小是 3024 × 4032 × 3 = 35 MB 。而实际,如下图,在我的 Mac 上看到的图片, 只有 6.8 M ,也就是说,我们在使用手机拍摄照片后,在保存在相册之前,相机程序已经自动对我们拍摄的照片照片进行了压缩,这里的压缩比是 35 / 6.8 = 5,压缩比并不是一个固定值,也就是说同样大小的不同照片,在经过相同的压缩处理后,占用磁盘的空间也是不一样的。

事实上,图像压缩在数字图像处理领域,是应用最为普遍的和成功的,大部分图片查看器,编辑器,网页浏览器,等与图片相关的应用程序,乃至,开发人员使用图片处理库,底层都使用了图像的压缩和解压缩算法,并且对于用户,或者上层的应用开发人员,是完全透明的,以至于我们觉察不到它的存在。

PS: 图像的压缩和解压缩,也称之为,编码和解码,其实是同一个意思,并且适用于数字视频的编解码

3. 图像压缩

如果,大家对上文,将手机拍摄的一张原始图像是 35 MB 压缩保存后是 6.8MB ,没有太大的概念。

那么,我们不妨再用电影举例,一部宽高为 720 × 480(彩色),帧率为 30 帧/秒,时长为 2 小时的电影,其未压缩前的大小是:

720 × 480 × 3 (字节/像素) × 30 (帧/秒) × 3600 (秒/小时) × 2小时 = 209 GB

不考虑音频,电影的画面,本质就是由一张张连续显示的图像构成,每一副图像,我们称之为一帧)

也就是一个 1TB 的移动硬盘,只能装下不到 5 部这样的清晰度一般的电影。这显然是不能接受,也与我们日常生活对电影存储的认知不符。

因此,我们要感到庆幸,对图像和视频的压缩算法,无时无刻不在为我们的数字生活服务。我们没有觉察到,但一定不能忽视它的存在。

3.1 存储在磁盘上真实图像的二进制数据

事实上,图像的压缩或编码,本质就是为了解决图像在存储和网络传输过程的空间消耗,让有限的磁盘和网络带宽,存储和传送海量的数字图像和视频提供了技术后盾。

那么压缩后的图片数据到底长啥样?

我们依然使用前文用到的那只可爱的 小狗狗 图片,它在我电脑上文件名为 dog.jpeg。

我们知道,不同于普通文本文件,图片在计算机里存储形式,是二进制文件。

在 linux 和 MacOS 系统上,我们可以借助一个命令行工具 hexdump 来查看任何二进制文件,包括图片。

读者,可以将下面这张图片 保存到 自己的电脑上。

在命令行界面,进入 dog.jpeg 文件所在目录,运行如下命令:

hexdump dog.jpeg
# 输出结果如下(中间数据已省略,只显示开头和结尾各两行):
# 0000000 ff d8 ff e0 00 10 4a 46 49 46 00 01 01 00 00 48
# 0000010 00 48 00 00 ff e1 00 8c 45 78 69 66 00 00 4d 4d
# ....
# 0004780 04 12 48 f5 a5 70 0b 82 18 7c 8c 30 39 cf 4e be
# 0004790 f5 11 82 30 c3 f7 47 00 12 39 3c 50 08 ff d9

Tips: 如果想对显示的格式进行控制,可以尝试增加如下选项,格式化显示输出:

hexdump -e '16/1 "%02X "' -e '"\n"' dog.jpeghexdump -e '16/1 "%02X " "  |  "' -e '16/1 "%_p" "\n"' dog.jpeg

我们看到的输出如下图所示(中间内容省略,这里只截取了开头和结尾各两行):

图中,红线框圈住的部分,是图片数据的字节流编址,可以看作是为了查看方便,添加的行号,红框右边的才是图片的真实存储字节流,并且每行显示 16 个字节。当然不管是“行号”还是图片数据,为了显示的简介性,默认都是用了16进制。

这里我忽略红框中的“行号”,只关注图片字节流数据。

这里要注意的是,图中数据是一行行显示的,并且每行中,字节间都有空格,其实,这里还是为了方便查看才这样显示的,真实存储的数据并非一行一行,字节间也没有空格,所谓字节流,就是图片数据字节都是连续不间断的,串成一条线,在程序里,体现为一个一维的字节数组。

为了验证这点,我们不妨用实践说话。

在一台已经安装了Python(MacOS 内置了 Python 2)机器,启动命令行,输入 python 进入 python 交互式编辑环境。使用如下 python 代码,查看 图片 dog.jpeg 的二进制字节流。

with open("dog.jpeg", "rb") as f:image_bytes_data = f.read()image_bytes_data[:16]
image_bytes_data[-16:]

运行输出如下:

image_bytes_data 以字节为单位,保存着图片二进制数据,可以使用切片,查看前 16 个字节 和最后 16 个字节。通过与前文使用 hexdump 查看的数据对比,可以看出是一致的。细心的读者可能发现,在 hexdump 显示结果的最后一行,只有 15 个字节。因此,这里看到最后 16 个字节,是从倒数第二行最后一个字节开始的。

3.2 图像二进制数据格式

我们已经知道如何通过命令行工具 hexdump 和 python 脚本查看图片的二进制数据,并且我们知道这不是图片原始的二维RGB阵列数据,而是经过压缩后,方便存储和网络传输用的一维二进制字节流。

那么这些字节数据,到底代表什么意思,我们使用的图片应用程序如何根据这些数据,解压缩或解码,还原成,计算机显示器可以显示的二维 RGB 像素阵列呢?

本文仅仅对字节流数据组成格式,各部分代表的含义进行简单介绍,以对图片存储数据解码有个基本认识,对于解码部分的完整实现,超出本文的讨论范围,感兴趣,推荐参考专业书籍或开源图片编解码器。

3.2.1 标记数据

首先,还是引用前文的数据截图:

我们注意到用橙色线框框着的两个部分,ff d8 表示图片数据开始,英文缩写 SOI (Start Of Image),ff d9 表示图片数据的结尾,英文缩写 EOI (End Of Image)。

我们不难发现,两者都是以 ff 开头。事实上,图片存储的数据,大体只包含两类数据,一类是 ff 开头,后跟1个字节, 这个字节既不能等于 0 也不能等于 ff,表示不同类型的标记(Marker)数据,而剩下的就是图片的压缩数据或编码数据。

由于标记数据记录着图片的元数据,同时决定了,图片压缩数据如何解码。因此我们重点介绍标记数据。

为了通过编程实践,更好地理解不同的标记数据,我们根据已经掌握的标记数据特点,即,标记数据都是以 ff 开头,后跟一个, 既不能等于 0 也不能等于 ff 的字节 类型。编写如下 python 脚本来提取图片中的标记数据。

Warm Tips:如下代码,请在 python3 环境下运行

将以下 python 脚本复制,保存到文件 view_dog_marker_data.py

# 从磁盘读取图片二进制字节流数据
with open('dog.jpeg', 'rb') as f:image_data = f.read()image_data = ['%x' % image_data[i] for i in range(len(image_data))]# 解析标记数据,保存到字典 tagmarker
tagmarker = dict()
tag = ''
tag_start = False
data_start = False
for i, b in enumerate(image_data):if len(b) == 1:b = '0' + bif b == 'ff':tag_start = Truecontinueif tag_start:if b != 'ff' and (b != '00'):tag = 'ff' + bif not tag in tagmarker:tagmarker[tag] = list()tag_start = Falsedata_start = Truecontinueelse:tag_start = Falsetagmarker[tag].append('ff')if data_start:tagmarker[tag].append(b)# 查看解析后,有那些标记数据,以及对应数据的长度
for tag, arr in tagmarker.items():s = len(arr)if s == 0:print(tag)continueprint("{}:\t{}\tbytes".format(tag, s))

使用 python3 运行脚本:

python view_dog_marker_data.py

输出结果如下:

ffd8
ffe0:   16      bytes
ffe1:   140     bytes
ffed:   56      bytes
ffc0:   17      bytes
ffc4:   424     bytes
ffdb:   134     bytes
ffdd:   4       bytes
ffda:   457     bytes
ffd0:   2391    bytes
ffd1:   2129    bytes
ffd2:   2571    bytes
ffd3:   2008    bytes
ffd4:   1936    bytes
ffd5:   1977    bytes
ffd6:   1951    bytes
ffd7:   2058    bytes
ffd9

3.2.2 标记数据字段含义

我们看到,除了已经知道的 ffd8ffd9 分别表示图片的开头和结尾,并且后面没有内容数据,其他我们还不知道的标记数据,后面都有不同长度的内容数据。

下面直接给出,这些标记数据对应字段:

字节码 标记符
ffd8 SOI*
ffe0 … ffef APP0 … APP15
ffdb DQT
ffc0 ffc1 ffc2 ffc3 SOF0 SOF1 SOF2 SOF3
ffc5 ffc6 ffc7 SOF5 SOF6 SOF7
ffc8 ffc9 ffca ffcb JPG SOF9 SOF10 SOF11
ffcd ffce ffcf SOF13 SOF14 SOF15
ffc4 DHT
ffcc DAC
ffd0 … ffd7 RSTm*
ffda SOS
ffdb DQT
ffdc DNL
ffdd DRI
ffde DHP
ffdf EXP
fff0 … fffd JPG0 … JPG13
fffe COM
ff01 TEM*
ff02 … ffbf RES
ffd9 EOI*

为了在前面的脚本基础上,得到字节码对应的标记符,创建如下字典:

tag_map = {"ffd8": "SOI","ffc4": "DHT",'ffc8': "JPG",'ffcc': "DAC","ffda": "SOS","ffdb": "DQT","ffdc": "DNL","ffdd": "DRI","ffde": "DHP","ffdf": "EXP","fffe": "COM","ff01": "TEM","ffd9": "EOI"
}for i in range(16):tag = 'ffe%x' % itag_map[tag] = 'APP'+str(i)for i in [0, 1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 14, 15]:tag = 'ffc%x' % itag_map[tag] = 'SOF'+str(i)for i in range(8):tag = 'ffd%x' % itag_map[tag] = 'RST'+str(i)for i in range(14):tag = 'fff%x' % itag_map[tag] = 'JPG'+str(i)map_tag = { v: k for k, v in tag_map.items() }

通过字典,将字节码替换成标记字段,查看解析结果:

for tag, arr in tagmarker.items():s = len(arr)if tag in tag_map:tag = tag_map[tag]if s == 0:print(tag)continueprint("{}:\t{}\tbytes".format(tag, s))

输出结果如下:

SOI
APP0:   16      bytes
APP1:   140     bytes
APP13:  56      bytes
SOF0:   17      bytes
DHT:    424     bytes
DQT:    134     bytes
DRI:    4       bytes
SOS:    457     bytes
RST0:   2391    bytes
RST1:   2129    bytes
RST2:   2571    bytes
RST3:   2008    bytes
RST4:   1936    bytes
RST5:   1977    bytes
RST6:   1951    bytes
RST7:   2058    bytes
EOI

到此为止,我们完成了对图像存储数据的初步解析,进一步解析出各标记数据的详细信息,以及图片压缩数据,期待后续更新。

附件

下图为 国际电信联盟 (INTERNATIONAL TELECOMMUNICATION UNION) 发布的静态图像数字压缩和编码规范中,关于标记码的分配表,也是本文解析图片标记数据,的参考依据。感兴趣,可以通过此表,了解到本文未详尽的内容,如关于标记数据的描述说明。

参考

  • [1] T.81 page 34
  • [2] JPEG File Interchange Format

坚持写专栏不易,如果觉得本文对你有帮助,记得点个赞。感谢支持!

  • 个人网站: https://kenblog.top
  • github 站点: https://kenblikylee.github.io
  • 掘金: https://juejin.im/user/5bd2b8b25188252a784d19d7
  • 知乎: https://www.zhihu.com/people/kenbliky/posts


微信扫描二维码 获取最新技术原创

JPEG 图片存储格式与元数据解析相关推荐

  1. ]JPEG图片存储格式及原理

    JPEG是联合图象专家组(Joint Picture Expert Group)的英文缩写,是国际标准化组织(ISO)和CCITT联合制定的静态图象的压缩编码标准.和相同图象质量的其它常用文件格式(如 ...

  2. JPEG图片格式简单分析

    JPEG文件格式简单分析 作者:小爽 摘要: 这篇文章大体上介绍了JPEG文件的结构信息以及它的压缩算法和编码方式.使读者能够对JPEG文件格式有大体上的了解.为读者进一步进行学习JPEG文件压缩做好 ...

  3. Android图片编码机制深度解析(Bitmap,Skia,libJpeg)

    问题 工作中遇到了Android中有关图片压缩保存的问题,发现这个问题还挺深,而且网上资料比较有限,因此自己深入研究了一下,算是把这个问题自顶至下全部搞懂了,在此记录. 相关的几个问题如下: 1.An ...

  4. w806开发板驱动ov2640读取jpeg图片1600x1200分辨率,以及花屏原因及解决办法

    主频需要160MHz以上,80MHz主频读取会丢数据,读取过程中要关闭所有中断否则会出现丢数据花屏现象,还有一个重要的地方需要注意,PCLK速度过慢同时照片信息量多时,jpeg文件过大也会花一部分,像 ...

  5. jpg图片与jpeg图片格式的区别(没有区别,.jpg只是扩展名.jpeg的缩写)JPEG图像压缩(YUV4:2:0 缩减采样、缩减取样)(离散余弦变换 DCT算法)(量化)(熵编码)(霍夫曼哈夫曼)

    文章目录 20191026 20220414 更新,更系统去了解里面的编码压缩流程 科普:关于图像格式JPG和JPEG你知多少? 一.前言 二.JPEG和JPG的关系 三.色彩空间转换 缩减取样 离散 ...

  6. Google图片存储格式WebP增加与PNG类似背景透明效果

    Google近日在博客上通报了他们提出的新型图片存储格式 - WebP - 的最新进展.Google有意让WebP成为新的网络图形存储格式标准以挑战JPEG.PNG等格式的地位.从博文中我们了解到,W ...

  7. 【图像处理】jpeg图片格式详解

    jpeg图片格式详解 1. JPEG文件简介 JPEG的全称是JointPhotographicExpertsGroup(联合图像专家小组),它是一种常用的图像存储格式, jpg/jpeg是24位的图 ...

  8. 网页图片存储格式-SVG

    今天来聊聊图片存储的格式-svg SVG是一种图像文件格式,它的英文全称为Scalable Vector Graphics,意思为可缩放的矢量图形.它是基于XML(Extensible Markup ...

  9. 如何将PDF转换成JPEG图片?

    ▌01 将PDF转换成JPEG 1.为什么转换? 有的时候需要将PDF文件转换成对应的JPEG文件嵌入在某些文档中,比如对应的PowerPoint,CSDN网页中.这方面的需求如同 使用 python ...

  10. 海思AI芯片(Hi3519A/3559A)方案学习(十四)JPEG图片转换成bgr文件

    原文:https://blog.csdn.net/avideointerfaces/article/details/89931156 前言 在系列文章海思AI芯片(Hi3519A/3559A)方案学习 ...

最新文章

  1. pandas使用replace函数替换dataframe中的值:replace函数对dataframe中指定数据列的值进行替换、替换具体数据列的相关值
  2. 《研磨设计模式》chap8 生成器模式Builder
  3. 关于ExtJS通过单击左边的treePanel在居中的panel加载页面问题
  4. Linux版本号含义
  5. [C#] 動的にアセンブリをロードする
  6. secucrecrt配置文件导入和保存
  7. mfc大观之五、六(消息机制和消息运行)
  8. 基础知识的困惑让BUG更隐蔽
  9. 事物(三)之服务端事务匹配请求
  10. 2022电工杯A题利用启发式算法寻优
  11. 前端框架EasyUI
  12. 2019eclipse 中文汉化包 安装教程
  13. 耳机插在主机后面声音很小,音频软件测试很大声音,如何解决电脑前面耳机没声音后面却正常的问题...
  14. php中的递归调用函数返回值,深入理解php递归函数返回值的正确用法
  15. CEIWEI CommMonitor 串口监控精灵v12.0 串口过滤;串口监控;Serial port monitor tools
  16. 慕课软件工程(第五章.初始模块结构图的设计)
  17. 李嘉诚14句经典成功格言
  18. 神秘的程序员6 没事笑一笑
  19. 利用Excel实现数据抽样
  20. 使用U盘安装Linux系统经验总结

热门文章

  1. 2021-5-17:Spring Boot整合Redis
  2. 一分钟了解业务流程图与功能流程图的区别
  3. excel工具栏隐藏了怎么办_?Excel菜单栏中工具栏突然不见了,怎么办?
  4. php 截掉最后一个字符_php 截取并删除字符串最后一个字符的方法
  5. arcgis发布服务后符号字体丢失解决办法
  6. Nginx 局域网内互传文件
  7. 如何准备全国计算机二级Python,二级Python考试技巧
  8. [渗透测试]ATTCK实战 | Vulnstack 红队(一)
  9. 微信特殊字符php,PHP方法处理微信昵称特殊符号过滤
  10. HP台式计算机不能启动,惠普电脑不能启动怎么处理