在TIFF中创建一个带有缩略图的子IFD(libtiff)

9
我知道thumbnail.c包含了一些创建并将缩略图放置在子IDF中的代码,但是这段代码中有很多内容(生成缩略图、应用对比度曲线等),我很难仅复制缩略图部分。谷歌也没有提供任何帮助。
我的问题是,在我打开一个输出文件并且有TIFF*时,我已经准备好了缩略图数据(以及我的主图像数据),如何以这样的方式添加它们,使得缩略图在主图像IFD的子IFD中?
2个回答

9

在浏览了一段时间的libtiff源代码后,我在tif_dirwrite.c中发现了以下内容:

 /*
 * Copyright (c) 1988-1997 Sam Leffler
 * Copyright (c) 1991-1997 Silicon Graphics, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software and
 * its documentation for any purpose is hereby granted without fee, provided
 * that (i) the above copyright notices and this permission notice appear in
 * all copies of the software and related documentation, and (ii) the names of
 * Sam Leffler and Silicon Graphics may not be used in any advertising or
 * publicity relating to the software without the specific, prior written
 * permission of Sam Leffler and Silicon Graphics.
 */

...
if (!n)
    return(0);
/*
 * Total hack: if this directory includes a SubIFD
 * tag then force the next <n> directories to be
 * written as ``sub directories'' of this one.  This
 * is used to write things like thumbnails and
 * image masks that one wants to keep out of the
 * normal directory linkage access mechanism.
 */
tif->tif_flags|=TIFF_INSUBIFD;
tif->tif_nsubifd=tif->tif_dir.td_nsubifd;
if (tif->tif_dir.td_nsubifd==1)
    tif->tif_subifdoff=0;
else
    tif->tif_subifdoff=m;
return(1);
...

我在这里包含版权信息,因为我不确定在此处发布库中的代码时是否需要。

所以,回答自己的问题(如何在主图像IFD的子IFD中编写缩略图):

//...
//For the sake of this demo we will assume that I have opened a 
//TIFF (TIFF* created_TIFF) in write mode and have included the correct header
//files

//set all of your TIFF fields for the main image
//...

//Define the number of sub-IFDs you are going to write
//(assuming here that we are only writing one thumbnail for the image):
int number_of_sub_IFDs = 1;
toff_t sub_IFDs_offsets[1] = { 0UL };

//set the TIFFTAG_SUBIFD field:
if(!TIFFSetField(created_TIFF, TIFFTAG_SUBIFD, number_of_sub_IFDs, 
    sub_IFDs_offsets))
{
    //there was an error setting the field
}

//Write your main image raster data to the TIFF (using whatever means you need,
//such as TIFFWriteRawStrip, TIFFWriteEncodedStrip, TIFFWriteEncodedTile, etc.)
//...

//Write your main IFD like so:
TIFFWriteDirectory(created_TIFF);

//Now here is the trick: like the comment in the libtiff source states, the 
//next n directories written will be sub-IFDs of the main IFD (where n is 
//number_of_sub_IFDs specified when you set the TIFFTAG_SUBIFD field)

//Set up your sub-IFD
if(!TIFFSetField(created_TIFF, TIFFTAG_SUBFILETYPE, FILETYPE_REDUCEDIMAGE))
{
    //there was an error setting the field
}

//set the rest of the required tags here, as well as any extras you would like
//(remember, these refer to the thumbnail, not the main image)
//...

//Write this sub-IFD:
TIFFWriteDirectory(created_TIFF);

//Assuming you are only writing one sub-IFD and are done with the file, you 
//can close it now. If you specified more than one sub-IFD, you need repeat 
//the above code (starting where we set TIFFTAG_SUBFILETYPE) for each of your
//sub-IFDs
TIFFClose(created_TIFF);

我希望这能帮助到某些人,让他们不必像我一样花费很多精力来弄清楚如何做这件事。真的很遗憾libtiff文档记录得如此之差,特别是考虑到它的广泛使用。


这对我非常有效,为我节省了额外的时间。我想指出,您必须为每个子文件夹放入SUBFILETYPE,否则至少Adobe将无法识别它。 - soulsabr

0

我认为@KSletmoe指出了一个关键点,即在编写任何子IFD之前,应该在IFD0中添加SubIFD标签(330),但仍存在问题。

SubIFD标签的定义是“指向子IFD的偏移量”。因此,如果您没有正确设置偏移量,则tiff解析器无法正确解析整个tiff。

有两种处理这种情况的方法。

首先,预先计算每个IFD/SubIFD的大小,然后在设置时填入SubIFD标签,而不是设置为0x0。

或者,您可以像下面这样正常编写每个IFD,然后返回到IFD0并使用由libtiff计算的最终偏移量添加SubIFD标签。

//Write down every thing you need in a tiff
// ... IFD0
TIFFWriteDirectory(tif);
// ... IFD1
TIFFWriteDirectory(tif);
// ... IFD2
TIFFWriteDirectory(tif);

//set current Dir as IFD0 in the end
TIFFSetDirectory(tif, 0);  

//get next dir offset
sub_offset[1] = TIFFGetNextDirOff(tif, 2);
sub_offset[0] = TIFFGetNextDirOff(tif, 1);
//only for clean next dir offset in IFD0
no_use_offset = TIFFGetNextDirOff(tif, 0);                                                    

TIFFSetField(tif, TIFFTAG_SUBIFD, 2, sub_offset);    

你可能需要在 tif_dir.c 中添加以下函数

uint64 TIFFGetNextDirOff(TIFF* tif, uint16 dirn)                                                 
{                                                                                                
    uint64 nextdir;                                                                              
    uint16 n;                                                                                    
    if (!(tif->tif_flags&TIFF_BIGTIFF))                                                          
        nextdir = tif->tif_header.classic.tiff_diroff;                                           
    else                                                                                         
        nextdir = tif->tif_header.big.tiff_diroff;                                               
    for (n = dirn; n > 0 && nextdir != 0; n--)                                                   
        if (!TIFFAdvanceDirectory(tif, &nextdir, NULL))                                          
            return (0);

    /*!!Watchout!! Here will reset the next dir offset to 0*/
    tif->tif_nextdiroff = 0;                                                                                                                                     
    return nextdir;                                                                              
}

感谢 @KSletmoe。希望这可以帮助到某些人,并期待更加优雅的方式来实现。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接