libtiff: タイル・ピラミッドファイルの書き込み

ピラミッド画像は、ピクセル数と一部のタグを除いてはベースの画像と変わらないが、ピラミッドのディレクトリにアクセスする関数はそのつど適切なものを選択する必要があります。

ディレクトリにアクセスする関数はTIFFSetDirectory()TIFFSetSubDirectory()で、
前者がディレクトリ番号を指定してアクセスするのに対し、後者はオフセットのバイト数を元にアクセスします。

以下の方法では、まずベースのタイル画像を書き込み、それ以降のピラミッド画像はベースの画像を元に作成します。

なお、読み込みは簡単で、TIFFSetDirectory()で目的のピラミッドにセットしたあと、普通に読み込めます。

BOOL GeneratePyramid
(
    LPCTSTR szFName,
    LPCTSTR szOutName,
    UINT uiImageWidth,
    UINT uiImageLength,
    BYTE btSample,
)
{
    //入力画像 => タイル^512ファイル作成
    TIFF *pIn, *pOut;
    TCHAR szErr[30];
    LPBYTE lpBuf, *lpTile;
    UINT i, j, k, nRow, nTile, m, n, nTileX, nTileY, nDir;
    UINT nCurSizeRow, nCurSizeCol;
    int nBaseDirOff, nNextDirOff;

    TIFFSetWarningHandler( NULL );

    pIn = TIFFOpen( szFName, "r" );
    if ( !pIn )
    {
        sprintf( szErr, "TIFFファイルを開けません[%s]", szFName );
        ::MessageBox( NULL, szErr, "エラー", MB_OK );
        return FALSE;
    }
    pOut = TIFFOpen( szOutName, "r+" );
    if ( !pOut )
    {
        sprintf( szErr, "出力ファイルを作成できません[%s]", szOutName );
        ::MessageBox( NULL, szErr, "エラー", MB_OK );
        return FALSE;
    }

    if ( !GetTiffInfo( pIn ) )
    {
        sprintf( szErr, "TIFFファイルに問題があります[%s]", szFName );
        ::MessageBox( NULL, szErr, "エラー", MB_OK );
        return FALSE;
    }

    lpBuf = new BYTE[uiImageWidth*btSample];
    nTile = (int)floor( uiImageWidth / TILE_SIZE ) + 1;
    lpTile = new BYTE*[nTile];
    for ( i = 0; i < nTile; i++ )
    {
        lpTile[i] = new BYTE[TILE_SIZE*TILE_SIZE*btSample];
        memset( lpTile[i], 0xFF, TILE_SIZE*TILE_SIZE*btSample );
    }

    nCurSizeRow = (uiImageLength/TILE_SIZE + 1)*TILE_SIZE;
    nCurSizeCol = nTile*TILE_SIZE;
    SetTIFFInfo( pOut, uiImageLength, uiImageWidth );

    nRow = 0;

    for ( i = 0; i < uiImageLength; i++ )
    {
        TIFFReadScanline( pIn, lpBuf, i );

        //1ラインを割り振る
        for ( j = 0; j < uiImageWidth; j++ )
        {
            n = j/TILE_SIZE;
            for ( k = 0; k < btSample; k++ )
            {
                lpTile[n][nRow*TILE_SIZE*btSample + (j-n*TILE_SIZE)*btSample + k] =
                                                                    lpBuf[j*btSample+k];
            }
        }
        if ( nRow == TILE_SIZE-1 || i == uiImageLength-1 )
        {
            for ( j = 0; j < nTile; j++ )
            {
                if ( 
                    TIFFWriteEncodedTile(pOut, TIFFComputeTile(pOut, j*TILE_SIZE,
                    (i/TILE_SIZE)*TILE_SIZE, 
                    0, 0), lpTile[j], TILE_SIZE*TILE_SIZE*btSample) < 0 )
                {
                    delete lpBuf;
                    for ( i = 0; i < nTile; i++ ) delete lpTile[i];
                    delete lpTile;
                    TIFFClose( pIn );
                    TIFFClose( pOut );
                    ::MessageBox( NULL, "書き込みに失敗しました", "エラー", MB_OK );
                    return FALSE;
                }
            }
            nRow = 0;
            for ( j = 0; j <  nTile; j++ )
                memset( lpTile[j], 0xFF, TILE_SIZE*TILE_SIZE*btSample );
            continue;
        }
        nRow++;
    }

    delete[] lpBuf;
    for ( i = 0; i < nTile; i++ ) delete[] lpTile[i];
    delete[] lpTile;
    TIFFClose( pIn );
    TIFFClose( pOut );

    m_uiCount = 0;

    //ベースのタイルからピラミッド作成
    pOut = TIFFOpen( szOutName, "r+" );
    if ( !pOut )
    {
        sprintf( szErr, "出力ファイルを作成できません[%s]", szOutName );
        ::MessageBox( NULL, szErr, "エラー", MB_OK );
        return FALSE;
    }

    lpBuf = new BYTE[TILE_SIZE*TILE_SIZE*btSample];
    lpTile = new BYTE*[4];
    for ( i = 0; i < 4; i++ ) lpTile[i] = new BYTE[TILE_SIZE*TILE_SIZE*btSample];
    //ベースディレクトリのオフセットを保持しておく
    nBaseDirOff = TIFFCurrentDirOffset( pOut );
    nDir = 2;
    while ( nCurSizeRow > TILE_SIZE*2 && nCurSizeCol > TILE_SIZE*2 )
    {
        //サブディレクトリを作成
        TIFFCreateDirectory( pOut );
        SetTIFFInfo( pOut, uiImageLength/nDir, uiImageWidth/nDir );
        //サブイメージにのみ必要なタグ
        TIFFSetField( pOut, TIFFTAG_SUBFILETYPE, FILETYPE_REDUCEDIMAGE );
        TIFFWriteCheck( pOut, TRUE, "TIFFBuildOverviews" );
        TIFFWriteDirectory( pOut );
        //作成したサブディレクトリの先頭にセット
        TIFFSetDirectory( pOut, TIFFNumberOfDirectories(pOut)-1 );
        //作成したサブディレクトリのオフセットを保持、
        //以後、nBaseDirOffとnNextDirOffでベースとサブを行き来する
        nNextDirOff = TIFFCurrentDirOffset( pOut );
        TIFFSetSubDirectory( pOut, nBaseDirOff );
        for ( i = 0; i < nCurSizeRow; i += TILE_SIZE*2 )
        {
            for ( j = 0; j < nCurSizeCol; j += TILE_SIZE*2 )
            {

                for ( m = 0; m < 4; m++ )
                    memset( lpTile[m], 0xFF, TILE_SIZE*TILE_SIZE*btSample );
                TIFFReadTile( pOut, lpTile[0], j, i, 0, 0 );
                if ( j+TILE_SIZE < nCurSizeCol )
                    TIFFReadTile( pOut, lpTile[1], j+TILE_SIZE, i, 0, 0 );
                if ( i+TILE_SIZE < nCurSizeRow )
                    TIFFReadTile( pOut, lpTile[2], j, i+TILE_SIZE, 0, 0 );
                if ( ( j+TILE_SIZE < nCurSizeCol ) && ( i+TILE_SIZE < nCurSizeRow ) )
                    TIFFReadTile( pOut, lpTile[3], j+TILE_SIZE, i+TILE_SIZE, 0, 0 );

                for ( m = 0; m < TILE_SIZE*2; m += 2 )
                {
                    for ( n = 0; n < TILE_SIZE*2; n += 2 )
                    {
                        nTileX = n/TILE_SIZE;
                        nTileY = m/TILE_SIZE;
                        for ( k = 0; k < btSample; k++ )
                        {
                            lpBuf[(m/2)*TILE_SIZE*btSample+(n/2)*btSample+k] = 
                                (
lpTile[nTileY*2+nTileX]
[((m+0)-nTileY*TILE_SIZE)*TILE_SIZE*btSample + ((n+0)-nTileX)*btSample + k] +
lpTile[nTileY*2+nTileX]
[((m+0)-nTileY*TILE_SIZE)*TILE_SIZE*btSample + ((n+1)-nTileX)*btSample + k] +
lpTile[nTileY*2+nTileX]
[((m+1)-nTileY*TILE_SIZE)*TILE_SIZE*btSample + ((n+0)-nTileX)*btSample + k] +
lpTile[nTileY*2+nTileX]
[((m+1)-nTileY*TILE_SIZE)*TILE_SIZE*btSample + ((n+1)-nTileX)*btSample + k] ) / 4;
                        }
                    }
                }
                TIFFSetSubDirectory( pOut, nNextDirOff );

                if ( TIFFWriteEncodedTile(pOut, TIFFComputeTile(pOut, j/2, i/2, 0, 0),
				 lpBuf, TILE_SIZE*TILE_SIZE*btSample) < 0 )
                {
                    for ( i = 0; i < 4; i++ ) delete lpTile[i];
                    delete lpTile;
                    delete lpBuf;
                    return FALSE;
                }
                TIFFFlush( pOut );
                TIFFSetSubDirectory( pOut, nBaseDirOff );
            }
        }
        nDir *= 2;
        nCurSizeRow = (UINT)floor((float)nCurSizeRow/TILE_SIZE/2+0.5)*TILE_SIZE;
        nCurSizeCol = (UINT)floor((float)nCurSizeCol/TILE_SIZE/2+0.5)*TILE_SIZE;
        TIFFFlush( pOut );
        nBaseDirOff = nNextDirOff;
        TIFFSetSubDirectory( pOut, nBaseDirOff );
    }

    for ( i = 0; i < 4; i++ ) delete[] lpTile[i];
    delete[] lpTile;
    delete[] lpBuf;

    TIFFFlush( pOut );
    TIFFClose( pOut );
    
    return TRUE;
}
アーカイブ