【问题标题】:BitMiracle LibTIFF.NET Can't Decompress TIFF Previously Created By ItselfBitMiracle LibTIFF.NET 无法解压之前自己创建的 TIFF
【发布时间】:2015-11-02 09:17:17
【问题描述】:

我实现了一个类,它读取由Microsoft.Reporting.WinForms.ReportViewer 生成的每像素 24 位 TIFF,将其转换为每像素 1 位 TIFF 并将结果存储到文件中。

这部分工作正常 - 我可以在 TIFF 查看器中打开生成的 TIFF 并查看内容。

对于压缩,我使用以下编解码器:

    outImage.SetField(TiffTag.COMPRESSION, Compression.CCITT_T6);

现在我正在尝试读取相同的每像素 1 位 TIFF 并解压缩它。我写了以下方法:

public static void DecompressTiff(byte[] inputTiffBytes)
{
    using (var tiffStream = new MemoryStream(inputTiffBytes))
    using (var inImage = Tiff.ClientOpen("in-memory", "r", tiffStream, new TiffStream()))
    {
        if (inImage == null)
            return null;

        int totalPages = inImage.NumberOfDirectories();
        for (var i = 0; i < totalPages; )
        {
            if (!inImage.SetDirectory((short) i))
                return null;

            var decompressedTiff = DecompressTiff(inImage);
...
}

private static byte[] DecompressTiff(Tiff image)
{
    // Read in the possibly multiple strips 
    var stripSize = image.StripSize();
    var stripMax = image.NumberOfStrips();
    var imageOffset = 0;
    int row = 0;

    var bufferSize = image.NumberOfStrips() * stripSize;
    var buffer = new byte[bufferSize];

    int height = 0;
    var result = image.GetField(TiffTag.IMAGELENGTH);
    if (result != null)
        height = result[0].ToInt();

    int rowsperstrip = 0;
    result = image.GetField(TiffTag.ROWSPERSTRIP);
    if (result != null)
        rowsperstrip = result[0].ToInt();
    if (rowsperstrip > height && rowsperstrip != -1)
        rowsperstrip = height;

    for (var stripCount = 0; stripCount < stripMax; stripCount++)
    {
        int countToRead = (row + rowsperstrip > height) ? image.VStripSize(height - row) : stripSize;
        var readBytesCount = image.ReadEncodedStrip(stripCount, buffer, imageOffset, countToRead); // Returns -1 for the last strip of the very first page
        if (readBytesCount == -1)
            return null;

        imageOffset += readBytesCount;
        row += rowsperstrip;
    }

    return buffer;
}

问题是当ReadEncodedStrip() 被调用为第一页的最后一条时-它返回-1,表明有错误。即使在调试了 LibTIFF.NET 解码器代码之后,我也无法弄清楚出了什么问题。这是在预期之外的地方发现了 EOL TIFF 标记的东西。

由于某种原因,LibTIFF.NET 无法读取自己生成的 TIFF,或者很可能我遗漏了一些东西。 Here 是 TIFF 的问题。

有人可以帮忙找出根本原因吗?

【问题讨论】:

    标签: c# .net image image-processing tiff


    【解决方案1】:

    经过半天多的调查,我终于找到了这个奇怪问题的原因。

    为了将每像素 24 位 TIFF 转换为每像素 1 位,我将原始 libtiff 随附的 2 个工具的算法从 C 移植到 C#:tiff2bwtiffdither

    tiffdither 的错误是它不包含输出图像中的最后一个图像行,即如果您向其提供一个高度为 2200 行的图像,您将获得高度为 2199 的图像作为输出。

    我在移植的一开始就注意到了这个错误并尝试修复,但最终结果证明并不完全,并且移植的算法实际上并没有通过WriteScanline() 方法将最后一行写入输出 TIFF。所以这就是 LibTIFF.NET 无法读取图像的最后一条\行的原因,具体取决于我使用的读取方法。

    令我惊讶的是,LibTIFF.NET 允许编写这种实际损坏的 TIFF,而不会在编写过程中出现任何错误。例如WriteDirectory() 方法在这种情况下返回true,当通过TiffTag.IMAGELENGTH 设置的图像高度与写入它的实际行数不同时。但是,后来它无法读取这样的图像,并且在读取时抛出错误。

    也许这种行为是从原始 libtiff 继承而来的。

    【讨论】:

    • 您的代码还有另一个问题。条带数 x strips_per_row 不等于图像长度(常见情况)。每个条带的条带大小(以字节为单位)也不同。在您的情况下,每个条带有 38 行和 58 个条带。最后一个条带没有 38 行,但您使用 58x38 (2204)。
    • @BitBank,我认为“条带大小(以字节为单位)也因每个条带而异”是指最后一条条带可能小于 StripSize() 返回的值,以防rows 不被 rows_per_strips 平均划分?正是在这种情况下,有一行: int countToRead = (row + rowsperstrip > height) ? image.VStripSize(height - row) : stripSize;这将返回少于整个条带大小(8094 字节)的字节数(在我的情况下,最后一个条带为 7242 字节)。还是我错过了什么?
    • @BitBank,关于您的第一个注释“条带 x strips_per_row 的数量不等于图像长度(常见情况)” - 我意识到缓冲区 var bufferSize = image.NumberOfStrips() * stripSize; 可能大于实际解码后的图像长度(以字节为单位)。为了简单起见,我把它留在这里。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多