libtiff: 浮動小数点ピクセルの読み込み/書き込み

あまり一般的には知られていないようですが、TIFFファイルはピクセルの値に
浮動小数を持つことができます。
もっとも、この形式をサポートするソフトは少なく、
PhotoshopやGIMPでも読み込めません。
読み込みが可能なのは、筆者の手元にあるソフトでは、QGIS、GRASS、OpenEV、Ossimでした。

書き込み

以下では、グレイスケールの浮動小数ピクセルの書き込みの例を示します。

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

BOOL WriteTIFF( LPCTSTR lpszFName, int nImageWidth, int nImageLength, float *pImg )
{
  int nRowsPerStrip;
  int nStripSize;
  int nRow;
  int nStrip = 0;
  int i, j;
  float *pData = pImg;
  WORD wRed[256], wGreen[256], wBlue[256];
  
  TIFF *pOut = TIFFOpen( lpszFName, "w" );
  if ( !pOut )
  {
    return FALSE;
  }
  
  if ( TIFFSetField( pOut, TIFFTAG_IMAGEWIDTH, nImageWidth ) != 1 )
    return false;      //横
  if ( TIFFSetField( pOut, TIFFTAG_IMAGELENGTH, nImageLength ) != 1 )
    return false;      //縦
  if ( TIFFSetField( pOut, TIFFTAG_COMPRESSION, COMPRESSION_NONE ) != 1 )
    return false;      //圧縮形式
  if ( TIFFSetField( pOut, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK ) != 1 )
    return false;      //種別
  if ( TIFFSetField( pOut, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(pOut, 0) ) != 1 )
    return false;      //ストリップあたり行数
  if ( TIFFSetField( pOut, TIFFTAG_SAMPLESPERPIXEL, 1 ) != 1 )
    return false;      //色数
  if ( TIFFSetField( pOut, TIFFTAG_BITSPERSAMPLE, 32 ) != 1 )
    return false;      //色深度
  if ( TIFFSetField( pOut, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG ) != 1 )
    return false;      //PLANARCONFIG
  if ( TIFFSetField( pOut, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT ) != 1 )
    return false;    //Orientation
  if ( TIFFSetField( pOut, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP ) != 1 )
    return false;
  if ( TIFFSetField( pOut, TIFFTAG_MINSAMPLEVALUE, 0 ) != 1 )
    return false;
  if ( TIFFSetField( pOut, TIFFTAG_MAXSAMPLEVALUE, 11 ) != 1 )
    return false;
  
  TIFFGetFieldDefaulted( pOut, TIFFTAG_ROWSPERSTRIP, &nRowsPerStrip );
  for ( i = 0; i < nImageLength; i += nRowsPerStrip )
  {
    nRow = ( i+nRowsPerStrip > nImageLength ) ? nImageLength - i : nRowsPerStrip;
    nStripSize = TIFFVStripSize( pOut, nRow );
    if ( TIFFWriteEncodedStrip( pOut, nStrip++, pData, nStripSize ) < 0 )
    {
      TIFFClose( pOut );
      return FALSE;
    }
    pData += nRowsPerStrip*nImageWidth;
  }
  
  TIFFClose( pOut );
}

太字部分がポイントです。
TIFFTAG_SAMPLEFORMATSAMPLEFORMAT_IEEEFPにすることで、float型を持つようになります。
もう1箇所BITSPERSAMPLEの指定も必須で、float型のビット数を指定します。
こうしないとTIFFTAG_SAMPLEFORMATSAMPLEFORMAT_IEEEFPを指定しても
正しい画像を作成できません。

あとは普通にfloat型の配列を書き込めます。

読み込み

これも特に難しいところは無く、libtiffが読み込んだデータを受け取るバッファをfloat型配列にすれば良いです。

#include <tiffio.h>
#include <stdlib.h>

int main( int argc, char *argv[] )
{
  int nImageLength, nImageWidth;
  int nBitsPerSample, nSamplePerPixel;
  int nSampleFormat;
  int nRowsPerStrip;
  int nPhotometric;
  int nRow;
  int nStrip = 0;
  float *pfPix;
  int i, j;
  
  TIFF *pTif = TIFFOpen( argv[1], "r" );
  
  TIFFGetField( pTif, TIFFTAG_IMAGEWIDTH, &nImageWidth );
  TIFFGetField( pTif, TIFFTAG_IMAGELENGTH, &nImageLength );
  TIFFGetField( pTif, TIFFTAG_BITSPERSAMPLE, &nBitsPerSample );
  TIFFGetField( pTif, TIFFTAG_SAMPLESPERPIXEL, &nSamplePerPixel );
  TIFFGetField( pTif, TIFFTAG_ROWSPERSTRIP, &nRowsPerStrip );
  TIFFGetField( pTif, TIFFTAG_PHOTOMETRIC, &nPhotometric );
  TIFFGetField( pTif, TIFFTAG_SAMPLEFORMAT, &nSampleFormat );
  
  if ( nSampleFormat == SAMPLEFORMAT_IEEEFP )
  {
    pfPix = new float[nImageWidth*nRowsPerStrip];
    for ( i = 0; i < nImageLength; i += nRowsPerStrip )
    {
      nRow = (i + nRowsPerStrip > nImageLength) ? nImageLength - i : nRowsPerStrip;
      nStrip = TIFFVStripSize( pTif, nRow );
      if ( TIFFReadEncodedStrip( pTif, TIFFComputeStrip
                                 ( pTif, i, 0 ), pfPix, nStrip ) == -1 )
      {
        fprintf( stderr, "ahokan" );
        delete[] pfPix;
        TIFFClose( pTif );
        exit( 1 );
      }
      
      //何か適当な処理
    }
    delete[] pfPix;
  }
  
  TIFFClose( pTif );
  
  return 0;
}

ほとんど用途はないかと思いますが、DEMデータの保存なんかに良いでしょう。
おそらくそういう用途を想定してGIS系ソフトではサポートされているのでしょう。

アーカイブ