你好啊,Progressive JPEG,十多年后再相见

2023-12-04 06:50

本文主要是介绍你好啊,Progressive JPEG,十多年后再相见,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文首发: http://blog.csdn.net/madongchunqiu/article/details/52813924

Web之初,渐进式(Progressive)图片还是很常见的,后来就渐渐消失了。现在都流行整块的高清大图,甚至还要上视频。

本想说说 Progressive JPEG 在移动端(手机)App上的轮回,但其实App兴盛之初,4G也同时上线了,因此也未见其在移动端有多大的应用。只是我们的App有个视频模块,整页都是视频截图,而截图均存放于S3,在国内做测试时速度较慢,逐行扫描(Baseline)出来的图片让整个页面此起彼伏,你可以想象那个场景。老板说在美国效果也没见有多好,于是还是想到了 Progressive JPG。

我们的图片效果还改成过:
1. 图片全部下载后才显示。但这样会有图片一个一个蹦出来的感觉,也很糟糕
2. 在1的基础上,做了一个 crossfade 的动画,图片是渐现式的出来,视觉上缓和很多。效果勉强还可以,就是网速问题会导致刚开始满屏的待下载 placeholder 图片。

之前其实就知道 Pinterest 有个PINRemoteImage,效果做得不错,大家可以鉴赏一番:
PINRemoteImage官方效果图
图一:PINRemoteImage官方效果图

Pinterest这个库利用模糊遮罩,巧妙的将丑陋的 Progressive JPEG 的前期图片装点得清新脱俗。既然效果这么棒,那就上呗。

然而我的文章从这里才开始。

一. 优化:一切都是为了显示效果

既然用上了 Progressive JPEG 了,并且解决了初始图片(以下称为初始 scan)的显示效果问题,那么紧接着的问题来了:到底快了多少?

万能的Github上有个 progressive-scans 的项目,可以将图片用 jpegtran 转成标准 Progressive JPG 后,告诉你每次 scan 占整个图片数据量的百分比。我修改了一点代码,使得可以打印出任意 Progressive JPEG 图片的每次 scan 所占的字节数:scan_jpeg.php 。以下图为例:
原图
图二:图片来自Flickr,并使用 jpegtran 标准渐进式参数编码

用 jpegtran 的标准渐进式参数转码后,得出的每次 scan 的字节数如下:

> jpegtran -optimize -progressive -outfile out.jpg in.jpg

scan No. 1, bytes = 7896 (8.52%)
scan No. 2, bytes = 10884 (11.75%)
scan No. 3, bytes = 4905 (5.29%)
scan No. 4, bytes = 6176 (6.66%)
scan No. 5, bytes = 12560 (13.55%)
scan No. 6, bytes = 15336 (16.55%)
scan No. 7, bytes = 1645 (1.78%)
scan No. 8, bytes = 6717 (7.25%)
scan No. 9, bytes = 7240 (7.81%)
scan No. 10, bytes = 19303 (20.83%)

可以看出初始 scan 仅需下载图片十分之一不到的数据量,效果杠杠的。根据 libjpeg 源代码中所含的 wizard.doc 文件,可以了解其默认的 scan 配置文件是这样的:

# Initial DC scan for Y,Cb,Cr (lowest bit not sent)
0,1,2: 0-0,   0, 1 ;
# First AC scan: send first 5 Y AC coefficients, minus 2 lowest bits:
0:     1-5,   0, 2 ;
# Send all Cr,Cb AC coefficients, minus lowest bit:
# (chroma data is usually too small to be worth subdividing further;
#  but note we send Cr first since eye is least sensitive to Cb)
2:     1-63,  0, 1 ;
1:     1-63,  0, 1 ;
# Send remaining Y AC coefficients, minus 2 lowest bits:
0:     6-63,  0, 2 ;
# Send next-to-lowest bit of all Y AC coefficients:
0:     1-63,  2, 1 ;
# At this point we've sent all but the lowest bit of all coefficients.
# Send lowest bit of DC coefficients
0,1,2: 0-0,   1, 0 ;
# Send lowest bit of AC coefficients
2:     1-63,  1, 0 ;
1:     1-63,  1, 0 ;
# Y AC lowest bit scan is last; it's usually the largest scan
0:     1-63,  1, 0 ;

文档内附有一个能解释95+%的说明,大意是说上面的文件中,每一行(entry) 代表着一次 scan 的描述,这个描述以冒号(:)分开,左边代表颜色的选取(根据JPEG 文件内的配置这里会不同,例如:0-Y, 1-Cb, 2-Cr),右边代表选择 DCT 变换后选用哪些 bits。右边四个数Ss、Se、Ah、Al,前两个指定位置范围,后两个指定选用位置的哪些 bit。wizard.doc 文件关于这四个参数的描述有点不太清楚,我在 books.google.com 上搜到了更详尽的描述:

