libTIFF 圖像讀取與保存



本系列文章由 @YhL_Leo 出品,轉載請注明出處。
文章鏈接: http://blog.csdn.net/YhL_Leo/article/details/49848391


1 頭文件

libtiff定義一系列C語言類型的數據結構,調用時包含的頭文件為:

#include "tiffio.h"

2 文件讀寫

/* read from an existing TIFF image */
void main()
{
    TIFF* tif = TIFFOpen("foo.tif", "r");
    ... do stuff ...
    TIFFClose(tif);  // or TIFFFlush(tif);
}

/* create or overwrite a TIFF image */
void main()
{
    TIFF* tif = TIFFOpen("foo.tif", "w");
    ... do stuff ...
    TIFFClose(tif);  // or TIFFFlush(tif);
}

不同於stdio library對TIFF文件的操作可以同時支持讀和寫,libtiff對於TIFF文件的操作模式是不可變更的,也就是說對一個指定的TIFF文件,一次只能支持對文件的讀或寫中的一種操作。

3 多目錄文件讀寫

TIFF格式支持將多個圖像文件存儲為一個文件的功能,每個圖片都有一個對應的數據結構稱為一個目錄,其中包括全部的信息格式和圖像數據內容。圖像之間可以是相關的也可以使不相關的。

#include "tiffio.h"
int main(int argc, char* argv[])
{
    TIFF* tif = TIFFOpen(argv[1], "r");
    if (tif) 
    {
        int dircount = 0;
        do {
            dircount++;
        } while (TIFFReadDirectory(tif));

        printf("%d directories in %s\n", dircount, argv[1]);
        TIFFClose(tif);
    }
    return 0;
}

// write: TIFFWriteDirectory()

4 標簽讀取與設置

圖像相關的信息例如寬、高、通道數、定向信息、顏色信息等。libtiff中提供了獲取和設置標簽值的函數:TIFFGetFieldTIFFSetField

/* read the tags */
uint32 width, height;
uint16 ncn;

TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);    // image width in pixels
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);  // image height in pixels
TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &ncn); // samples per pixel -> channels

cout << width << " " << height << " " << ncn << endl; 

/* set the tags */ 
TIFFSetField( imageWrite, TIFFTAG_IMAGEWIDTH, width );
TIFFSetField( imageWrite, TIFFTAG_IMAGELENGTH, height );
TIFFSetField( imageWrite, TIFFTAG_BITSPERSAMPLE, 8);    // 8 bits per channel
TIFFSetField( imageWrite, TIFFTAG_SAMPLESPERPIXEL, 4);  // 4 channels

下面列出幾種常用的TIFF圖像信息標簽:

#define TIFFTAG_IMAGEWIDTH 256 /* image width in pixels */
#define TIFFTAG_IMAGELENGTH 257 /* image height in pixels */
#define TIFFTAG_BITSPERSAMPLE 258 /* bits per channel (sample) */
#define TIFFTAG_SAMPLESPERPIXEL 277 /* samples per pixel */
#define TIFFTAG_COMPRESSION 259 /* data compression technique */
#define TIFFTAG_PHOTOMETRIC 262 /* photometric interpretation */
#define TIFFTAG_PLANARCONFIG 284 /* storage organization */
#define TIFFTAG_XRESOLUTION 282 /* pixels/resolution in x */
#define TIFFTAG_YRESOLUTION 283 /* pixels/resolution in y */
#define TIFFTAG_RESOLUTIONUNIT 296 /* units of resolutions */

5 RGBA 圖像讀取與存儲

對於4通道的圖像,libtiff提供的數據顏色順序為A B G R,並且整合為32-bit無符號整型數據(每個通道為8 bits),數據讀取方法為使用TIFFReadRGBAImage函數:

#include "tiffio.h"

