libtiff: 10bitのTIFF画像の読み込み

8で割り切れないビット数のピクセル深度のファイルを読み込むのはなかなか大変です。
ここではこういったファイルの読み込みに対応しているImageMagicを参考にしてみました。

以下の例では、TIFFReadScanline()関数を使用しています。

サイズ計算

ビット数が8で割り切れないので、必要なバイト数は端数になりますが、
TIFFScanlineSize()関数は切り上げた数を返してくれるので、
このサイズのとおりにバッファを確保すれば大丈夫です。

ライン読み込み

端数ビットがあるので、ラインの最後の1バイトには次のラインの先頭ピクセルの一部があるように思われますが、
帰ってくるデータはきっちり1ライン分です。末尾の余分なビットは0で埋められています。
これはファイル自体がそのように保存されているのか(そのように決められているのか)、
libtiff内部でそのように調節しているのかはよくわかりません。

読み込みの実際

上記のとおり、TIFFReadScanline()関数はきっちり1ライン分のデータを返すので、
次のラインへ余ったビットデータを持ち越すことは考える必要がありません。
あとはラインごとにビット演算でピクセルに分解します。

以下の例はピクセル深度10bitのグレースケールTIFF画像を読み込む例です。
WORD型でピクセルを取り出して、raw形式で書き出しています。
ビット計算の部分はImageMagicを参考にしています。

#include <tiffio.h>
#include <windows.h>

int main( int argc, char *argv[] )
{
  int nImageLength, nImageWidth;
  int nBitsPerSample = 0;
  int nPhotometric;
  int i, j;
  WORD wPix;
  BYTE *p, r;
  int q;  // 読み込みバッファの1byteあたり残りビット数
  int b;  // 書き込み側オフセットのビット数
  int n;  // 書き込み側の残りビット数
  
  TIFF *pTif = TIFFOpen( argv[1], "r" );
  FILE *pfRaw = fopen( argv[2], "wb" );
  
  int nScanlineSize = TIFFScanlineSize( pTif );
  TIFFGetField( pTif, TIFFTAG_IMAGEWIDTH, &nImageWidth );
  TIFFGetField( pTif, TIFFTAG_IMAGELENGTH, &nImageLength );
  TIFFGetField( pTif, TIFFTAG_BITSPERSAMPLE, &nBitsPerSample );
  TIFFGetField( pTif, TIFFTAG_PHOTOMETRIC, &nPhotometric );
  
  LPBYTE lpszBuf = (LPBYTE)_TIFFmalloc( nScanlineSize );

  for ( i = 0; i < nImageLength; i++ )
  {
    TIFFReadScanline( pTif, lpszBuf, i );

    // バッファの先頭にセット
    p = lpszBuf;
    
    // 残りビット数を8にセット
    q = 8;
    r = *p++;
    for ( j = 0; j < nImageWidth; j++ )
    {
      wPix = 0;
      
      // 10bitならn=10
      n = nBitsPerSample;

      // 10bitを詰め終わるまでループ      
      while ( n > 0 )
      {
      
        // 読み込みバッファ1byteを使い切っていたら
        // 次の1byteに進む
        if ( q == 0 )
        {
          r = *p++;
          q = 8;
        }
        
        // ビットのオフセットを計算
        b = n;
        if ( b > q )
        {
          b = q;
        }
        
        // 残りビット数などを更新
        n -= b;
        q -= b;
        
        // ピクセル値を計算
        wPix = (wPix << b) | ((r >> q) &~ ((~0UL) << b));
        
      }
      
      fwrite( &wPix, sizeof(WORD), 1, pfRaw );
    }
  }
  
  _TIFFfree( lpszBuf );
  
  TIFFClose( pTif );
  fclose( pfRaw );
  
  return 0;
}
アーカイブ