Ss: Start of spectral selection OR predictor selection, …
Se: End of spectral selection, …
Ah: Successive approximation bit position high, …
Al: Successive approximation bit position low OR point transform, …

按照这些描述,可以将 jpegtran 默认的 scan 顺序绘制如下:

下面三张图中,纵向是颜色成份,横向是 8x8 DCT 后按权重排序(zigzag) 的位置信息。圈圈中的数字是扫描次数 (scan number),该配置总共有10次scan。

jpeg_scan1
图三-1:前5次 scan 将所有位置上的高位(红色)信息发送出去

jpeg_scan2
图三-2:第6次 scan 将Y信息的中位(绿色)信息发送出去

jpeg_scan3
图三-3:最后4次 scan 将所有位置上的最低位(蓝色)信息发送出去

二. 分析:死理性的不归路

jpegtran 的默认配置效果还不错,不过我们当然不想止步于此,咱还想着 “私人定制”呢。

为了在第一时间获取到数据,初始 scan 的数据量当然是越小越好,小到最少可以是?

# scanfile1
# 将DC位置的信息分成16份发送,Al(最后一个数字)是4bit
0,1,2: 0-0,   0, 15 ;
# 下略

运行:

> jpegtran -optimize -scans scanfile1 -outfile out.jpg in.jpg

现实的恶意凸显了,jpegtran 报错,不支持 scanfile1 的配置。


继续尝试,直到将Al设置成10,jpegtran 才接受了。也就是说将 DC 位置的信息做11次发送:

# scanfile2
# 将DC位置的信息分成11份发送
0,1,2: 0-0,   0, 10 ;
# 下略

使用上面的程序看看初始 scan 的数据量,不错哟:

scan No. 1, bytes = 2004 (1.97%)

这里有个程序:jpeg_split 可以将每次 scan 的数据分离出来,可以看到每次 scan 后的图片。那么我们最小数据量的图长啥样呢?这里…
scanfile2图
图四:将 DC 分成11份来传对应的初始 scan 图

不忍直视!看来数据太少了的确也有问题。


继续加大输出:

# scanfile3
# 将DC位置的信息分成7份发送
0,1,2: 0-0,   0, 6 ;
# 下略

数据量:

scan No. 1, bytes = 2584 (2.70%)

scanfile3图
图五:将 DC 分成7份来传对应的初始 scan 图

感觉好了一些,但还是不能看啊。


再一次加大输出:

# scanfile4
# 将DC位置的信息分成4份发送
0,1,2: 0-0,   0, 3 ;
# 下略

数据量:

scan No. 1, bytes = 5014 (5.39%)

scanfile4图
图六:将 DC 分成4份来传对应的初始 scan 图

挺不错的。相对于标准图(图二),第一次的传输量下降了一半,效果还是让人满意的。


还有一个想法是初始 scan 仅传Y,不传Cb、Cr,这样

# scanfile5
# 初始scan仅传Y,不传Cb、Cr
0: 0-0,   0, 3 ;
1,2: 0-0, 0, 3 ;
# 下略

按照 scanfile5,第一次传输的数据差不多又要节省一半还多,但却是一张灰度图,我是无法接受的。

写到这里,基本能玩的都玩到了,结论是:jpegtran 的默认配置基本够用了,如果对初始 scan 的数据要求更加严格,可以采用 scanfile4,但可能会面临初始 scan 图像偏色或者严重失真的情况

附: 其它方向

在网上查资料时,有个人提出了自己的需求:初始 scan 希望能更清晰一些,这样既能加快显示,又能在一开始就呈现出较好的效果。
然而,Progressive JPEG 的标准在这里设置了一道坎。从 book.google.com 中搜索到的 JPEG 标准中写到:

Each band is a continuous range of DCT coefficients using the zigzag order. The only restriction on the coefficients in a band, other than that the band must contain a continuous range, is that the DC component must be in a band by itself. At a minimum, a component will be divided into two scans: one containing the DC coefficient and the other the AC coefficients. At the most extreme the component can be divided into 64 bands with one coefficient in each.

意思就是说,DC 只能单独发送,因此初始 scan 理论上最大的发送配置是:

# scanfile6
# 初始scan发送尽可能多的数据
0,1,2: 0-0,   0, 0 ;
# 下略

数据量:

scan No. 1, bytes = 9490 (10.09%)

scanfile6图
图七:最大限度发送的初始 scan 图

这张图和标准图相比,传输的数据量和呈现效果都没有较大区别。所以,这个”第一次就传送较大的数据量以呈现更好的效果”的尝试,只能宣告失败了。


下表总结了上面的各初始 scan 图像质量和对应数据量:

初始scan数据量百分比图像质量
DC分成11份1.97%惨不忍睹
DC分成7份2.70%较大偏差
DC分成4份5.39%可以接受
DC分成2份(标准)8.52%可以接受
DC最大程度发送10.09%与标准无甚不同

(完结)

这篇关于你好啊,Progressive JPEG,十多年后再相见的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/452544

相关文章

