圣者
精华
|
战斗力 鹅
|
回帖 0
注册时间 2008-2-7
|
本帖最后由 烈之斩 于 2019-1-3 23:00 编辑
这个问题一直有遇到,既然有别人问就稍微费点时间研究了下。
基本上,如果你在EH下那个几个专门发垃圾扫图的uploader(比如ninetydollardoujin、LWND),就会遇到这种情况。一个例子是这本。
根据问题的症状,基本可以看出是和progressive JPEG有关的。
所谓的progressive(一般翻译作步进)JPEG,就是在JPEG编码完成后,把数据不是一次、而是分为多个scan传输。这个分scan可以有多种方式。
最有意义的是可以把从低频到高频的64个分量、甚至每个分量内的比特分为多个不同的Scan来传递。
这样,在仅接收到前几个Scan的时候就可以进行解码,显示出粗略的图片,而不必等到图像读取完成;
之后再根据收到的更精细的高频分量以及less significant bit/digit来补全。
除此之外,分成多个Scan还有助提升后面的无损霍夫曼编码的压缩率。
另外,三个通道(Y,Cb,Cr)也可以分开传输。这也是本次问题的关键。当然,这个是要结合前两者的,否则你单独传了Y通道出来的是一个黑白图片,就失去了用progressive的意义了。
所以一般常见的应用场景是三种分法组合,用N个scan来逐渐地将每个通道从低频到高频、大比特到小比特逐渐传输。
广告:关于步进JPEG或者JPEG相关的话题,可以看我当年写的blog。
那么我们来看看这个问题。这里我们可以明显看到,在纵向上,Y(Luma)通道和Chroma通道没对齐:
也就是说问题确实出在不同通道混合还原上面:因为某些原因,解码器未能正确地把抽样过的色度通道的分辨率还原。另外,只有Y方向,可以看到这个JPEG用的是4:2:2的色度抽样(而不是常见的4:2:0,否则chroma就会缩到左上角1/4了)(至于8px(JPEG基本编码单元的大小)的条纹的产生原因显然也类似,但是我说不清具体,反正是bug就没必要纠结具体症状了)
我先用libjpeg-turbo的默认设置生成了个progressive的图像,却发现MM可以正常显示。可见MM并不是完全不支持progressive图像,只是支持的不好。
我接着用JPEGSnoop这个神级软件来看看这个bug图的header:
我们找到所有的Scan头(SOS,start of scan),然后把信息一一抄写下来。
最后可以看到这个图是分为了7个Scan,写成libjpeg-turbo的格式是:
- 0: 0-0, 0, 0 ;
- 1: 0-0, 0, 0 ;
- 2: 0-0, 0, 0 ;
- 0: 1-63, 0, 1 ;
- 0: 1-63, 1, 0 ;
- 1: 1-63, 0, 0 ;
- 2: 1-63, 0, 0 ;
复制代码
这里看不懂没有关系,就知道是三个通道(0,1,2)从DC分量开始就全部都是分成不同的Scan来传输就可以了。
事实上,只有DC分量有这个选择权:根据ITU T.81规定,progressive JPEG的AC分量必须分开传输,不能interleave。
再和libjpeg-turbo默认的、无显示BUG的“progressive” preset对比,那个是这样的:
- # 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 ;
复制代码
可以看到,这个虽然有高达10个Scan,但是所有三个通道的DC分量是interleave在一起同时传输的(第一行)。
经过简短测试(省略不谈)可以看到这正是问题所在,和后面的AC分量如何分配没有关系。MM的jpeg decoder无法正确解码三个通道的DC分量完全在不同Scan传输的图像。甚至,你如果使用这样的Scan(只分通道,别的完全不分):
MM会直接解码失败。
以上就是问题产生的原因。至于解决办法只能是修正那个jpg.dll,但是那个dll看上去是作者自己写的而不是什么通用库,而MM作者由于当年的开源风波早已删代码跑路,只能指望超能力大神反编译修改了。
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
评分
-
查看全部评分
|