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
中提供了获取和设置标签值的函数:TIFFGetField
和TIFFSetField
:
/* 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
图像。代码里给出了TIFFWriteScanline
写TIFF
的两种方法,其中注释掉的部分即为另一种方法。
6 三种图像I/O读写方法
libTIFF
中提供了三种文件读写方式:
- Scanline-based
- Strip-oriented
- Tile-oriented
此处不做过的介绍,详情请阅读 Using The TIFF Library~
Opencv中也有对TIFF
文件的操作,也是基于libTIFF
库,详情参考文件:grfmt_tiff.cpp
。
PS:
libTIFF 图像读取与保存的更多相关文章
- Opencv 图像读取与保存问题
转自 @yhl_leo 1 图像读取 首先看一下,imread函数的声明: // C++: Mat based Mat imread( ); // C: IplImage based IplImage ...
- DIB位图文件的格式、读取、保存和显示(转载)
一.位图文件结构 位图文件由三部分组成:文件头 + 位图信息 + 位图像素数据 1.位图文件头:BitMapFileHeader.位图文件头主要用于识别位图文件.以下是位图文件头结构的定义: type ...
- openCV学习——一、图像读取、显示、输出
openCV学习——一.图像读取.显示.输出 一.Mat imread(const string& filename,int flags=1),用于读取图片 1.参数介绍 filename ...
- opencv图像读取-imread
前言 图像的读取和保存一定要注意imread函数的各个参数及其意义,尽量不要使用默认参数,否则就像数据格式出现错误(here)一样,很难查找错误原因的: re: 1.opencv图像的读取与保存; 完
- 【opencv学习笔记五】一个简单程序:图像读取与显示
今天我们来学习一个最简单的程序,即从文件读取图像并且创建窗口显示该图像. 目录 [imread]图像读取 [namedWindow]创建window窗口 [imshow]图像显示 [imwrite]图 ...
- 【opencv系列02】OpenCV4.X图像读取与显示
一.读取图片 opencv中采用imread() 函数读取图像 imread(filename, flags=None) filename 图片的路径 flags 图像读取方式 ● c ...
- Python,ElementTree模块处理XML时注释无法读取和保存的问题
from xml.etree import ElementTree class CommentedTreeBuilder ( ElementTree.XMLTreeBuilder ): def __i ...
- 【原】Learning Spark (Python版) 学习笔记(二)----键值对、数据读取与保存、共享特性
本来应该上周更新的,结果碰上五一,懒癌发作,就推迟了 = =.以后还是要按时完成任务.废话不多说,第四章-第六章主要讲了三个内容:键值对.数据读取与保存与Spark的两个共享特性(累加器和广播变量). ...
- 做参数可以读取参数 保存参数 用xml文件的方式
做参数可以读取参数 保存参数 用xml文件的方式 好处:供不同用户保存适合自己使用的参数
随机推荐
- rsync实时同步mysql数据库
1.主机slave 注:没有包的可以去下载 yum -y install gcc gcc-c++ 上传包 rsync-3.1.3.tar.gz 使用tar命令解压 使用gcc gcc-c++编译 ./ ...
- 小学生都能学会的python(生成器)
小学生都能学会的python(生成器) 1. 生成器 生成器的本质就是迭代器. 生成器由生成器函数来创建或者通过生成器表达式来创建 # def func(): # lst = [] # for i i ...
- HH实习(hpu1287)(斐波那契运用)
HH实习 Time Limit: 1 Sec Memory Limit: 128 MB Submit: 44 Solved: 29 [Submit][id=1287">Status ...
- POI 导入excel数据自己主动封装成model对象--代码分析
上完代码后,对代码进行基本的分析: 1.主要使用反射api将数数据注入javabean对象 2.代码中的日志信息级别为debug级别 3.获取ExcelImport对象后须要调用init()方法初始化 ...
- 使用excel进行数据挖掘(6)---- 预測
在配置环境后,能够使用excel进行数据挖掘. 环境配置问题可參阅: http://blog.csdn.net/xinxing__8185/article/details/46445435 例子 DM ...
- 使用docker搭建hadoop分布式集群
使用docker搭建部署hadoop分布式集群 在网上找了非常长时间都没有找到使用docker搭建hadoop分布式集群的文档,没办法,仅仅能自己写一个了. 一:环境准备: 1:首先要有一个Cento ...
- Oracle SGA具体解释
SGA(SYSTEM Global Area )系统全局区 l 数据快速缓存 在Oracle进行数据处理的过程中,代价最昂贵的就是物理 I/O操作了.相同的数据从内存中得到要比从磁盘上读取快的多. 因 ...
- C++中的字节对齐
本博客(http://blog.csdn.net/livelylittlefish)贴出作者(三二一.小鱼)相关研究.学习内容所做的笔记,欢迎广大朋友指正! 字节对齐 1. 基本概念字节对齐:计算机存 ...
- 14、反射(reflect)
一.反射概念 不用实例化也可以调用类中的私有成员:反射慢,实例化快:反射可以看到其他类中的内部构造,透明,但是不安全. JAR:JAVA函数库 WAR:web发布的包 YAR:RPC服务 二.idea ...
- 公司--下载svg图片
加载本地svg图片: SVGParserRenderer norDrawable = OtherPageConfigsManager.getInstance().getSVGParserRendere ...