C 解码字典中尚未出现的TIFF LZW代码
我制作了一个LZW压缩TIFF图像的解码器,所有的部分都工作了,它可以在不同的比特深度下解码大图像,有或没有水平预测,只有一种情况除外。虽然它可以解码大多数程序编写的文件(如Photoshop和Krita,具有各种编码选项),但ImageMagick的C 解码字典中尚未出现的TIFF LZW代码,c,compression,tiff,lzw,C,Compression,Tiff,Lzw,我制作了一个LZW压缩TIFF图像的解码器,所有的部分都工作了,它可以在不同的比特深度下解码大图像,有或没有水平预测,只有一种情况除外。虽然它可以解码大多数程序编写的文件(如Photoshop和Krita,具有各种编码选项),但ImageMagick的转换创建的文件有一点非常奇怪,它生成的LZW代码还没有出现在字典中,我不知道如何处理它 在大多数情况下,LZW流中的9到12位代码还没有在字典中,这是我的解码算法将尝试放入字典的下一个代码(虽然我的算法在包含这种情况的图像上失败,但我不确定这是否应
转换
创建的文件有一点非常奇怪,它生成的LZW代码还没有出现在字典中,我不知道如何处理它
在大多数情况下,LZW流中的9到12位代码还没有在字典中,这是我的解码算法将尝试放入字典的下一个代码(虽然我的算法在包含这种情况的图像上失败,但我不确定这是否应该是一个问题),但有时它甚至可能是未来的数百个代码。在一种情况下,清除代码(256)后的第一个代码是364,这似乎是不可能的,因为清除代码清除了我的字典中的所有代码258及以上,在另一种情况下,当我的字典只升到317时,代码是501
我不知道如何处理它,但似乎只有我一个人有这个问题,其他程序中的解码器加载这样的图像很好。那么他们是如何做到的呢
这是我的解码算法的核心,显然由于涉及的代码太多,我无法以紧凑的方式提供完整的可编译代码,但由于这是一个算法逻辑的问题,这应该足够了。它严格遵循官方(第61页)中描述的算法,事实上大部分规范的伪代码都在注释中
void tiff_lzw_decode(uint8_t *coded, buffer_t *dec)
{
buffer_t word={0}, outstring={0};
size_t coded_pos; // position in bits
int i, new_index, code, maxcode, bpc;
buffer_t *dict={0};
size_t dict_as=0;
bpc = 9; // starts with 9 bits per code, increases later
tiff_lzw_calc_maxcode(bpc, &maxcode);
new_index = 258; // index at which new dict entries begin
coded_pos = 0; // bit position
lzw_dict_init(&dict, &dict_as);
while ((code = get_bits_in_stream(coded, coded_pos, bpc)) != 257) // while ((Code = GetNextCode()) != EoiCode)
{
coded_pos += bpc;
if (code >= new_index)
printf("Out of range code %d (new_index %d)\n", code, new_index);
if (code == 256) // if (Code == ClearCode)
{
lzw_dict_init(&dict, &dict_as); // InitializeTable();
bpc = 9;
tiff_lzw_calc_maxcode(bpc, &maxcode);
new_index = 258;
code = get_bits_in_stream(coded, coded_pos, bpc); // Code = GetNextCode();
coded_pos += bpc;
if (code == 257) // if (Code == EoiCode)
break;
append_buf(dec, &dict[code]); // WriteString(StringFromCode(Code));
clear_buf(&word);
append_buf(&word, &dict[code]); // OldCode = Code;
}
else if (code < 4096)
{
if (dict[code].len) // if (IsInTable(Code))
{
append_buf(dec, &dict[code]); // WriteString(StringFromCode(Code));
lzw_add_to_dict(&dict, &dict_as, new_index, 0, word.buf, word.len, &bpc);
lzw_add_to_dict(&dict, &dict_as, new_index, 1, dict[code].buf, 1, &bpc); // AddStringToTable
new_index++;
tiff_lzw_calc_bpc(new_index, &bpc, &maxcode);
clear_buf(&word);
append_buf(&word, &dict[code]); // OldCode = Code;
}
else
{
clear_buf(&outstring);
append_buf(&outstring, &word);
bufwrite(&outstring, word.buf, 1); // OutString = StringFromCode(OldCode) + FirstChar(StringFromCode(OldCode));
append_buf(dec, &outstring); // WriteString(OutString);
lzw_add_to_dict(&dict, &dict_as, new_index, 0, outstring.buf, outstring.len, &bpc); // AddStringToTable
new_index++;
tiff_lzw_calc_bpc(new_index, &bpc, &maxcode);
clear_buf(&word);
append_buf(&word, &dict[code]); // OldCode = Code;
}
}
}
free_buf(&word);
free_buf(&outstring);
for (i=0; i < dict_as; i++)
free_buf(&dict[i]);
free(dict);
}
如果有人坚持要潜入我的代码(这应该是不必要的,而且它是一个很大的库)。伪造的代码来自于试图解码超出我们预期的代码。问题是,LZW条有时可能不会以信息257代码的结尾结束,因此当输出一定数量的解码字节时,解码循环必须停止。每个条带的字节数由TIFF标记ROWSPERSTRIP*IMAGEWITH*BITSPERSAMPLE/8确定,如果PLANARCONFIG为1(表示交错通道,而非平面通道),则将其全部乘以SAMPLESPERPIXEL。因此,除了在遇到代码257时停止解码循环外,还必须在达到解码字节数后停止循环。伪代码来自于试图解码超出我们预期的部分。问题是,LZW条有时可能不会以信息257代码的结尾结束,因此当输出一定数量的解码字节时,解码循环必须停止。每个条带的字节数由TIFF标记ROWSPERSTRIP*IMAGEWITH*BITSPERSAMPLE/8确定,如果PLANARCONFIG为1(表示交错通道,而非平面通道),则将其全部乘以SAMPLESPERPIXEL。因此,除了在遇到代码257时停止解码循环外,还必须在达到解码字节数后停止循环。这可能是一个愚蠢的问题,但我猜您已经阅读了libtiff是如何做到这一点的?这似乎是对的:@cgohlke我试图在问题中明确表示,跟踪代码大小不可能是一个问题。很明显,这是一个理论问题,ImageMagick显然认为有可能有如此高的代码,而且大多数解码器似乎都知道如何很好地处理它。@jcupitt我知道,尽管我尽量避免试图根据不追求清晰的代码得出结论。但是,至少在遵循一个清晰代码LIbTIFF的情况下,会考虑下面的代码255以上是腐败的,这也是我的代码所做的,但这无济于事。@ MichelRouzic,我尝试了您的示例文件与我的库(使用LIPTIFF为TIFF加载),它对我来说很好。ImageMagick也使用libtiff进行TIFF写入,所以我认为其中一定有秘密。我会在调试器中尝试
转换奇怪的.tif x.png
,然后观察LZWDecode
(或任何最终被调用的内容)的执行情况。事后看来,这应该是显而易见的,但我想了想,想不出如何避免阅读过多(我想的是如何限制输入,我没有想到计算输出大小),所以我没有这么做。但是,该规范的伪代码非常明确,您可能会认为它包含了这样一个考虑因素,但是很好。至少我现在明白了。这可能是一个愚蠢的问题,但我想你已经读过libtiff是如何做到这一点的?这似乎是对的:@cgohlke我试图在问题中明确表示,跟踪代码大小不可能是一个问题。很明显,这是一个理论问题,ImageMagick显然认为有可能有如此高的代码,而且大多数解码器似乎都知道如何很好地处理它。@jcupitt我知道,尽管我尽量避免试图根据不追求清晰的代码得出结论。但是,至少在遵循一个清晰代码LIbTIFF的情况下,会考虑下面的代码255以上是腐败的,这也是我的代码所做的,但这无济于事。@ MichelRouzic,我尝试了您的示例文件与我的库(使用LIPTIFF为TIFF加载),它对我来说很好。ImageMagick也使用libtiff进行TIFF写入,所以我认为其中一定有秘密。我会在调试器中尝试转换奇怪的.tif x.png
,然后观察LZWDecode
(或任何最终被调用的内容)的执行情况。事后看来,这应该是显而易见的,但我想了想,想不出如何避免阅读过多(我想的是如何限制输入,我没有想到计算输出大小),所以我没有这么做。但是,该规范的伪代码非常明确,您可能会认为它包含了这样一个考虑因素,但是很好。至少我现在明白了。
#include "loadtiff.h"
#include "loadtiff.c"
void loadtiff_test(char *path)
{
int width, height, format;
floadtiff(fopen(path, "rb"), &width, &height, &format);
}