// first method: TIFFReadRGBAImage
int main(int argc, char* argv[])
{
    TIFF* tif = TIFFOpen(argv[1], "r");
    if (tif) {
    uint32 w, h;
    size_t npixels;
    uint32* raster;

    TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
    TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);
    npixels = w * h;
    raster = (uint32*) _TIFFmalloc(npixels * sizeof (uint32));
    if (raster != NULL) {
        if (TIFFReadRGBAImage(tif, w, h, raster, 0)) {
        ...process raster data...
        }
        _TIFFfree(raster);
    }
    TIFFClose(tif);
    }
    return 0;
}

// second method: TIFFRGBAImageBegin & TIFFRGBAImageGet
int main(int argc, char* argv[])
{
    TIFF* tif = TIFFOpen(argv[1], "r");
    if (tif) {
    TIFFRGBAImage img;
    char emsg[1024];

    if (TIFFRGBAImageBegin(&img, tif, 0, emsg)) {
        size_t npixels;
        uint32* raster;

        npixels = img.width * img.height;
        raster = (uint32*) _TIFFmalloc(npixels * sizeof (uint32));
        if (raster != NULL) {
        if (TIFFRGBAImageGet(&img, raster, img.width, img.height)) {
            ...process raster data...
        }
        _TIFFfree(raster);
        }
        TIFFRGBAImageEnd(&img);
    } else
        TIFFError(argv[1], emsg);
    TIFFClose(tif);
    }
    return 0;
}

TIFFReadRGBAImage為例,讀取圖像后,獲得其某一通道的結果,可使用:

// image channel read order : A B G R
if ( TIFFReadRGBAImage(tif, width, height, raster, 0) )
{
    BYTE *imageR = new BYTE[nPixels];
    // image pixels are in an inverted order, which is same as bmp format
    uint32* rowPoint2Src = raster + (height-1)*width;
    BYTE *rowPointerToR  = imageR;

    for ( int rows = height-1; rows >= 0; --rows )
    {
        uint32 *colPoint2Src = rowPoint2Src;
        BYTE* colPoint2R = rowPointerToR;
        for ( int cols = 0; cols < width; cols ++ )
        {
            // read the channel : A
            *colPoint2R = (BYTE)TIFFGetA(*colPoint2Src);
            // or : colPoint2R[0] = (BYTE)TIFFGetA(colPoint2Src[0]);
            colPoint2R++;
            colPoint2Src++;
        }
        rowPoint2Src  -= width;
        rowPointerToR += width;
    } 
    cv::Mat imageR_mat( height, width, CV_8UC1, imageR, width );
    imwrite("E:\\0-Alpha.jpg", imageR_mat);

    _TIFFfree(imageR);
}

如果想把4通道TIFF文件,讀入內存后轉為Mat格式,可以這么做:

/* save as a Mat */

cv::Mat image(height, width, CV_8UC4, cv::Scalar::all(0));
if ( TIFFReadRGBAImage(tif, width, height, raster, 0) )
{
    uchar* imageData = (uchar*)image.data;
    uint32* rowPoint2Src = raster + (height-1)*width;

    for ( int rows = height-1; rows >= 0; --rows )
    {
        uint32 *colPoint2Src = rowPoint2Src;
        // image pixels are in an inverted order, which is same as bmp format
        uchar* colPoint = image.ptr<uchar>( height - rows - 1 );
        for ( int cols = 0; cols < width; cols ++ )
        {
            *colPoint++ = (uchar)TIFFGetB(*colPoint2Src); // B
            *colPoint++ = (uchar)TIFFGetG(*colPoint2Src); // G
            *colPoint++ = (uchar)TIFFGetR(*colPoint2Src); // R
            *colPoint++ = (uchar)TIFFGetA(*colPoint2Src); // A

            colPoint2Src++;
        }
        rowPoint2Src  -= width;
    } 
}

創建並保存4通道TIFF圖像可以按照下面的方法:

/* creat and write a ABGR tiff image */
#include <iostream>
#include <vector>

#include "cv.h"
#include "highgui.h"

#include "tiffio.h"

using namespace std;
using namespace cv;

