前情概要(/t/835906):
本科毕设,用 Java 从零开始写一个 JPEG 编码器,v 友们的回答使我受益匪浅。
目前转换 YCC 、DCT 、量化已经完成,用 ISO/IEC 10918-1:1993 中 K.3 推荐的 Huffman 表,可以完美运行。
然而,用固定的 Huffman 表压缩效果不一定是最好的,所以我打算根据单个图片本身生成一个新的表。
单个图片中,并不是所有 Run/Size 值都用得到,所以我只打算给用到的值分配 Huffman 编码。
(虽是变长编码,由于 JPEG 限制码长不得超过 16bit ,分配过多会导致权重大的值码长增加)
解码测试结果:
疑问:
和 libjpeg 比,我这个只是一个小玩具。但是本着敢于质疑的精神,提出了这个问题,希望有懂 JPEG 的大神解惑,非常感谢!
折腾了一下午,问题解决,在此记录一下:
答案:
可以不给没用到的值分配编码。我这么做是对的。
所以libjpeg报错,是我另外的问题。
既然libjpeg报错了,我就查了libjpeg源码,看看哪里判错了。
“Bogus Huffman table definition”,是在jdhuff.c
第381行,
/* Figure C.2: generate the codes themselves */
/* We also validate that the counts represent a legal Huffman code tree. */
code = 0;
si = huffsize[0];
p = 0;
while (huffsize[p]) {
while (((int) huffsize[p]) == si) {
huffcode[p++] = code;
code++;
}
/* code is now 1 more than the last code used for codelength si; but
* it must still fit in si bits, since no code is allowed to be all ones.
*/
if (((INT32) code) >= (((INT32) 1) << si))
ERREXIT(cinfo, JERR_BAD_HUFF_TABLE);
code <<= 1;
si++;
}
然后我把报错的代码注释掉:
// if (((INT32) code) >= (((INT32) 1) << si))
// ERREXIT(cinfo, JERR_BAD_HUFF_TABLE);
竟然能解码了!没有任何问题。(事实证明,ffmpeg能够解码的原因,是因为ffmpeg没有做这个验证。)
那 libjpeg 为什么要拦住我呢。
再看了眼注释:no code is allowed to be all ones.
原来JPEG里面用的Huffman编码,不能全为1。
所以将全1的编码长度往后推一个就行。
问题解决。