Tiff编码解码器封装
文章目录
- TIFF图像处理库大纲介绍
- 1. 概述
- 2. 核心组件
- 2.1 内存IO处理
- 2.2 TiffManager类
- 2.3 实现类(Impl)
- 3. 主要功能
- 3.1 解码功能
- 3.2 编码功能
- 3.3 辅助功能
- 4. 技术特点
- 4.1 内存处理
- 4.2 格式支持
- 4.3 错误处理
- 5. 使用示例
- 5.1 解码TIFF图像
- 5.2 编码为TIFF
- 5.3 获取图像信息
- 6. 设计考虑
- 6.1 性能优化
- 6.2 扩展性
- 6.3 兼容性
- 头文件实现
- 源文件实现
- 测试代码
TIFF图像处理库大纲介绍
1. 概述
这个C++库提供了完整的TIFF图像编解码功能,支持内存中的TIFF图像处理,无需临时文件。主要特点包括:
-
内存到内存的TIFF编解码
-
支持多种像素格式(灰度、RGB、RGBA)
-
支持多种压缩格式
-
多帧TIFF处理能力
-
高效的像素数据转换
2. 核心组件
2.1 内存IO处理
-
TiffMemoryIO
结构体:管理内存中的TIFF数据 -
自定义IO函数:
-
TiffMemoryRead
:内存读取函数 -
TiffMemoryWrite
:内存写入函数 -
TiffMemorySeek
:内存定位函数 -
TiffMemoryClose
:关闭处理函数 -
TiffMemorySize
:获取大小函数 -
TiffMemoryMap/Unmap
:内存映射函数
-
2.2 TiffManager类
主接口类,提供以下功能:
-
从内存解码TIFF图像
-
将图像编码为TIFF格式到内存
-
获取TIFF图像信息
-
像素格式转换
2.3 实现类(Impl)
封装实际处理逻辑:
-
像素格式与TIFF光度解释的转换
-
TIFF标签读取和解析
-
图像数据解码和编码
3. 主要功能
3.1 解码功能
-
Decode
方法:从内存解码TIFF图像-
支持指定输出格式(RGB/RGBA/灰度)
-
自动处理多帧TIFF
-
保留原始TIFF数据
-
3.2 编码功能
-
Encode
方法:将像素数据编码为TIFF-
支持多种压缩格式(包括JPEG)
-
可设置压缩质量
-
支持RGB/RGBA/灰度格式
-
3.3 辅助功能
-
GetInfo
:获取TIFF图像信息而不解码像素 -
IsValidTIFF
:验证TIFF数据有效性 -
GetChannelsCount
:获取格式对应的通道数 -
ConvertRGBToFormat
:像素格式转换
4. 技术特点
4.1 内存处理
-
完全在内存中操作,无文件I/O开销
-
自定义内存IO处理函数
-
支持内存映射访问
4.2 格式支持
-
输入:支持各种TIFF变体(包括压缩格式)
-
输出:可转换为指定像素格式
-
自动处理光度解释和色彩空间
4.3 错误处理
-
全面的错误检查
-
清晰的错误返回
-
资源自动释放(使用RAII)
5. 使用示例
5.1 解码TIFF图像
TiffManager manager;
ImageInfo info;
std::vector<uint8_t> tiffData = LoadTiffFile("image.tif");
if (manager.Decode(tiffData, imageinfo::Format::RGB, info)) {// 使用解码后的像素数据(info.pixels_)
}
5.2 编码为TIFF
TiffManager manager;
std::vector<uint8_t> tiffData;
std::vector<uint8_t> pixels = GetImagePixels(); // 获取RGB像素数据
if (manager.Encode(pixels, width, height, imageinfo::Format::RGB, tiffData, TiffCompression::LZW)) {// 使用生成的TIFF数据(tiffData)
}
5.3 获取图像信息
TiffManager manager;
ImageInfo info;
if (manager.GetInfo(tiffData, info)) {// 获取图像宽度、高度等信息
}
6. 设计考虑
6.1 性能优化
-
最小化内存拷贝
-
使用扫描线处理大图像
-
支持并行处理的接口设计
6.2 扩展性
-
易于添加新的压缩格式支持
-
可扩展支持更多像素格式
-
分离接口与实现便于维护
6.3 兼容性
-
处理各种TIFF变体
-
自动转换不兼容的像素格式
-
健壮的错误处理
这个库为C++应用程序提供了完整的内存TIFF处理解决方案,适用于需要高效处理TIFF图像而不依赖临时文件的场景。
头文件实现
#pragma once#include "tiff.h"
#include "tiffio.h"
#include <vector>
#include <cstdint>
#include <memory>
#include <string>
#include <optional>
#include <algorithm>
#include <stdexcept>
#include <functional>
// 图像格式枚举
namespace imageinfo {enum class Format {UNKNOWN = 0,GRAY = 1,RGB = 3,RGBA = 4};
}// TIFF 压缩方法枚举
enum class TiffCompression {NONE = COMPRESSION_NONE,LZW = COMPRESSION_LZW,DEFLATE = COMPRESSION_DEFLATE,JPEG = COMPRESSION_JPEG,PACKBITS = COMPRESSION_PACKBITS,ADOBE_DEFLATE = COMPRESSION_ADOBE_DEFLATE
};// TIFF 像素格式枚举
enum class TiffPhotometric {MINISWHITE = PHOTOMETRIC_MINISWHITE,MINISBLACK = PHOTOMETRIC_MINISBLACK,RGB = PHOTOMETRIC_RGB,PALETTE = PHOTOMETRIC_PALETTE,MASK = PHOTOMETRIC_MASK,SEPARATED = PHOTOMETRIC_SEPARATED,YCBCR = PHOTOMETRIC_YCBCR,CIELAB = PHOTOMETRIC_CIELAB,LOGL = PHOTOMETRIC_LOGL,LOGLUV = PHOTOMETRIC_LOGLUV
};// 图像信息结构体
struct ImageInfo {int width_ = 0;int height_ = 0;int channels_ = 0;std::vector<uint8_t> image_data_; // 原始编码的TIFF数据std::vector<uint8_t> pixels_; // 解码后的像素数据imageinfo::Format format_ = imageinfo::Format::UNKNOWN;// 额外的TIFF特定信息TiffCompression compression_ = TiffCompression::NONE;TiffPhotometric photometric_ = TiffPhotometric::RGB;uint16_t bits_per_sample_ = 8;uint16_t samples_per_pixel_ = 3;uint16_t planar_config_ = PLANARCONFIG_CONTIG;// 清除数据void Clear() {width_ = height_ = channels_ = 0;image_data_.clear();pixels_.clear();format_ = imageinfo::Format::UNKNOWN;}// 检查是否有效bool IsValid() const {return width_ > 0 && height_ > 0 && channels_ > 0 && !pixels_.empty();}
};class TiffManager {
public:TiffManager();~TiffManager();// 禁止拷贝,允许移动TiffManager(const TiffManager&) = delete;TiffManager& operator=(const TiffManager&) = delete;TiffManager(TiffManager&&) noexcept;TiffManager& operator=(TiffManager&&) noexcept;/*** @brief 从内存数据解码TIFF图像* @param tiff_data TIFF编码数据* @param output_format 期望的输出格式* @param info_out 输出的图像信息* @return 是否解码成功*/bool Decode(const std::vector<uint8_t>& tiff_data,imageinfo::Format output_format,ImageInfo& info_out);/*** @brief 编码像素数据为TIFF格式* @param pixel_data 像素数据* @param width 图像宽度* @param height 图像高度* @param format 像素格式* @param compression 压缩方法* @param quality 压缩质量(0-100,仅对JPEG压缩有效)* @param tiff_data_out 输出的TIFF数据* @return 是否编码成功*/bool Encode(const std::vector<uint8_t>& pixel_data,int width,int height,imageinfo::Format format,std::vector<uint8_t>& tiff_data_out ,TiffCompression compression = TiffCompression::LZW,int quality = 75);/*** @brief 获取TIFF图像信息(不解码像素数据)* @param tiff_data TIFF编码数据* @param info_out 输出的图像信息* @return 是否成功获取信息*/bool GetInfo(const std::vector<uint8_t>& tiff_data,ImageInfo& info_out);/*** @brief 检查数据是否为有效的TIFF格式* @param data 待检查的数据* @return 是否为有效的TIFF*/static bool IsValidTIFF(const std::vector<uint8_t>& data);/*** @brief 获取像素格式对应的通道数* @param format 像素格式* @return 通道数*/static int GetChannelsCount(imageinfo::Format format);/*** @brief 转换RGB数据到指定格式* @param rgb_data RGB源数据* @param width 图像宽度* @param height 图像高度* @param target_format 目标格式* @return 转换后的像素数据*/static std::vector<uint8_t> ConvertRGBToFormat(const uint8_t* rgb_data,int width,int height,imageinfo::Format target_format);private:class Impl;std::unique_ptr<Impl> impl_;// 内存写入支持struct MemoryWriter {std::vector<uint8_t>& buffer;toff_t pos = 0;};// 内存写入函数static tmsize_t TiffMemoryWrite(thandle_t handle, void* buf, tmsize_t size);static toff_t TiffMemorySeek(thandle_t handle, toff_t offset, int whence);static int TiffMemoryClose(thandle_t handle);static toff_t TiffMemorySize(thandle_t handle);static int TiffMemoryMap(thandle_t handle, void** base, toff_t* size);static void TiffMemoryUnmap(thandle_t handle, void* base, toff_t size);
};
源文件实现
#include "TiffManager.h"
#include <memory>
#include <cstring>
#include <functional>
#include <iostream>#ifndef EXTRASAMPLES_ASSOCALPHA
#define EXTRASAMPLES_ASSOCALPHA 1
#endif#ifndef EXTRASAMPLES_UNASSALPHA
#define EXTRASAMPLES_UNASSALPHA 2
#endif// 内存IO处理结构
struct TiffMemoryIO {const uint8_t* data;tmsize_t size;toff_t pos;
};// TIFF内存读取函数
static tmsize_t TiffMemoryRead(thandle_t handle, void* buf, tmsize_t size) {TiffMemoryIO* memio = static_cast<TiffMemoryIO*>(handle);tmsize_t available = memio->size - memio->pos;tmsize_t to_read = (size < available) ? size : available;if (to_read > 0) {memcpy(buf, memio->data + memio->pos, to_read);memio->pos += to_read;}return to_read;
}// 修正:添加缺失的静态声明
static tmsize_t TiffMemoryReadForWrite(thandle_t, void*, tmsize_t) {// 写入模式下不需要读取功能return 0;
}// TIFF内存写入函数
tmsize_t TiffManager::TiffMemoryWrite(thandle_t handle, void* buf, tmsize_t size) {TiffManager::MemoryWriter* writer = static_cast<TiffManager::MemoryWriter*>(handle);// 确保缓冲区足够大if (writer->pos + size > writer->buffer.size()) {writer->buffer.resize(writer->pos + size);}// 写入数据memcpy(writer->buffer.data() + writer->pos, buf, size);writer->pos += size;return size;
}// TIFF文件定位函数
toff_t TiffManager::TiffMemorySeek(thandle_t handle, toff_t offset, int whence) {TiffManager::MemoryWriter* writer = static_cast<TiffManager::MemoryWriter*>(handle);switch (whence) {case SEEK_SET:writer->pos = offset;break;case SEEK_CUR:writer->pos += offset;break;case SEEK_END:writer->pos = writer->buffer.size() + offset;break;}// 确保位置有效if (writer->pos < 0) writer->pos = 0;if (static_cast<size_t>(writer->pos) > writer->buffer.size()) {writer->buffer.resize(writer->pos);}return writer->pos;
}// 修正:为读取操作添加单独的内存定位函数
static toff_t TiffMemorySeekForRead(thandle_t handle, toff_t offset, int whence) {TiffMemoryIO* memio = static_cast<TiffMemoryIO*>(handle);switch (whence) {case SEEK_SET:memio->pos = offset;break;case SEEK_CUR:memio->pos += offset;break;case SEEK_END:memio->pos = memio->size + offset;break;}// 确保位置有效if (memio->pos < 0) memio->pos = 0;if (memio->pos > memio->size) memio->pos = memio->size;return memio->pos;
}// TIFF文件关闭函数
int TiffManager::TiffMemoryClose(thandle_t handle) {// 不需要特殊处理return 0;
}// 修正:为读取操作添加单独的文件关闭函数
static int TiffMemoryCloseForRead(thandle_t) {// 不需要特殊处理return 0;
}// TIFF文件大小获取函数
toff_t TiffManager::TiffMemorySize(thandle_t handle) {TiffManager::MemoryWriter* writer = static_cast<TiffManager::MemoryWriter*>(handle);return writer->buffer.size();
}// 修正:为读取操作添加单独的文件大小获取函数
static toff_t TiffMemorySizeForRead(thandle_t handle) {TiffMemoryIO* memio = static_cast<TiffMemoryIO*>(handle);return memio->size;
}// TIFF内存映射函数
int TiffManager::TiffMemoryMap(thandle_t handle, void** base, toff_t* size) {TiffManager::MemoryWriter* writer = static_cast<TiffManager::MemoryWriter*>(handle);*base = writer->buffer.data();*size = writer->buffer.size();return 1;
}// 修正:为读取操作添加单独的内存映射函数
static int TiffMemoryMapForRead(thandle_t handle, void** base, toff_t* size) {TiffMemoryIO* memio = static_cast<TiffMemoryIO*>(handle);*base = const_cast<uint8_t*>(memio->data);*size = memio->size;return 1;
}void TiffManager::TiffMemoryUnmap(thandle_t, void*, toff_t) {// 无操作
}// 修正:为读取操作添加单独的内存取消映射函数
static void TiffMemoryUnmapForRead(thandle_t, void*, toff_t) {// 无操作
}class TiffManager::Impl {
public:Impl() = default;~Impl() = default;// 转换像素格式到TIFF的光度解释static TiffPhotometric FormatToPhotometric(imageinfo::Format format) {switch (format) {case imageinfo::Format::GRAY:return TiffPhotometric::MINISBLACK;case imageinfo::Format::RGB:return TiffPhotometric::RGB;case imageinfo::Format::RGBA:return TiffPhotometric::RGB;default:return TiffPhotometric::RGB;}}// 转换TIFF的光度解释到像素格式static imageinfo::Format PhotometricToFormat(uint16_t photometric, uint16_t samples_per_pixel) {switch (photometric) {case PHOTOMETRIC_MINISWHITE:case PHOTOMETRIC_MINISBLACK:return imageinfo::Format::GRAY;case PHOTOMETRIC_RGB:return (samples_per_pixel == 4) ? imageinfo::Format::RGBA : imageinfo::Format::RGB;case PHOTOMETRIC_PALETTE:return imageinfo::Format::RGB; // 调色板图像转换为RGBcase PHOTOMETRIC_SEPARATED:return imageinfo::Format::RGB; // CMYK转换为RGBdefault:return imageinfo::Format::RGB;}}// 从TIFF文件读取图像信息bool ReadTIFFInfo(TIFF* tif, ImageInfo& info) {uint32_t width = 0, height = 0;uint16_t samples = 0, bits = 0, photometric = 0;uint16_t compression = 0, planar_config = 0;if (!TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width) ||!TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height)) {return false;}TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samples);TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bits);TIFFGetFieldDefaulted(tif, TIFFTAG_PHOTOMETRIC, &photometric);TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &compression);TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &planar_config);info.width_ = static_cast<int>(width);info.height_ = static_cast<int>(height);info.samples_per_pixel_ = samples;info.bits_per_sample_ = bits;info.photometric_ = static_cast<TiffPhotometric>(photometric);info.compression_ = static_cast<TiffCompression>(compression);info.planar_config_ = planar_config;info.channels_ = samples;info.format_ = PhotometricToFormat(photometric, samples);return true;}// 解码TIFF图像数据bool DecodeTIFFData(TIFF* tif, imageinfo::Format output_format, ImageInfo& info) {uint32_t width = info.width_;uint32_t height = info.height_;uint32_t npixels = width * height;// 分配内存存储解码后的数据std::vector<uint32_t> raster(npixels);if (!TIFFReadRGBAImage(tif, width, height, raster.data(), 0)) {return false;}// 根据输出格式转换数据int output_channels = TiffManager::GetChannelsCount(output_format);info.pixels_.resize(npixels * output_channels);info.format_ = output_format;info.channels_ = output_channels;// 转换RGBA数据到目标格式for (uint32_t i = 0; i < npixels; ++i) {uint32_t pixel = raster[i];uint8_t r = TIFFGetR(pixel);uint8_t g = TIFFGetG(pixel);uint8_t b = TIFFGetB(pixel);uint8_t a = TIFFGetA(pixel);size_t base_idx = i * output_channels;switch (output_format) {case imageinfo::Format::RGB:info.pixels_[base_idx] = r;info.pixels_[base_idx + 1] = g;info.pixels_[base_idx + 2] = b;break;case imageinfo::Format::RGBA:info.pixels_[base_idx] = r;info.pixels_[base_idx + 1] = g;info.pixels_[base_idx + 2] = b;info.pixels_[base_idx + 3] = a;break;case imageinfo::Format::GRAY:// 转换为灰度info.pixels_[i] = static_cast<uint8_t>((r * 299 + g * 587 + b * 114) / 1000);break;default:return false;}}return true;}
};// TiffManager 实现
TiffManager::TiffManager() : impl_(std::make_unique<Impl>()) {}TiffManager::~TiffManager() = default;TiffManager::TiffManager(TiffManager&&) noexcept = default;
TiffManager& TiffManager::operator=(TiffManager&&) noexcept = default;bool TiffManager::Decode(const std::vector<uint8_t>& tiff_data,imageinfo::Format output_format,ImageInfo& info_out)
{if (!IsValidTIFF(tiff_data)) {return false;}// 设置内存IOTiffMemoryIO memio;memio.data = tiff_data.data();memio.size = static_cast<tmsize_t>(tiff_data.size());memio.pos = 0;// 修正:使用正确的读取函数指针TIFF* tif = TIFFClientOpen("memory", "r",&memio,TiffMemoryRead,TiffMemoryReadForWrite, // 修正:提供写入函数(虽然读取模式下不会使用)TiffMemorySeekForRead, // 修正:使用读取专用的定位函数TiffMemoryCloseForRead, // 修正:使用读取专用的关闭函数TiffMemorySizeForRead, // 修正:使用读取专用的大小函数TiffMemoryMapForRead, // 修正:使用读取专用的映射函数TiffMemoryUnmapForRead // 修正:使用读取专用的取消映射函数);if (!tif) {return false;}// 确保TIFF文件会被关闭auto tif_closer = [](TIFF* t) { TIFFClose(t); };std::unique_ptr<TIFF, decltype(tif_closer)> tif_managed(tif, tif_closer);// 读取图像信息if (!impl_->ReadTIFFInfo(tif, info_out)) {return false;}// 保存原始数据info_out.image_data_ = tiff_data;// 解码图像数据if (!impl_->DecodeTIFFData(tif, output_format, info_out)) {return false;}return true;
}bool TiffManager::Encode(const std::vector<uint8_t>& pixel_data,int width,int height,imageinfo::Format format,std::vector<uint8_t>& tiff_data_out,TiffCompression compression,int quality
)
{if (width <= 0 || height <= 0) {return false;}const int channels = GetChannelsCount(format);const int expected_size = width * height * channels;if (pixel_data.size() != static_cast<size_t>(expected_size)) {return false;}// 检查压缩方式与格式的兼容性if (compression == TiffCompression::JPEG) {// JPEG压缩不支持RGBA格式if (format == imageinfo::Format::RGBA) {return false;}// 对于灰度图像,我们使用MINISBLACK,否则使用YCBCR}// 创建内存写入器MemoryWriter writer{ tiff_data_out, 0 };tiff_data_out.clear(); // 清空,准备写入// 打开内存TIFF文件用于写入TIFF* tif = TIFFClientOpen("memory", "w",&writer,TiffMemoryReadForWrite, // 修正:提供读取函数(虽然写入模式下不会使用)TiffMemoryWrite,TiffMemorySeek,TiffMemoryClose,TiffMemorySize,TiffMemoryMap,TiffMemoryUnmap);if (!tif) {return false;}auto tif_closer = [](TIFF* t) { TIFFClose(t); };std::unique_ptr<TIFF, decltype(tif_closer)> tif_managed(tif, tif_closer);// 设置TIFF标签TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width);TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height);TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, channels);TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);TIFFSetField(tif, TIFFTAG_COMPRESSION, static_cast<uint16_t>(compression));// 设置光度解释uint16_t photometric;if (compression == TiffCompression::JPEG) {if (format == imageinfo::Format::GRAY) {photometric = PHOTOMETRIC_MINISBLACK;}else {photometric = PHOTOMETRIC_YCBCR;}}else {photometric = static_cast<uint16_t>(Impl::FormatToPhotometric(format));}TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric);// 对于RGBA设置额外的标签if (format == imageinfo::Format::RGBA) {uint16_t extra_samples = 1;uint16_t sample_info = EXTRASAMPLES_ASSOCALPHA;TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, extra_samples, &sample_info);}// 设置JPEG质量(仅当压缩为JPEG时)if (compression == TiffCompression::JPEG) {TIFFSetField(tif, TIFFTAG_JPEGQUALITY, quality);// 设置YCBCR子采样,默认使用2x2if (format == imageinfo::Format::RGB) {TIFFSetField(tif, TIFFTAG_YCBCRSUBSAMPLING, 2, 2);}}// 设置行大小tmsize_t scanline_size = TIFFScanlineSize(tif);if (scanline_size == 0) {return false;}// 写入扫描线数据for (int row = 0; row < height; ++row) {const uint8_t* row_data = pixel_data.data() + row * width * channels;if (TIFFWriteScanline(tif, const_cast<uint8_t*>(row_data), row, 0) < 0) {return false;}}// 在TIFFClose时,数据会被刷新到内存缓冲区return true;
}bool TiffManager::GetInfo(const std::vector<uint8_t>& tiff_data,ImageInfo& info_out)
{if (!IsValidTIFF(tiff_data)) {return false;}// 设置内存IOTiffMemoryIO memio;memio.data = tiff_data.data();memio.size = static_cast<tmsize_t>(tiff_data.size());memio.pos = 0;// 修正:使用正确的读取函数指针TIFF* tif = TIFFClientOpen("memory", "r",&memio,TiffMemoryRead,TiffMemoryReadForWrite, // 修正:提供写入函数TiffMemorySeekForRead, // 修正:使用读取专用的定位函数TiffMemoryCloseForRead, // 修正:使用读取专用的关闭函数TiffMemorySizeForRead, // 修正:使用读取专用的大小函数TiffMemoryMapForRead, // 修正:使用读取专用的映射函数TiffMemoryUnmapForRead // 修正:使用读取专用的取消映射函数);if (!tif) {return false;}auto tif_closer = [](TIFF* t) { TIFFClose(t); };std::unique_ptr<TIFF, decltype(tif_closer)> tif_managed(tif, tif_closer);info_out.image_data_ = tiff_data;return impl_->ReadTIFFInfo(tif, info_out);
}bool TiffManager::IsValidTIFF(const std::vector<uint8_t>& data) {if (data.size() < 4) {return false;}// 检查TIFF文件头// TIFF文件头可以是"II"(小端)或"MM"(大端)return (data[0] == 0x49 && data[1] == 0x49 && data[2] == 0x2A && data[3] == 0x00) ||(data[0] == 0x4D && data[1] == 0x4D && data[2] == 0x00 && data[3] == 0x2A);
}int TiffManager::GetChannelsCount(imageinfo::Format format) {switch (format) {case imageinfo::Format::GRAY: return 1;case imageinfo::Format::RGB: return 3;case imageinfo::Format::RGBA: return 4;default: return 3;}
}std::vector<uint8_t> TiffManager::ConvertRGBToFormat(const uint8_t* rgb_data,int width,int height,imageinfo::Format target_format)
{const int pixel_count = width * height;const int output_channels = GetChannelsCount(target_format);std::vector<uint8_t> result(pixel_count * output_channels);switch (target_format) {case imageinfo::Format::RGB:memcpy(result.data(), rgb_data, pixel_count * 3);break;case imageinfo::Format::RGBA:for (int i = 0; i < pixel_count; ++i) {result[i * 4] = rgb_data[i * 3]; // Rresult[i * 4 + 1] = rgb_data[i * 3 + 1]; // Gresult[i * 4 + 2] = rgb_data[i * 3 + 2]; // Bresult[i * 4 + 3] = 0xFF; // A}break;case imageinfo::Format::GRAY:for (int i = 0; i < pixel_count; ++i) {result[i] = static_cast<uint8_t>((rgb_data[i * 3] * 299 +rgb_data[i * 3 + 1] * 587 +rgb_data[i * 3 + 2] * 114) / 1000);}break;default:result.clear();break;}return result;
}
测试代码
#include "TiffManager.h"
#include <memory>
#include <cstring>
#include <functional>
#include <iostream>#ifndef EXTRASAMPLES_ASSOCALPHA
#define EXTRASAMPLES_ASSOCALPHA 1
#endif#ifndef EXTRASAMPLES_UNASSALPHA
#define EXTRASAMPLES_UNASSALPHA 2
#endif// 内存IO处理结构
struct TiffMemoryIO {const uint8_t* data;tmsize_t size;toff_t pos;
};// TIFF内存读取函数
static tmsize_t TiffMemoryRead(thandle_t handle, void* buf, tmsize_t size) {TiffMemoryIO* memio = static_cast<TiffMemoryIO*>(handle);tmsize_t available = memio->size - memio->pos;tmsize_t to_read = (size < available) ? size : available;if (to_read > 0) {memcpy(buf, memio->data + memio->pos, to_read);memio->pos += to_read;}return to_read;
}// 修正:添加缺失的静态声明
static tmsize_t TiffMemoryReadForWrite(thandle_t, void*, tmsize_t) {// 写入模式下不需要读取功能return 0;
}// TIFF内存写入函数
tmsize_t TiffManager::TiffMemoryWrite(thandle_t handle, void* buf, tmsize_t size) {TiffManager::MemoryWriter* writer = static_cast<TiffManager::MemoryWriter*>(handle);// 确保缓冲区足够大if (writer->pos + size > writer->buffer.size()) {writer->buffer.resize(writer->pos + size);}// 写入数据memcpy(writer->buffer.data() + writer->pos, buf, size);writer->pos += size;return size;
}// TIFF文件定位函数
toff_t TiffManager::TiffMemorySeek(thandle_t handle, toff_t offset, int whence) {TiffManager::MemoryWriter* writer = static_cast<TiffManager::MemoryWriter*>(handle);switch (whence) {case SEEK_SET:writer->pos = offset;break;case SEEK_CUR:writer->pos += offset;break;case SEEK_END:writer->pos = writer->buffer.size() + offset;break;}// 确保位置有效if (writer->pos < 0) writer->pos = 0;if (static_cast<size_t>(writer->pos) > writer->buffer.size()) {writer->buffer.resize(writer->pos);}return writer->pos;
}// 修正:为读取操作添加单独的内存定位函数
static toff_t TiffMemorySeekForRead(thandle_t handle, toff_t offset, int whence) {TiffMemoryIO* memio = static_cast<TiffMemoryIO*>(handle);switch (whence) {case SEEK_SET:memio->pos = offset;break;case SEEK_CUR:memio->pos += offset;break;case SEEK_END:memio->pos = memio->size + offset;break;}// 确保位置有效if (memio->pos < 0) memio->pos = 0;if (memio->pos > memio->size) memio->pos = memio->size;return memio->pos;
}// TIFF文件关闭函数
int TiffManager::TiffMemoryClose(thandle_t handle) {// 不需要特殊处理return 0;
}// 修正:为读取操作添加单独的文件关闭函数
static int TiffMemoryCloseForRead(thandle_t) {// 不需要特殊处理return 0;
}// TIFF文件大小获取函数
toff_t TiffManager::TiffMemorySize(thandle_t handle) {TiffManager::MemoryWriter* writer = static_cast<TiffManager::MemoryWriter*>(handle);return writer->buffer.size();
}// 修正:为读取操作添加单独的文件大小获取函数
static toff_t TiffMemorySizeForRead(thandle_t handle) {TiffMemoryIO* memio = static_cast<TiffMemoryIO*>(handle);return memio->size;
}// TIFF内存映射函数
int TiffManager::TiffMemoryMap(thandle_t handle, void** base, toff_t* size) {TiffManager::MemoryWriter* writer = static_cast<TiffManager::MemoryWriter*>(handle);*base = writer->buffer.data();*size = writer->buffer.size();return 1;
}// 修正:为读取操作添加单独的内存映射函数
static int TiffMemoryMapForRead(thandle_t handle, void** base, toff_t* size) {TiffMemoryIO* memio = static_cast<TiffMemoryIO*>(handle);*base = const_cast<uint8_t*>(memio->data);*size = memio->size;return 1;
}void TiffManager::TiffMemoryUnmap(thandle_t, void*, toff_t) {// 无操作
}// 修正:为读取操作添加单独的内存取消映射函数
static void TiffMemoryUnmapForRead(thandle_t, void*, toff_t) {// 无操作
}class TiffManager::Impl {
public:Impl() = default;~Impl() = default;// 转换像素格式到TIFF的光度解释static TiffPhotometric FormatToPhotometric(imageinfo::Format format) {switch (format) {case imageinfo::Format::GRAY:return TiffPhotometric::MINISBLACK;case imageinfo::Format::RGB:return TiffPhotometric::RGB;case imageinfo::Format::RGBA:return TiffPhotometric::RGB;default:return TiffPhotometric::RGB;}}// 转换TIFF的光度解释到像素格式static imageinfo::Format PhotometricToFormat(uint16_t photometric, uint16_t samples_per_pixel) {switch (photometric) {case PHOTOMETRIC_MINISWHITE:case PHOTOMETRIC_MINISBLACK:return imageinfo::Format::GRAY;case PHOTOMETRIC_RGB:return (samples_per_pixel == 4) ? imageinfo::Format::RGBA : imageinfo::Format::RGB;case PHOTOMETRIC_PALETTE:return imageinfo::Format::RGB; // 调色板图像转换为RGBcase PHOTOMETRIC_SEPARATED:return imageinfo::Format::RGB; // CMYK转换为RGBdefault:return imageinfo::Format::RGB;}}// 从TIFF文件读取图像信息bool ReadTIFFInfo(TIFF* tif, ImageInfo& info) {uint32_t width = 0, height = 0;uint16_t samples = 0, bits = 0, photometric = 0;uint16_t compression = 0, planar_config = 0;if (!TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width) ||!TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height)) {return false;}TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samples);TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bits);TIFFGetFieldDefaulted(tif, TIFFTAG_PHOTOMETRIC, &photometric);TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &compression);TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &planar_config);info.width_ = static_cast<int>(width);info.height_ = static_cast<int>(height);info.samples_per_pixel_ = samples;info.bits_per_sample_ = bits;info.photometric_ = static_cast<TiffPhotometric>(photometric);info.compression_ = static_cast<TiffCompression>(compression);info.planar_config_ = planar_config;info.channels_ = samples;info.format_ = PhotometricToFormat(photometric, samples);return true;}// 解码TIFF图像数据bool DecodeTIFFData(TIFF* tif, imageinfo::Format output_format, ImageInfo& info) {uint32_t width = info.width_;uint32_t height = info.height_;uint32_t npixels = width * height;// 分配内存存储解码后的数据std::vector<uint32_t> raster(npixels);if (!TIFFReadRGBAImage(tif, width, height, raster.data(), 0)) {return false;}// 根据输出格式转换数据int output_channels = TiffManager::GetChannelsCount(output_format);info.pixels_.resize(npixels * output_channels);info.format_ = output_format;info.channels_ = output_channels;// 转换RGBA数据到目标格式for (uint32_t i = 0; i < npixels; ++i) {uint32_t pixel = raster[i];uint8_t r = TIFFGetR(pixel);uint8_t g = TIFFGetG(pixel);uint8_t b = TIFFGetB(pixel);uint8_t a = TIFFGetA(pixel);size_t base_idx = i * output_channels;switch (output_format) {case imageinfo::Format::RGB:info.pixels_[base_idx] = r;info.pixels_[base_idx + 1] = g;info.pixels_[base_idx + 2] = b;break;case imageinfo::Format::RGBA:info.pixels_[base_idx] = r;info.pixels_[base_idx + 1] = g;info.pixels_[base_idx + 2] = b;info.pixels_[base_idx + 3] = a;break;case imageinfo::Format::GRAY:// 转换为灰度info.pixels_[i] = static_cast<uint8_t>((r * 299 + g * 587 + b * 114) / 1000);break;default:return false;}}return true;}
};// TiffManager 实现
TiffManager::TiffManager() : impl_(std::make_unique<Impl>()) {}TiffManager::~TiffManager() = default;TiffManager::TiffManager(TiffManager&&) noexcept = default;
TiffManager& TiffManager::operator=(TiffManager&&) noexcept = default;bool TiffManager::Decode(const std::vector<uint8_t>& tiff_data,imageinfo::Format output_format,ImageInfo& info_out)
{if (!IsValidTIFF(tiff_data)) {return false;}// 设置内存IOTiffMemoryIO memio;memio.data = tiff_data.data();memio.size = static_cast<tmsize_t>(tiff_data.size());memio.pos = 0;// 修正:使用正确的读取函数指针TIFF* tif = TIFFClientOpen("memory", "r",&memio,TiffMemoryRead,TiffMemoryReadForWrite, // 修正:提供写入函数(虽然读取模式下不会使用)TiffMemorySeekForRead, // 修正:使用读取专用的定位函数TiffMemoryCloseForRead, // 修正:使用读取专用的关闭函数TiffMemorySizeForRead, // 修正:使用读取专用的大小函数TiffMemoryMapForRead, // 修正:使用读取专用的映射函数TiffMemoryUnmapForRead // 修正:使用读取专用的取消映射函数);if (!tif) {return false;}// 确保TIFF文件会被关闭auto tif_closer = [](TIFF* t) { TIFFClose(t); };std::unique_ptr<TIFF, decltype(tif_closer)> tif_managed(tif, tif_closer);// 读取图像信息if (!impl_->ReadTIFFInfo(tif, info_out)) {return false;}// 保存原始数据info_out.image_data_ = tiff_data;// 解码图像数据if (!impl_->DecodeTIFFData(tif, output_format, info_out)) {return false;}return true;
}bool TiffManager::Encode(const std::vector<uint8_t>& pixel_data,int width,int height,imageinfo::Format format,std::vector<uint8_t>& tiff_data_out,TiffCompression compression,int quality
)
{if (width <= 0 || height <= 0) {return false;}const int channels = GetChannelsCount(format);const int expected_size = width * height * channels;if (pixel_data.size() != static_cast<size_t>(expected_size)) {return false;}// 检查压缩方式与格式的兼容性if (compression == TiffCompression::JPEG) {// JPEG压缩不支持RGBA格式if (format == imageinfo::Format::RGBA) {return false;}// 对于灰度图像,我们使用MINISBLACK,否则使用YCBCR}// 创建内存写入器MemoryWriter writer{ tiff_data_out, 0 };tiff_data_out.clear(); // 清空,准备写入// 打开内存TIFF文件用于写入TIFF* tif = TIFFClientOpen("memory", "w",&writer,TiffMemoryReadForWrite, // 修正:提供读取函数(虽然写入模式下不会使用)TiffMemoryWrite,TiffMemorySeek,TiffMemoryClose,TiffMemorySize,TiffMemoryMap,TiffMemoryUnmap);if (!tif) {return false;}auto tif_closer = [](TIFF* t) { TIFFClose(t); };std::unique_ptr<TIFF, decltype(tif_closer)> tif_managed(tif, tif_closer);// 设置TIFF标签TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width);TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height);TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, channels);TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);TIFFSetField(tif, TIFFTAG_COMPRESSION, static_cast<uint16_t>(compression));// 设置光度解释uint16_t photometric;if (compression == TiffCompression::JPEG) {if (format == imageinfo::Format::GRAY) {photometric = PHOTOMETRIC_MINISBLACK;}else {photometric = PHOTOMETRIC_YCBCR;}}else {photometric = static_cast<uint16_t>(Impl::FormatToPhotometric(format));}TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric);// 对于RGBA设置额外的标签if (format == imageinfo::Format::RGBA) {uint16_t extra_samples = 1;uint16_t sample_info = EXTRASAMPLES_ASSOCALPHA;TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, extra_samples, &sample_info);}// 设置JPEG质量(仅当压缩为JPEG时)if (compression == TiffCompression::JPEG) {TIFFSetField(tif, TIFFTAG_JPEGQUALITY, quality);// 设置YCBCR子采样,默认使用2x2if (format == imageinfo::Format::RGB) {TIFFSetField(tif, TIFFTAG_YCBCRSUBSAMPLING, 2, 2);}}// 设置行大小tmsize_t scanline_size = TIFFScanlineSize(tif);if (scanline_size == 0) {return false;}// 写入扫描线数据for (int row = 0; row < height; ++row) {const uint8_t* row_data = pixel_data.data() + row * width * channels;if (TIFFWriteScanline(tif, const_cast<uint8_t*>(row_data), row, 0) < 0) {return false;}}// 在TIFFClose时,数据会被刷新到内存缓冲区return true;
}bool TiffManager::GetInfo(const std::vector<uint8_t>& tiff_data,ImageInfo& info_out)
{if (!IsValidTIFF(tiff_data)) {return false;}// 设置内存IOTiffMemoryIO memio;memio.data = tiff_data.data();memio.size = static_cast<tmsize_t>(tiff_data.size());memio.pos = 0;// 修正:使用正确的读取函数指针TIFF* tif = TIFFClientOpen("memory", "r",&memio,TiffMemoryRead,TiffMemoryReadForWrite, // 修正:提供写入函数TiffMemorySeekForRead, // 修正:使用读取专用的定位函数TiffMemoryCloseForRead, // 修正:使用读取专用的关闭函数TiffMemorySizeForRead, // 修正:使用读取专用的大小函数TiffMemoryMapForRead, // 修正:使用读取专用的映射函数TiffMemoryUnmapForRead // 修正:使用读取专用的取消映射函数);if (!tif) {return false;}auto tif_closer = [](TIFF* t) { TIFFClose(t); };std::unique_ptr<TIFF, decltype(tif_closer)> tif_managed(tif, tif_closer);info_out.image_data_ = tiff_data;return impl_->ReadTIFFInfo(tif, info_out);
}bool TiffManager::IsValidTIFF(const std::vector<uint8_t>& data) {if (data.size() < 4) {return false;}// 检查TIFF文件头// TIFF文件头可以是"II"(小端)或"MM"(大端)return (data[0] == 0x49 && data[1] == 0x49 && data[2] == 0x2A && data[3] == 0x00) ||(data[0] == 0x4D && data[1] == 0x4D && data[2] == 0x00 && data[3] == 0x2A);
}int TiffManager::GetChannelsCount(imageinfo::Format format) {switch (format) {case imageinfo::Format::GRAY: return 1;case imageinfo::Format::RGB: return 3;case imageinfo::Format::RGBA: return 4;default: return 3;}
}std::vector<uint8_t> TiffManager::ConvertRGBToFormat(const uint8_t* rgb_data,int width,int height,imageinfo::Format target_format)
{const int pixel_count = width * height;const int output_channels = GetChannelsCount(target_format);std::vector<uint8_t> result(pixel_count * output_channels);switch (target_format) {case imageinfo::Format::RGB:memcpy(result.data(), rgb_data, pixel_count * 3);break;case imageinfo::Format::RGBA:for (int i = 0; i < pixel_count; ++i) {result[i * 4] = rgb_data[i * 3]; // Rresult[i * 4 + 1] = rgb_data[i * 3 + 1]; // Gresult[i * 4 + 2] = rgb_data[i * 3 + 2]; // Bresult[i * 4 + 3] = 0xFF; // A}break;case imageinfo::Format::GRAY:for (int i = 0; i < pixel_count; ++i) {result[i] = static_cast<uint8_t>((rgb_data[i * 3] * 299 +rgb_data[i * 3 + 1] * 587 +rgb_data[i * 3 + 2] * 114) / 1000);}break;default:result.clear();break;}return result;
}