zoukankan      html  css  js  c++  java
  • 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
    ", 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:

  • 相关阅读:
    [转] 股票基础知识
    [原] combobox如何让用户不能输入只能从下拉列表里面选择
    【原】2个面试问题(与同事李将的交流)
    [转] 纯代码取得本机IP地址
    [转] 关于硬盘修复以及低级格式化的一些文章
    [转] 130道C#面试题
    初学Sockets编程(四) 发送和接收数据
    利用Beyond Compare比较文件
    第三日:SimuLink之后是Stateflow
    简单的RPC编程实践——HelloWorld的实现
  • 原文地址:https://www.cnblogs.com/hehehaha/p/6332219.html
Copyright © 2011-2022 走看看