Accessing float32 pixels from tiff or tiff->RGB-raw in pure C language

I used tiffdump to get the bitmap offset from a float tiff I wrote from developing a Nikon .nef file in RawTherapee 5.9. I expected to be able to fseek past the headed, read the file from there into memory into a float array. The numbers are all mangled and not in the [0.0 … 1.0] range.

I converted the .tif to a .rgb with ImageMagick and then converted the .raw to a .jpg to make sure it worked. The .jpg looks perfect.

Is there something else you have to do other than read the RGB bytes 12 at a time into a float32 array in memory to look at the values? I expect to see the same values shown in RT, values in the [0.0 … 1.0] range. Surely, they are in the IEEE 754 floating point format used by the C language.

Does anybody have a simple C program which reads a single, 12 byte, RGB float pixel from either a tif or an RGB-raw image and prints out the values? Does anybody see the problem in this code?

    // =============================================================================
    // 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);
    }

Running the program:

    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


The RawTherapee values shown at pixel (0, 0) are (22.7%, 31.0% and 0%).

Mainly, you can’t just union same-sized floats and integers to produce equivalent values, you need to do a type-cast assignment.

Also keep in mind: Tiffs can be generated as little-endian on intel or big-endian on mac. The first two bytes tell you II for intel and MM for mac.

Not mac. (Well, not mac of today at least.) “MM” is for Motorola.

Anyhow, this, and casting is one thing.

Maybe there is compression as well?

The offset also looks weird, it’s usually even (word boundary). You get the offset to the data by inspecting the StripeOffset or TileOffset. But, there can be more than one stripe/tile, and it’s not guaranteed they will be stored back to back as one contiguous stream.

Finally, why are you writing your own TIFF parser? Just use libtiff.

2 Likes

Hiram,
The floats aren’t mangled by intel’s braindead byte swapping, just ints of 2+ bytes. I will never buy another cpu with this defect as long as I live.

Plus, I am reading them from a (void *) by the byte and stuffing directly into a float array.

I just happened to overlay the float array with a uint32 array so I could easily print them in hex and see the bit patterns.

I don’t see any possibility of byte swapping here.

B

KMilos,
There is no compression. There is an option in RawTherapee for uncompressed and I used it.

I converted the tif to rgb with ImageMagick and the IM file is exactly $xres * $yres * 12 bytes long. Then, I converted the .rgb file to a .jpg and it looked perfect so I am confident that the .rgb is correct.

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 file sizes of the tif and the rgb are 434785284 and 434771040 for a difference of 14244. bytes. After allowing for the 13,022 byte tif header that still leaves
add 14244 -13022 → 1222 bytes left over at the end.

With the tiff spaghetti labyrinth file mal-format, there is no telling what it could be. Part of the data I am looking for could be here.

Exiftool shows the header and bitmap sizes. This is all I am trying to decode:
StripByteCounts : 434771040
StripOffsets : 13022 << Scalar,not array

tiffdump pf-274887.edge.f32.tif agrees:
StripOffsets (273) LONG (4) 1<13022>
StripByteCounts (279) LONG (4) 1<434771040>

I am seeking to byte [13022] and then dumping that memory directly into a float array. The values are not in the [0.0 … 1.0] range and look nothing like the RT gui shows when you put the mouse on pixel (0, 0).

I may need to call in Shaggy and Scoob on this case!

B

Glenn,
I read the memory after header offset 13022 into a float array which just happened to overlay the uint32 array. This way, I could easily look at the bit fields for the floats to see why they were so far out of the [0.0 … 1.0] expected range.

I wrote a Perl tool to decode the floats I read from the rgb file:

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

The blue float value is >> B 35650027520.000000 <<
The uint32 of the same bit is: >> UINT32: B 1359269508 <<

Putting this decimal value in my KCalc programmer’s calculator, I get this binary:
0b1010001000001001100111010000100

