libtiff: セパレートタイプのTIFF読み込み

通常のカラー画像フォーマットはRGBRGB…というようにデータが並んでいますが、
セパレートタイプのTIFFとは、Rが1行、Gが1行、Bが1行というように並んでいるものです。
リモセン用語で言うと、通常のカラー画像はBIP、セパレートタイプはBILということになります。

このフォーマットの場合、libtiffでの挙動は通常のカラー画像と以下のような違いがあります。

  • TIFFScanlineSize()関数は通常の画像は画像の横幅*色数の値を帰すが、
    セパレートの場合は画像の横幅の値
  • TIFFReadScanline()関数は1色分のデータを読み込む
  • TIFFRasterScanlineSize()関数ならば同じ値を返す

見るからにめんどくさそうなフォーマットですが、
TIFFReadScanline()関数に便利な4番目の引数というのがあります。
これはセパレートタイプの場合にのみ有効な引数で、読み込む色(サンプル)を指定するというものです。
これを使って、下記のサンプルコードではセパレートファイルから通常のフォーマットに書き出しています。

ちなみに、TIFFベースラインの仕様では、セパレートタイプのフォーマットは非推奨であり、
ソフトウェアはセパレートタイプのTIFFファイルを読み込む義務は無いそうです。
セパレートだ、とわかったらはじいてもいいということだそうです。

さらにちなみに、フリーのリモセンソフトのMultiSpecで、バンド別の画像を合成してTIFFファイルを出力すると
セパレートタイプとして出力されます。

以下のサンプルコードは、そのMultiSpecで合成されたセパレート8バンドのTIFFファイルから、
指定したバンド3つを合成してカラーの通常フォーマットのTIFFファイルに書き出すサンプルです。

#include "../../#libtiff/tiffio.h"

int main( int argc, char *argv[] )
{
    int i, j;
    int nImageWidth, nImageLength, nSample = 0, nBits = 0, nRows = 0, nPlan = 0;
    unsigned char *pbIn_R, *pbIn_G, *pbIn_B, *pbOut;

    TIFF *pTif = TIFFOpen( argv[1], "r" );
    if ( !pTif )
    {
        fprintf( stderr, "Unable to open TIFF file [%s]n", argv[1] );
        exit( 1 );
    }

    TIFF *pOut = TIFFOpen( argv[2], "w" );
    if ( !pOut )
    {
        fprintf( stderr, "Unable to create TIFF file [%s]n", argv[2] );
        TIFFClose( pTif );
        exit( 1 );
    }

    int nR = atoi( argv[3] );
    int nG = atoi( argv[4] );
    int nB = atoi( argv[5] );

    TIFFGetField( pTif, TIFFTAG_IMAGEWIDTH, &nImageWidth );
    TIFFGetField( pTif, TIFFTAG_IMAGELENGTH, &nImageLength );
    TIFFGetField( pTif, TIFFTAG_SAMPLESPERPIXEL, &nSample );
    TIFFGetField( pTif, TIFFTAG_BITSPERSAMPLE, &nBits );
    TIFFGetField( pTif, TIFFTAG_ROWSPERSTRIP, &nRows );
    TIFFGetField( pTif, TIFFTAG_PLANARCONFIG, &nPlan );    // nPlan==2ならセパレート

    if ( nBits != 8 )
    {
        fprintf( stderr, "File is no 8bitn" );
        TIFFClose( pTif );
        TIFFClose( pOut );
        exit( 1 );
    }

    TIFFSetField( pOut, TIFFTAG_IMAGEWIDTH, nImageWidth );
    TIFFSetField( pOut, TIFFTAG_IMAGELENGTH, nImageLength );
    TIFFSetField( pOut, TIFFTAG_SAMPLESPERPIXEL, 3 );
    TIFFSetField( pOut, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB );
    TIFFSetField( pOut, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG );
    TIFFSetField( pOut, TIFFTAG_BITSPERSAMPLE, nBits );

    pbIn_R = (unsigned char *)_TIFFmalloc( TIFFScanlineSize( pTif ) );
    pbIn_G = (unsigned char *)_TIFFmalloc( TIFFScanlineSize( pTif ) );
    pbIn_B = (unsigned char *)_TIFFmalloc( TIFFScanlineSize( pTif ) );
    pbOut = (unsigned char *)_TIFFmalloc( TIFFScanlineSize( pTif ) * 3 );

    for ( i = 0; i < nImageLength; i++ )
    {
        TIFFReadScanline( pTif, pbIn_R, i, nR );
        TIFFReadScanline( pTif, pbIn_G, i, nG );
        TIFFReadScanline( pTif, pbIn_B, i, nB );
        for ( j = 0; j < nImageWidth; j++ )
        {
            pbOut[j*3+0] = pbIn_R[j];
            pbOut[j*3+1] = pbIn_G[j];
            pbOut[j*3+2] = pbIn_B[j];
        }
        TIFFWriteScanline( pOut, pbOut, i );
    }

    _TIFFfree( pbIn_R );
    _TIFFfree( pbIn_G );
    _TIFFfree( pbIn_B );
    _TIFFfree( pbOut );

    TIFFClose( pTif );
    TIFFClose( pOut );

    return 0;
}

参考文献

アーカイブ