计算内存中的 TIFF 图像大小 [C/C++]

Calculating TIFF image size in memory [C/C++]

如果我有一个指向 TIFF 数据的指针,但没有指示大小,有什么方法可以准确计算它?

我经历了几种不同的想法,所有这些想法大部分时间都有效,但并非总是如此,因为格式化 TIFF 的方法太多了,我认为必须有一种更简单的方法来做这个。现在,我得到的最接近的是:

ULONG readImageHeader(char* image)
{
TIF_HDR       *xTIFHdr;
TIF_IFD       *xTIFIFD;
TIF_IFD_ENTRY *pxTIFIFDEntry;
UCHAR         *pHdrPtr;
USHORT         i;
ULONG length  = 0;
ULONG imgLength = 0;
ULONG count = 0;

// check to see if it is a TIFF header
xTIFHdr = (TIF_HDR *)image;

// Little Endian
if (xTIFHdr->usTIFID == TIF_HEAD_LITTLE)
{
    pHdrPtr = (UCHAR*)image;
    pHdrPtr += xTIFHdr->ulFirstIFDOffset;

    // read TIF IFD
    xTIFIFD = (TIF_IFD *)pHdrPtr;

    // Look at all the IFD entries and set internal image hdr
    pHdrPtr += TIF_IFD_LEN;
    pxTIFIFDEntry = (TIF_IFD_ENTRY *)pHdrPtr;

    // iterate through each IFD entry
    for (i=0; i<xTIFIFD->usNumIFDEntries; i++)
    {
        if(length <= (ULONG)pxTIFIFDEntry->ulTIFValueOffset)
        {
            length = (ULONG)pxTIFIFDEntry->ulTIFValueOffset;

            // the TIF length is in units of the TIF type
            switch(pxTIFIFDEntry->usTIFType)
            {
            case TIF_BYTE:
                length += (ULONG)pxTIFIFDEntry->ulTIFLength * TIF_BYTE_SIZE;
                break;
            case TIF_ASCII:
                length += (ULONG)pxTIFIFDEntry->ulTIFLength * TIF_ASCII_SIZE;
                break;
            case TIF_SHORT:
                length += (ULONG)pxTIFIFDEntry->ulTIFLength * TIF_SHORT_SIZE;
                break;
            case TIF_LONG:
                length += (ULONG)pxTIFIFDEntry->ulTIFLength * TIF_LONG_SIZE;
                break;
            case TIF_RATIONAL:
                length += (ULONG)pxTIFIFDEntry->ulTIFLength * TIF_RATIONAL_SIZE;
                break;
            default:
                length += (ULONG)pxTIFIFDEntry->ulTIFLength;
                break;
            }
        }
        switch (pxTIFIFDEntry->usTIFTag)
        {
        case TIF_STRIP_BYTE_COUNTS:
        case TIF_STRIP_OFFSETS:
        {
            ULONG valueOffset = (ULONG)pxTIFIFDEntry->ulTIFValueOffset;
            count = (ULONG)pxTIFIFDEntry->ulTIFLength;

            // if the count > 1, then the valueOffset actually represents an offset
            if(count > 1)
            {
                ULONG countsize = (count - 1) * sizeof(ULONG);
                imgLength += *(ULONG*) ((UCHAR*)image + valueOffset + countsize);
            }
            else
            {
                // if count is 1, then the valueOffset is really just the value of that item
                imgLength += valueOffset;
            }
            break;
        }
        default:
            break;
        }
    pxTIFIFDEntry++;
    }

    // the length is the largest offset, plus the length of that item
    // the imgLength is the offset of the image, plus the size of the image, which is stored as two separate tags
    // return the largest of them
    return(length > imgLength ? length : imgLength);
}
// Big Endian
else if(xTIFHdr->usTIFID == TIF_HEAD_BIG)
{
    // I don't care about this
    printf("Big Endian TIFF image\n");
}

printf("Invalid TIFF image\n");
return(0);
}

基本上我在这里做的是遍历 TIFF header,并计算两个 运行 总和:(最大偏移量 + 数据长度)和(条带偏移量 + 条带字节)数数)。然后我只使用两个值中较大的一个。

除了有时 ulTIFValueOffset 根本不是偏移量,而是实际值外,这大部分都有效。在(某些)这些情况下,我得到的文件太大了。到目前为止,我所有失败的例子都是在抓取 Width 或 Length 标签时,尽管我不能排除其他标签可能有同样问题的可能性。

有没有

  1. 给定 headers 计算文件大小的方法?或
  2. 一种了解 header 是值还是偏移量的方法?

谢谢!

务实 导向的答案是,除非绝对必要,否则不要自己直接处理图像格式。使用图像库。对于 TIFF,有各种免费的(libre and/or 免费)图形文件库,包括 libTIFF, ImageMagick / GraphicMagick, DevIL, FreeImage 等。

TIFF image format 非常强大和灵活,但其代价是可以说是最复杂的图像格式,如 TIFF 6.0 规范中所述。此外,当前的实现还包含用于 JPEG 支持的 TIFF 技术说明 #2,以及 BigTIFF 草案。

I've gone through several different ideas, all of which work most of the time, but not always, since there's just so many different ways to format a TIFF

这就是我推荐使用图片库的原因。


If I have a pointer to TIFF data, but no indication of the size, is there any way to accurately calculate it?

如果您说 "the TIFF data" 是指 TIFF 图像本身,不,据我所知不是。如果不解析 TIFF 图像,则无法确定其文件大小(在磁盘上或内存中)。

A way to calculate the file size given the headers?

只使用8字节的图像文件头,然后没有。

通过解析 Image File Directory (IFD),您可以计算出该值。

A way to know if the headers are a value or an offset?

您应该能够确定 IFD(图像文件目录,TIFF 规范中的术语)条目的 ValueOffset 是值还是偏移量。它是值 当且仅当 它适合 4 个字节(ValueOffset 字段的大小)。 (参考:TIFF 6.0 specification: TIFF Structure - Value/Offset)

我将你的问题解释为 "all I have is a blind pointer to data which is allegedly a TIFF. Can I determine the size of the block of memory allocated to that pointer?"

至于仅根据 TIFF 数据确定块大小,有时答案是肯定的,但在一般情况下不会,而且肯定不安全。

TIFF IFD 结构构建为概念链表,任何 IFD 中的最后 4 个字节指向下一个 IFD 的偏移量或为 0。我有一组损坏的 TIFF 用于测试我的 TIFF 库,这表明有些编写代码来编写 TIFF 的人甚至无法正确完成这个简单的任务。我经常看到指向 space 某处的 IFD 偏移量或数据偏移量。如果您在不知道内存块限制的情况下编写内存中 IFD 遍历代码,那么当您遍历堆时遇到分段错误,您将是 幸运

TIFF 是一种具有欺骗性的文件格式。粗略看一下表明它很简单,但是有太多棘手的特殊情况,使用 TIFF 的代码需要处理这些情况 生产者搞砸了特殊情况的情况。

即使你编写了一个完整的消费者浏览所有的 IFD 和所有的偏移标签并试图找出数据中最远的那个,仍然不能保证数据没有被截断(我有几个这个条带的文件),也没有在最后一个 IFD 之后没有更多的垃圾数据(我有几个这样的文件)。

如果你决定编写代码来遍历文件(我不建议你这样做),你应该考虑一个抽象层来将数据读入结构而不是盲目转换,因为 TIFF 数据偏移量不必遵守任何特定的 word/long 单词对齐方式,这可能会让您感到悲伤。