My secret binary to floater decoder ring:
bb.pl 0b1010001000001001100111010000100
prepended 1 zeros, length 31 → 32
Sign bit = 0 → ‘+’ << bit[0]
B2D: 0b10100010 → 162 decimal << Reading the exponent bits [1 … 8]
Fexp = 35 = raw 162 - exp_bias 127 << Applying -127 “bias” to calc exponent
CBF: 8 1s found in bits23, fraction = 0.03755236
<< The significand is in bits [9 … 31], [9] → 1/2, [10] → 1/4, etc.
+1.03755236E+35

The final value should be +1.03755236E+35, but it is not.

I wonder whether the floats are stored in some format other than IEEE 754.

B

I got the stripeOffset from both ExifTool and tiffdump and it is 130022 bytes.
They both also agree on the StripByteCounts : 434771040

mult 434771040 /4916 /7370 /3 = 4
The bitmap size divided by the XY resolution and number of channels gives 4 byte quanta. There is only one stripe.

And, I am reading the first pixel.

There are exactly 36,230,920 float pixels in 434,771,040 bytes of raw data. It should not be hard to extract them directly. I suspect my C code is of by a byte somewhere, but I can’t find it. Some ui64 truncated to 32 bits or a 64 bit read reading the garbage after a 32 bit variable. Or an array overwriting the next variable in your memory segment (so no segv). Haven’t you seen this type of bug a million times? Time for Valgrind to seek out memory overwrites!

Just use libtiff <<
I could go to Red Lobster and order the Salmon if I just wanted Salmon.
I want to know the guts of my data and catch the Salmon. :slight_smile:

Of course they are.

1 Like

They are not, as looking at the SampleFormat tag would tell you.

Without all the TIFF tags shared, all I can say is, good luck fishing! :wink:

KMilos,
I never suspected intel had degenerated the floats too.
I did a uint32 swap on the float/uint32 union and voila:

pf-274887.edge.f32.tif: Seek → 13022, Read 3: [ Pixel (0, 0) ]
Bich swapped: float: R 0.316480, G 0.307325, B -0.073858 →
RawTherapee gui reads pixel (0, 0) = 31.4%, 30.6%, 0%)

Try pixel (3, 3):
Img_xy=(3, 3) → offset_bytes = 177012
pf-274887.edge.f32.tif: Seek → 190034, Read 3:
Bich swapped: float: R 0.230569, G 0.309901, B 0.453831
RawTherapee gui reads pixel (3, 3) = 22.7%, 31.0%, 45.1%)

They are not “quite” right. The tif red is 0.316480 but the RawTherapee reads 31.4%.
And the only really bad one (so far) is the tif blue reading -0.073858 where the GUI reads 0.0%. Maybe the GUI has a hard bottom at 0.0%?

Could they be employing a Gamma function?

The raw-RGB file is still just as mangled with and without swapping and it should be the simple one! Darn.

This explains a lot! Thank you!

B

In TIFF files, floating-point pixel values are typically in the range [0,1].

In ImageMagick RGB files, floating-point [EDIT and integer] pixel values are typically in the range [0,QuantumRange] where QuantumRange is 255 if your IM is Q8, otherwise 65535.

EDIT Correction: 255 for 8-bit values, 65535 for 16-bit values, and 2**32-1 = 4294967295 for 32-bit numbers.

Does that explain the problem?

1 Like

Alan,

QuantumRange is 255 if your IM is Q8, otherwise 65535.
Does that explain the problem?

Her is my test results on the RGB file:
float.pixel.test (with printf %f replaced with %g)

pf-274887.edge.f32.tif.rgb: Seek → 0, Read 3:
float: R 35650027520.000000, G 1450022528.000000, B 0.000000 →
UINT32: R 1359269508, G 1319951149, B 0
Bich swapped:
float: R -4.84343e-36, G 2.49739e-11, G 0 →
UINT32: R 2228094033, G 769371214, B 0

