如何使用FreeImage对64位浮点原始图像执行色调映射?
问题
我有一个64位浮点原始二进制图像文件,想通过FreeImage进行色调映射。查过FreeImage文档后知道,FreeImage_ConvertFromRawBits函数可以把内存中的图像转成FreeImage格式,但它不支持64位浮点图像。我当前的代码运行后得到了扫描线扭曲的错误图像,请问处理这类图像进行色调映射的正确方法是什么?
我的尝试代码:
#include <iostream> #include <stdlib.h> #include <fstream> #include <vector> #include "FreeImage.h" int main() { std::ifstream stream("c:/output/15_init_image.bin", std::ios::in | std::ios::binary); std::vector<uint8_t> contents((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>()); int width = 2048; // 图像宽度 int height = 3096; // 图像高度 int bpp = 32; FreeImage_Initialise(); int pitch = 512; FIBITMAP* src = FreeImage_ConvertFromRawBitsEx(true, contents.data(), FIT_BITMAP, width, height, pitch, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, FALSE); FreeImage_Save(FIF_JPEG, src, "viewport.jpeg"); return 0; }
解决方案
你的代码存在几个核心问题:使用仅支持整数位图的FreeImage_ConvertFromRawBitsEx处理64位浮点数据、pitch值设置完全错误、未处理FreeImage扫描线的存储顺序,这些共同导致了图像扭曲。正确的处理流程需要先将浮点数据加载为FreeImage支持的浮点位图,再进行色调映射,最后转成8位位图保存。
具体步骤:
- 正确加载64位浮点数据:直接将二进制文件读取为
double类型数组,而非uint8_t,因为每个像素是64位(8字节)的浮点值。 - 创建FreeImage浮点位图:使用
FreeImage_AllocateT创建FIT_DOUBLE类型的位图(对应64位浮点单通道),指定正确的宽高和每像素位深(64)。 - 处理扫描线顺序:FreeImage默认将图像数据按从下到上的顺序存储,而原始二进制文件通常是从上到下的扫描线顺序,需要反转行数据修正。
- 执行色调映射:使用
FreeImage_ToneMapping将高动态范围的浮点图像转换为适合显示的8位低动态范围图像,可选择REINHARD02等常用算法。 - 保存最终图像:将色调映射后的8位位图保存为JPEG格式。
修正后的代码
#include <iostream> #include <fstream> #include <vector> #include <algorithm> #include "FreeImage.h" int main() { const int width = 2048; const int height = 3096; const int channels = 1; // 假设为单通道64位浮点图像,多通道需调整此值 const size_t pixel_size = sizeof(double); // 64位浮点每个像素占8字节 const size_t row_size = width * channels * pixel_size; // 读取64位浮点原始数据 std::ifstream stream("c:/output/15_init_image.bin", std::ios::in | std::ios::binary); std::vector<double> data(width * height * channels); stream.read(reinterpret_cast<char*>(data.data()), data.size() * pixel_size); if (!stream) { std::cerr << "读取二进制文件失败" << std::endl; return 1; } FreeImage_Initialise(); // 创建64位浮点单通道位图 FIBITMAP* float_img = FreeImage_AllocateT(FIT_DOUBLE, width, height, 64, 0, 0, 0); if (!float_img) { std::cerr << "创建浮点位图失败" << std::endl; FreeImage_DeInitialise(); return 1; } // 复制数据并修正扫描线顺序(FreeImage从下到上存储) for (int y = 0; y < height; ++y) { double* src_row = data.data() + y * width * channels; double* dst_row = reinterpret_cast<double*>(FreeImage_GetScanLine(float_img, height - 1 - y)); std::copy(src_row, src_row + width * channels, dst_row); } // 执行色调映射(使用Reinhard02算法) FIBITMAP* ldr_img = FreeImage_ToneMapping(float_img, FITMO_REINHARD02); if (!ldr_img) { std::cerr << "色调映射失败" << std::endl; FreeImage_Unload(float_img); FreeImage_DeInitialise(); return 1; } // 保存为JPEG(高画质) if (!FreeImage_Save(FIF_JPEG, ldr_img, "viewport.jpeg", JPEG_QUALITYSUPERB)) { std::cerr << "保存图像失败" << std::endl; } // 释放资源 FreeImage_Unload(float_img); FreeImage_Unload(ldr_img); FreeImage_DeInitialise(); return 0; }
关键说明:
- 如果你的图像是多通道(比如RGBA各64位),需要调整
channels值,并修改FreeImage_AllocateT的最后三个通道掩码参数。 pitch在使用FreeImage_GetScanLine时会自动处理,无需手动设置。- 色调映射算法可根据需求替换为
FITMO_DRAKE、FITMO_REINHARD05等其他选项。
内容的提问来源于stack exchange,提问作者Andre Ahmed
相关产品推荐
相关产品推荐

