当前位置: 首页 > news >正文

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;
}
http://www.dtcms.com/a/461810.html

相关文章:

  • Android 性能优化 — Profiler 使用指南
  • DynamoDB 到 Redshift Zero-ETL 集成:完整实施指南
  • 长沙软件公司排行-专业软件开发公司
  • 深圳网站建设电话wordpress linux 伪静态
  • 缺少需求评审会导致哪些严重后果?
  • 176.在vue3中使用OpenLayers实现上传 CSV 文件并导出为 GeoJSON
  • 时钟服务器配置
  • 中国建设教育网官方网站二级网站怎么做
  • vue3和uniapp的生命周期
  • uniapp 防止长表单数据丢失方案,缓存表单填写内容,放置卡退或误操作返回。
  • uniapp | 图片上传的两种实现方式(传统VS组件)
  • Android NDK 命令规范
  • C语言 分支结构(2)
  • 哪个做app的网站好排版设计技巧
  • 如何鉴赏网站论文wordpress静态nginx规则
  • 数据库存储中的哈希表和B+树
  • 绵阳网站推广优化和田知名网站建设企业
  • MQTT 协议应用指导
  • 蘑菇采摘公司:Mycionics
  • billfish本地资源库占内存吗
  • 深度残差网络(ResNet)
  • 专题五:位运算~
  • C++语言编程规范-资源分配和释放
  • 影视广告网站重庆网站建设制作
  • Hadess入门到实战(9) - 如何管理Composer(PHP)制品
  • 如何设计公司官网站苏宁易购网站风格
  • wx小程序扫码入口方式
  • Agent 开发设计模式(Agentic Design Patterns )第 1 章:提示词链
  • asp美食网站源码天津网站推广
  • 图像处理踩坑:浮点数误差导致的缩放尺寸异常与解决办法