The RGB file appears to be OK because the JPG made from it looks perfect:
convert -size 4916x7370 -depth 32 RGB:pf-274887.edge.f32.tif.rgb
pf-274887.edge.f32.jpg

Checking the jpg with RawTherapee:
(0, 0) = (32.2%, 29.0%, 0%); (3, 3) = (23.1%, 31.0%, 44.7%); // JPG
(0, 0) = (22.7%, 31.0%, 0%); (3, 3) = (22.7%, 31.0%, 45.1%); // TIF

cat_bin_bytes pf-274887.edge.f32.tif.rgb 0 12 > rgb.0-11.dat
FSeek to 0, cat 12 bytes

vim rgb.0-11.dat and convert to hex shows:
00000000: 84ce 0451 2ddb ac4e 0000 0000 0a

“84ce 0451” → 2228094033 → 0b10000100110011100000010001010001

Calculate the float from the 32 bits:

bb.pl 0b10000100110011100000010001010001  // "84ce 0451"
Sign bit = 1 -> '-'
B2D: 0b00001001 -> 9 decimal
Fexp = -118 = raw 9 - exp_bias 127
CBF: 8 1s found in bits[23](10011100000010001010001), fraction = 0.60950673
-1.60950673E-118  <- does not match the Red -4.84343e-36 above

Bit[0] → sign
bits[1 … 8] -127 = exponent
bits[9 … 31] → fraction, [9]==1 => +=1/2, [10]==1 => +=1/4, [11]==1 => +=1/8 …

To test the fraction part code, I pass in 23 1 bits and it returns 0.99999988

From wikevilpedia:
“The exponent is an 8-bit unsigned integer from 0 to 255, in biased form: an exponent value of 127 represents the actual zero.”

0b10000100110011100000010001010001
  SEEEEEEEEF->fraction
   00001001 -> 0b1001=9, 9-127 = -118; printf %g shows exp = -36

printf does not appear to be printing to the IEEE 754 float standard.
Did I break printf?

Above, you reported:

pf-274887.edge.f32.tif: Seek → 13022, Read 3: [ Pixel (0, 0) ]
Bich swapped: float: R 0.316480, G 0.307325, B -0.073858 →
RawTherapee gui reads pixel (0, 0) = 31.4%, 30.6%, 0%)

And your hex RGB values are:

84ce0451 2ddbac4e 00000000

When creating the RGB file, you didn’t use "-define quantum:format=floating-point ", so those values are 32-bit integers, on a scale of 0 to 2**32-1. (See my edit to my previous post.) The first byte is the least significant. (Note that you could use “-endian MSB” or “-endian LSB” when writing to the RGB: file.)

I reverse the bytes for the calculation:

Red is:

C:\web\im>numBinHex 0x5104ce84

decimal: 1359269508
 binary: 01010001000001001100111010000100
    hex: 0x5104ce84

C:\web\im>ccalc = 1359269508 / (2**32-1) = exit
0.3164795945204048

This is within spitting distance of your result 0.316480.

Green is:

C:\web\im>numBinHex 0x4eacdb2d

decimal: 1319951149
 binary: 01001110101011001101101100101101
    hex: 0x4eacdb2d

C:\web\im>ccalc = 1319951149 / (2**32-1) = exit
0.3073250756848895

This is within spitting distance of your result 0.307325.

Blue is zero. Integer RGB formats can’t record negative numbers.

Note that numBinHex and ccalc are my home-grown tools. Doubtless your O/S has similar tools such as awk and bc, or you could do the calculations in your program.

EDIT: I think you reversed the bytes to get the hex values 84ce0451 2ddbac4e 00000000. I have reversed them again, probably giving the original values you read from the RGB file.

2 Likes

This is the way. (or, for those looking for Python, tifffile)

Did I miss an answer to that question being provided? I fully agree, for something like this, tiffile or libtiff (depending on your language) are the way to go.