我继续进行重复实验,看看是否能弄清楚发生了什么。
程序
我使用默认设置(如下所示)使用GIMP中的“实噪声”滤镜(滤镜>渲染>云>实噪声...)生成了一个256 x 256像素的随机RGB图像:
结果:
然后,使用默认设置将图像另存为JPEG:
然后,我将图像传输到Windows,并通过在文件资源管理器中右键单击该图像并从菜单中选择“ 预览”,使用Windows Photo Viewer打开该图像。然后,我使用底部的按钮旋转了图像,并使用箭头键导航到下一张图像来保存图像。
对于下面的每个测试,我从原始图像的副本开始,然后在保存之前旋转(单击旋转按钮)相应的次数。以下是重新排列的大小(ls -l -r
):
size in bytes last-modified date
VVVVV VVVVV
-rwxrwx--- 1 root vboxsf 6258 Nov 8 11:24 original.jpg
-rwxrwx--- 1 root vboxsf 23645 Nov 8 11:30 cw.jpg
-rwxrwx--- 1 root vboxsf 23636 Nov 8 11:30 cw-cw.jpg
-rwxrwx--- 1 root vboxsf 23649 Nov 8 11:30 cw-cw-cw.jpg
-rwxrwx--- 1 root vboxsf 6258 Nov 8 11:27 cw-cw-cw-cw.jpg
-rwxrwx--- 1 root vboxsf 23649 Nov 8 11:31 cw-cw-cw-cw-cw.jpg
-rwxrwx--- 1 root vboxsf 23649 Nov 8 11:29 ccw.jpg
-rwxrwx--- 1 root vboxsf 23636 Nov 8 11:29 ccw-ccw.jpg
-rwxrwx--- 1 root vboxsf 23645 Nov 8 11:29 ccw-ccw-ccw.jpg
-rwxrwx--- 1 root vboxsf 6258 Nov 8 11:27 ccw-ccw-ccw-ccw.jpg
-rwxrwx--- 1 root vboxsf 23649 Nov 8 11:30 ccw-ccw-ccw-ccw-ccw.jpg
立即观察
- Windows Photo Viewer(WPV)确实会大大增加大小;在此测试中,增加量约为四倍!
- 所有新图像都增加到大约相同的大小,但它们并不相同。
- 当图像旋转360度的倍数时,WPV不会重新编码甚至重新保存图像。(时间戳为11:27,是首次复制文件的时间。)
cmp -l
在应具有相同内容的文件上使用,使我们可以查看文件的不同之处。
robert@unity ../jpeg-rotate-test % cmp -l cw.jpg ccw-ccw-ccw.jpg
2223 63 62
2224 60 71
2226 60 64
2227 60 66
robert@unity ../jpeg-rotate-test % cmp -l cw-cw.jpg ccw-ccw.jpg
2223 63 62
2224 60 71
2226 60 64
2227 62 64
robert@unity ..jpeg-rotate-test % cmp -l ccw.jpg cw-cw-cw.jpg
2223 62 63
2224 71 60
2226 64 60
2227 61 64
robert@unity ../jpeg-rotate-test % cmp -l cw.jpg cw-cw-cw-cw-cw.jpg
2221 60 61
2223 63 61
2224 60 66
2226 60 61
2227 60 61
robert@unity ../jpeg-rotate-test % cmp -l ccw.jpg ccw-ccw-ccw-ccw-ccw.jpg
2223 62 63
2224 71 60
2226 64 65
2227 61 64
这些文件只有四个字节的不同(实际上是一个时间戳),这意味着WPV每次都在做相同的事情。现在我们只需要弄清楚那是什么。
详细观察
为此,我使用JPEGsnoop来查看图像中到底是什么。
由于输出相当长,因此我已将其链接为要点。以下是差异的摘要:
GIMP仅将APP0
(JFIF)和COM
(注释)段用于元数据。WPV使该APP0
段保持不变,但奇怪的是在注释中添加了一个空字节(以便它以空终止)。
WPV添加了两个APP1
段,分别是Exif和XMP元数据。这些段分别为4286和12726字节。它们在一起几乎占了文件大小的全部增长。
GIMP生成渐进式JPEG,而WPV生成基线(非渐进式)JPEG。因此,GIMP的图像有多个扫描段,而WPV图像只有一个。以我的经验,渐进式图像有时会略小。
GIMP使用1×1色度二次采样,而WPV使用2×2色度采样。这使我相信WPV不会使用“真实的”无损旋转,除非它能够以某种方式检测到这是黑白图像。
为了解决这些问题,我进行了第二次测试。
程序
我遵循与第一次测试类似的步骤。我使用具有以下设置的RGB噪波滤镜(滤镜>鼻子> RGB鼻子...)创建了一个随机的256×256 RGB图像:
结果如下:
我使用以下设置将文件导出为JPEG:
渐进式已关闭,但子采样仍设置为4:4:4(这是1×1子采样的另一个名称)。质量增加到98。
我复制了图像,然后顺时针旋转了副本。然后复制旋转版本并逆时针旋转该副本,这样我们就可以直接比较原始版本和WPV处理过的副本之间的质量。
结果
-rwxrwx--- 1 root vboxsf 159774 Nov 8 16:21 original-random.jpg
-rwxrwx--- 1 root vboxsf 222404 Nov 8 16:24 cw-random.jpg
-rwxrwx--- 1 root vboxsf 222467 Nov 8 16:24 cw-ccw-random.jpg
尽管这次的增加相对而言较小(大约40%),但绝对增加甚至更大-大约62 kB。这表明WMV正在使用效率较低的编码。
我将使用ImageMagick比较两个图像:
robert@unity ../jpeg-rotate-test % compare -verbose -metric AE original-random.jpg cw-ccw-random.jpg null:
original-random.jpg JPEG 256x256 256x256+0+0 8-bit sRGB 160KB 0.000u 0:00.009
cw-ccw-random.jpg JPEG 256x256 256x256+0+0 8-bit sRGB 222KB 0.010u 0:00.010
Image: original-random.jpg
Channel distortion: AE
red: 0
green: 0
blue: 0
all: 0
original-random.jpg=> JPEG 256x256 256x256+0+0 8-bit sRGB 0.050u 0:00.020
有零个像素不同的原件和循环副本之间。因此,即使WPV没有使用“真正的”无损旋转,它也做得很好。我怀疑我知道发生了什么,并且要解释一下,我将稍微介绍一下JPEG压缩背后的数学原理。
JPEG压缩算法将图像分成8×8像素块。然后,对这些块中的每个块进行离散余弦变换(DCT)。所得的DCT系数将该块描述为不同频率波的总和。然后,该算法“丢弃”高频波中与噪声和非常小的细节相对应的一些信息。解码过程使DCT反向,将存储的波加在一起以返回该块。
无需实际撤消和重做变换就可以旋转DCT“波”(基本上,将所有水平波转换为垂直波,反之亦然)。我认为在WPV中发生的事情是图像实际上已经解码,旋转然后重新编码。在重新编码过程中,由于我们的图像尺寸在两个维度上均为8的倍数,因此每个新块都对应于一个原始块。重要的是,由于每个块都没有高频分量,因此该算法不会丢弃任何信息,并且可以找到“真正的”无损旋转将具有的正确DCT分量。
最后,我将再次查看JPEG文件的组件。结果再次与要点联系在一起。比较两者:
WPV图像包含额外的4286 + 2字节的Exif元数据,注释中的1个额外字节和12726 + 2字节的XMP元数据。这是总共17,017字节的其他元数据。这些数据全部用来做什么?我使用值得信赖的十六进制编辑器和相关标准的副本浏览了该文件:
Exif元数据的结构类似于TIFF图像,其中包含许多标签(方式更加复杂,但我将跳过它)。Exif段中的大多数字节都包含在两个相同的标签号为标签EA1C
(十进制59,932)的标签中。在我能找到的任何地方都没有记录该标签号。这两个标记都包含2060个“未定义”类型的字节,除了前六个(1C EA 00 00 00 08
)之外,所有字节均为空字节。我不知道这些标签是什么,为什么有两个标签,为什么每个标签都需要2 kB。
XMP元数据实际上是一个完整的带有名称空间和长UUID的嵌入式XML文档,其中仅包含WPV版本字符串(该字符串已在Exif元数据中)。但是,这仅占大约400个字节。该段的其余部分是100个空格的122个重复,后跟换行符。完全浪费的空间超过12,000个字节。
像以前的测试一样,GIMP和WPV都使用相同的DCT量化表。这意味着他们应该计算出完全相同的DCT系数,这就是为什么图像完全相同的原因。我不确定WPV是否恰好使用了相同的量化表,还是从输入中复制了这些表。
与之前的测试不同,这次WPV使用1×1二次采样,因此实际上可能是在检测这是彩色图像(或者至少需要更高的样本才能无损地重新编码图像)。
GIMP和WPV使用不同的霍夫曼表(熵编码步骤的一部分)。WPV的表大了总共279个字节,在一种情况下,包含7倍的代码。
查看JPEGsnoop的统计数据,我们可以发现其中一些代码很少使用。例如,在ID: 1, Class: AC
表中,在定义的119个16位代码中,实际上仅使用了23个。总体而言,在WPV版本中,实际扫描段要大28.5%。
摘要
版本信息:
作业系统(Linux)(uname -a
):
Linux unity 3.16.0-4-amd64 #1 SMP Debian 3.16.36-1+deb8u1 (2016-09-03) x86_64 GNU/Linux
作业系统(Windows):
GIMP(Linux):2.8.14(来自package gimp
,版本2.8.14-1+deb8u1
)
窗口照片查看器(根据图像元数据):
Microsoft Windows Photo Viewer 10.0.10586.0