研究人员在RSA大会上演示利用恶意JPEG图片入侵企业内网

安全研究人员Marcus Murray在正在旧金山举行的RSA大会上公布了一种利用恶意JPEG图片入侵企业网络内部Windows服务器的新方法。  攻击流程及漏洞分析 最近,安全专家兼渗透测试员Marcus Murray发现了一种利用恶意JPEG图片来攻击Windows服务器的新方法,利用该方法还可以在目标网络中进行特权提升。几天前,在旧金山举行的RSA大会上,该Marcus现场展示了攻击流程,

Unstructured cannot write mode RGBA as JPEG 错误解决

Unstructured cannot write mode RGBA as JPEG 错误解决 0. 错误详细1. 解决方法 0. 错误详细 Image Extraction Error: Skipping the failed imageTraceback (most recent call last):File "/root/miniconda3/envs/learn-y

基于FPGA的开源项目:FOC/SHA/USB/JPEG等

文章目录 [1. USB 1.1控制器](https://github.com/WangXuan95/FPGA-USB-Device)[2. FOC控制算法](https://github.com/WangXuan95/FPGA-FOC)[3. BSV高级硬件描述语言入门指南](https://github.com/WangXuan95/BSV_Tutorial_cn)[4. 基于XDMA的

wsksvg - 支持SVG、JPEG、GIF、PNG、WebP格式图片的优化

前言 上一篇文章介绍了 wsksvg 插件的开发思路和灵感,而本篇则详细阐述了 wsksvg 扩展功能,以及技术的介绍。通过 wsksvg 插件,开发者可以高效地优化 PNG、JPG、JPEG、WEBP 和 GIF 图像,同时对 SVG 文件进行深入处理,包括优化、生成 Vue 和 React 组件以及转换为 Base64 编码格式。这些功能不仅简化了图像管理流程,也提升了应用的性能和用户体验。

Matlab JPEG详细介绍

作为一个基本的图像压缩方式,JPEG 已经得到了广泛的运用,但 JPEG 相关的基本原理,却经常被忽视,或解释得很不确切。这里我们详细讨论一下 JPEG 的编码原理,并结合实例来给出一个更加感性的认识。JPEG 编码的详细过程有着诸多的信息可以给我们巨大的启发,我们在这里讨论的就是要对这些信息做一个具体细致的分析,通过我们的讨论,大家会对 JPEG 编码过程中出现的内容有一个确切的了解,并且能

xml转txt,适应各种图片格式,如jpg,png,jpeg,PNG,JPEG等

xml转txt,适应各种图片格式,如jpg,png,jpeg,PNG,JPEG等 import xml.etree.ElementTree as ETimport osimport cv2import numpy as npimport globclasses = []def convert(size, box):dw = 1. / (size[0])dh = 1. / (size[1]

2014年总结,2015年你好!

2014年就这么转瞬的离去,估计是自己越来越成熟了吧。感觉现在时间过得越来越快!平时也总是提醒自己,多总结,提效率,有目标,戒懒惰!但真正都做到并不是这么容易的。 回望2014年,第一次有本命年的概念的一年,自己经历的太多,承担的也太多,成熟很多,对自己的意志力,勇气,心态,胸怀等都有很大提升! 一学习方面,除了强化python语言编程,接触JAVA编程。读了四本书,《做最好的自己》、《图穷变

【读书笔记】第1章 你好,C++并发世界

1.1 什么是并发(concurrency) 并发是指两个或更多独立的活动同时发生。 1.1.1 计算机系统中的并发 大多数计算机只有一个处理器,具有单个处理单元(processing unit)或核心(core),如今还有很多这样的台式机。通过“这个任务做一会,再切换到别的任务,再做一会儿”的方式,让任务看起来是并行执行的。这种方式称为“任务切换(task switching)”。如今,我

浏览器中的data类型的Url格式,data:image/png,data:image/jpeg!

所谓"data"类型的Url格式,是在RFC2397中 提出的,目的对于一些“小”的数据,可以在网页中直接嵌入,而不是从外部文件载入。例如对于img这个Tag,哪怕这个图片非常非常的小,小到只有一个 点,也是要从另外一个外部的图片文件例如gif文件中读入的,如果浏览器实现了data类型的Url格式,这个文件就可以直接从页面文件内部读入了。 data类型的Url格式早在1998年就提出了,时至

从零开始,认识游戏设计师(1)你好,未来的游戏制作人

前言 本博客主要参考游戏设计艺术,将带着大家一起对这本书进行一个精细地解读以及分析,虽然博主也热衷于编程,但是策划才是博主真正的归属。 本栏的主旨固然可以让你成为更好的游戏开发者,但是其实我更多想要让大家能从我的角度去重新审视游戏设计,去从本初的角度看待一款游戏。 本专栏更多会从游戏设计(策划)角度去进行讲解,去挖掘独属于游戏设计的深处内容。 游戏设计是什么? 这个问题,恐怕我们要谈很久