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

时间:2015-05-26 14:57:53

标签: c++ c tiff

如果我有指向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标题,并计算两个运行总和:(最大偏移+数据长度)和(条带偏移+条带字节计数)。然后我只使用两个值中较大的一个。

除了有时ulTIFValueOffset根本不是偏移量,而是实际值以外,这种情况大多有效。在(某些)这些情况下,我得到的文件太大了。到目前为止,我所有失败的例子都是在抓取宽度或长度标签的时候,虽然我不能排除其他标签可能出现同样问题的可能性。

有吗

  1. 根据标题计算文件大小的方法?或
  2. 一种了解标题是值还是偏移量的方法?
  3. 谢谢!

2 个答案:

答案 0 :(得分:3)

面向实用主义的答案,除非你绝对必须,否则不要直接自己处理图像格式。使用图像库。对于TIFF,有各种免费( libre 和/或 gratis )图形文件库,包括libTIFFImageMagick / {{3} },GraphicMagickDevIL和其他人。

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

  

我经历了几个不同的想法,所有这些想法都是最有效的   时间,但并非总是如此,因为有很多不同的方式   格式化TIFF

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

  

如果我有指向TIFF数据的指针,但没有指示大小,   有没有办法准确计算它?

如果您使用“TIFF数据”,那么您的意思是TIFF图像本身,不,不是我所知道的。如果不对其进行解析,则无法确定TIFF图像的文件大小(在磁盘上或内存中)。

  

根据标题计算文件大小的方法?

只需使用8字节图像文件标题,然后使用no。

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

  

一种了解标题是值还是偏移量的方法?

您应该能够确定IFD(图像文件目录,TIFF规范中的术语)条目ValueOffset何时是值或偏移量。值当且仅当符合4字节(ValueOffset字段的大小)时。 (参考:TIFF image format - 价值/抵消)

答案 1 :(得分:2)

我正在解释你的问题是“我所拥有的只是指向数据的盲目指针,据称是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的人甚至无法正确完成这项简单的任务。我经常看到IFD偏移或数据偏移指向某个地方的空间。如果您在不知道内存块限制的情况下编写内存中的IFD遍历代码,那么当您在堆中进行遍历时,如果出现分段错误,则幸运

TIFF是一种欺骗性的文件格式。粗略的外观表明它很简单,但有很多棘手的特殊情况,代码消耗TIFF需要处理这些情况生产者拙劣的特殊情况。

即使你写了一个完整的消费者来撇去所有的IFD和所有的偏移标签,并试图找出数据中哪个最远,但仍然无法保证数据不会被截断(我有几个文件这个条纹)也没有在最后一个IFD之后没有更多的垃圾数据(我有几个这样的文件)。

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