C语言读取TIFF/RGB raw图像32位浮点像素值异常问题
问题概述
我尝试读取RawTherapee生成的32位浮点TIFF文件中的像素值,GIMP可正常打开该文件。通过ExifTool和tiffdump获取到核心信息:
- TIFF头大小:13022字节
- 图像尺寸:宽4916、高7370
- 位图数据大小:434771040字节
测试操作与异常结果
我编写了C程序测试读取(0,0)像素,步骤如下:
- 以二进制模式打开文件
- 跳过13022字节的TIFF头
- 读取第一个像素的3个浮点值(共12字节)
- 用
%f格式打印结果
RawTherapee中显示该像素RGB值为(22.7%, 31.0%, 0%),对应浮点值应处于[0.0, 1.0]范围内,但程序输出的是随机数。
随后用ImageMagick将TIFF转换为raw RGB位图,执行相同读取步骤(无需跳过头部),结果仍为随机数,且与TIFF文件的读取结果不匹配。但将该RGB文件转成JPG后显示正常,说明RGB文件本身数据完好。
文件验证信息
该浮点TIFF仅包含1个strip,StripByteCounts为标量而非数组,其值与预期的RGB数据大小(宽×高×12字节/像素)完全一致,RGB文件大小也与该值匹配:
lsr -s pf-274887.edge.f32.tif.rgb -> 434771040 mult 4916 7370 12 434771040
ExifTool输出的关键字段:
exiftool -s -all pf-274887.edge.f32.tif | sort | egrep "^ImageSize|^Strip|SampleFormat" ImageSize : 4916x7370 SampleFormat : Float; Float; Float StripByteCounts : 434771040 StripOffsets : 13022 << Scalar,not array
tiffdump输出的关键字段:
tiffdump pf-274887.edge.f32.tif | egrep "BitsPerSample|ImageLength|ImageWidth|^Strip" ImageWidth (256) SHORT (3) 1<4916> ImageLength (257) SHORT (3) 1<7370> BitsPerSample (258) SHORT (3) 3<32 32 32> StripOffsets (273) LONG (4) 1<13022> StripByteCounts (279) LONG (4) 1<434771040>
程序运行结果
---------- Running: float.pixel.test pf-274887.edge.f32.tif: Seek -> 13022, Read 3: float: B -0.000000, G -0.000003, R 0.000000 -> UINT32: B 2634654270, G 3059326270, R 457414589 pf-274887.edge.f32.tif.rgb: Seek -> 0, Read 3: float: B 35650027520.000000, G 1450022528.000000, R 0.000000 -> UINT32: B 1359269508, G 1319951149, R 0 ---- IMAGEMAGICK CONVERSION: ------ convert pf-274887.edge.f32.tif -depth 32 RGB:pf-274887.edge.f32.tif.rgb convert -size 4916x7370 -depth 32 RGB:pf-274887.edge.f32.tif.rgb pf-274887.edge.f32.jpg The .JPG looks perfect. SIZE CHECK: ------ lsr -s pf-274887.edge.f32.tif.rgb -> 434771040 mult 4916 7370 12 434771040
排查情况与需求
我原本认为读取无TIFF结构的RGB文件能得到合理结果,且浮点值采用与内存一致的IEEE 754格式。我的目标是掌握手动读取的方法,而非仅获取当前案例的像素值,能正确读取RGB文件是关键进展。
我尝试读取(0,0)和(3,3)像素,得到的浮点值均不在[0.0,1.0]范围内,且TIFF与RGB文件的原始值不匹配。我已确保所有seek和fread操作使用size_t类型,检查了fread的读取数量,验证了浮点值并非NaN,也简化了测试程序排除多余操作,但问题依旧。有人建议使用libtiff,但这无法解决RGB文件的读取问题,我希望手动实现解析逻辑。
测试代码
// ============================================================================= // Read and decode a specific pixel in a float32 tiff /* gcc -g -O0 -m64 bln/float.pixel.test.c -lm -lz -o bln/float.pixel.test */ #include "stdio.h" #include "stdlib.h" #include "stdint.h" #include <math.h> int osrp(char *ifn, size_t soff, size_t numf); // Open, Seek, Read, Print // ============================================================================= void main(int argc, char *argv[]) { char tfn[]={ "pf-274887.edge.f32.tif" }; // From RawTherapee char rfn[]={ "pf-274887.edge.f32.tif.rgb" }; // From ImageMagick size_t toff=13022ll, roff=0, pixoff=0; // Tif and raw-RGB header Offsets int imgx=3, imgy=3, imgw=4916, pix_bytes=12; pixoff = (imgy * imgw + imgx) * pix_bytes; // Byte offet to pixel (3,3) pixoff = 0ll; // Try pixel (0, 0) osrp(tfn, toff+pixoff, 3); // Open, seek, read, print osrp(rfn, roff+pixoff, 3); // FileName, seek_OFFset, #_floats per pixel exit(0); // } // End Main(). // ============================================================================= // ============================================================================= // Open, seek, read, print. Expect floats in (0.0 .. 1.0) range // Check for errors in number of floats read and NAN on their values int osrp(char *ifn, size_t soff, size_t numf) { union { // Overlay floats and 32 bit unsigned ints float fpix[4]; // Holds 1 pixel uint32_t ipix[4]; // UI32 and float share same bits } fiu; // Float-Int-Union FILE *ifp; // Image_File_Pointer int ii, numnan=0; // Number of NANs, Not_A_Numbers size_t numr; // uint64_t ifp = fopen(ifn, "rb"); // Read Image in Binary mode fseek(ifp, soff, SEEK_SET); // Seek to start of bitmap data // Read numf * 4 bytes from bitmap, store in uint/float union numr = fread((void *)fiu.fpix, sizeof(float), numf, ifp); // 3*4=12B fclose(ifp); if(numr != numf) { printf("ERROR! NumR %llu != NumF %llu\n", numr, numf); } printf("%s: Seek -> %lld, Read %llu: \nfloat: B %8.6f, G %8.6f, R %8.6f -> \nUINT32: B %10u, G %10u, R %10u\n", ifn, soff, numf, fiu.fpix[0], fiu.fpix[1], fiu.fpix[2], fiu.ipix[0], fiu.ipix[1], fiu.ipix[2]); for(ii=0; ii < numf; ii++) { // Check for Non-Number floats if(isnan(fiu.fpix[ii])) { numnan++; } // Count NANs } if(numnan == 0) { printf("\n"); return(0); } printf("%d floats, %d are NANs!\n\n", numf, numnan); return(numnan); }
内容的提问来源于stack exchange,提问作者Brian