void main()
{
    cv::Mat imageGray  = cv::imread( "C:\\Users\\Leo\\Desktop\\Test\\0.jpg" );
    cv::Mat imageAlpha = cv::imread( "C:\\Users\\Leo\\Desktop\\Test\\0-R.jpg" ); 

    if ( imageGray.channels() == 3 )
        cv::cvtColor( imageGray, imageGray, CV_RGB2GRAY );
    if ( imageAlpha.channels() == 3 )
        cv::cvtColor( imageAlpha, imageAlpha, CV_RGB2GRAY );

    int cols = imageGray.cols;
    int rows = imageGray.rows;

    cv::Mat imageMerged(rows, cols, CV_8UC4, cv::Scalar::all(0));

    uchar* data        = (uchar*) imageMerged.data;
    uchar* data_gray   = (uchar*) imageGray.data;
    uchar* data_alpha  = (uchar*) imageAlpha.data;

    for ( int i=0; i<rows; i++ )
    {
        for ( int j=0; j<cols; j++ )
        {
            int index = i*cols + j;
            data[index*4]   = data_gray[index];
            data[index*4+1] = data_gray[index];
            data[index*4+2] = data_gray[index];
            data[index*4+3] = data_alpha[index];
        }
    }

    uint32 width, height;
    width  = cols;
    height = rows;

    /* save as PNG */
    std::vector<int> compression_params;
    compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION);
    compression_params.push_back(9);
    cv::imwrite( "C:\\Users\\Leo\\Desktop\\Test\\0-1.png", imageMerged, compression_params );

    /* save as TIFF */
    TIFF *imageWrite =  TIFFOpen( "C:\\Users\\Leo\\Desktop\\Test\\0-2.tif", "w" );
    if ( imageWrite )
    {
        TIFFSetField( imageWrite, TIFFTAG_IMAGEWIDTH, width );
        TIFFSetField( imageWrite, TIFFTAG_IMAGELENGTH, height );
        TIFFSetField( imageWrite, TIFFTAG_COMPRESSION, COMPRESSION_PACKBITS);
        TIFFSetField( imageWrite, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
        TIFFSetField( imageWrite, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
        TIFFSetField( imageWrite, TIFFTAG_BITSPERSAMPLE, 8);
        TIFFSetField( imageWrite, TIFFTAG_SAMPLESPERPIXEL, 4);

        uchar *bits = (uchar*) imageMerged.data;
// uchar* pdst = new uchar[cols*4];

        for ( int i=0; i<rows; i++ )
        { 
// int curidx_bit = i * cols * 4;
// for ( int idx = 0; idx < cols; idx ++ )
// {
// int curidx_dst = idx * 4;
// int curidx_bit2 = curidx_bit + curidx_dst;
// 
// pdst[curidx_dst] = bits[curidx_bit2];
// pdst[curidx_dst+1] = bits[curidx_bit2+1];
// pdst[curidx_dst+2] = bits[curidx_bit2+2];
// pdst[curidx_dst+3] = bits[curidx_bit2+3];
// }
            TIFFWriteScanline( imageWrite, &bits[i*cols*4], i, 0 );
// TIFFWriteScanline( imageWrite, pdst, i, 0 );
        }
        TIFFClose( imageWrite );
    }
    else
    {
        std::cout << "Open file error!" << std::endl;
        exit(1);
    }
}

這段代碼讀取了兩張圖像,一張為灰度圖,另一張為對應的Alpha通道圖像,然后將其轉換為RGBA圖像。代碼里給出了TIFFWriteScanlineTIFF的兩種方法,其中注釋掉的部分即為另一種方法。

6 三種圖像I/O讀寫方法

libTIFF中提供了三種文件讀寫方式:

  • Scanline-based
  • Strip-oriented
  • Tile-oriented

此處不做過的介紹,詳情請閱讀 Using The TIFF Library~

Opencv中也有對TIFF文件的操作,也是基於libTIFF庫,詳情參考文件:grfmt_tiff.cpp


PS:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM