pyvips icon indicating copy to clipboard operation
pyvips copied to clipboard

'gdouble' is invalid or out of range for property 'xres'

Open karray opened this issue 3 years ago • 7 comments

When working with medical slides, the resolution is often specified in mm or even nm scale. resunit only accepts cm and inches, so by creating a WSI using tiffsave the values becmoe very small:

slide.tiffsave('slide.tiff', compression='jpeg', Q=80, tile=True, tile_width=128, tile_height=128, pyramid=True, depth=3,
               xres=0.00002333, yres=0.00002333, resunit='cm')

The values fall back to 28.3465 pixels/cm with a warning:

GLib-GObject-WARNING **: 17:22:50.793: value "0.000023" of type 'gdouble' is invalid or out of range for property 'xres' of type 'gdouble'

GLib-GObject-WARNING **: 17:22:50.793: value "0.000023" of type 'gdouble' is invalid or out of range for property 'yres' of type 'gdouble'

karray avatar Oct 05 '22 15:10 karray

Hi @karray,

You're right, these values are sanity checked in the tiffsave API to be > 0.001. I'll change it, thanks for reporting this issue.

As a workaround, you can set the resolution on the image, rather than in the tiff saver. For example:

slide = slide.copy(xres=0.0002333, yres=0.0002333)
slide.tiffsave('x.tif', compression='jpeg', Q=80, tile=True, pyramid=True, depth=3, resunit='cm')

.copy() always works in pixels per millimetre.

https://www.libvips.org/API/current/libvips-conversion.html#vips-copy

jcupitt avatar Oct 05 '22 17:10 jcupitt

Thank you for the fast reply. I tried your workaround but the resolution was set with different values:

$ exiftool slide.tiff
...
X Resolution                    : 0.009999999776
Y Resolution                    : 0.009999999776
Resolution Unit                 : cm

but should be 0.00002333

Also, if I understand correctly, tiffinfo prints the metadata for each of the levels and the resolution on each of them remains the same. Shouldn't it be recalculated accordingly?

karray avatar Oct 06 '22 07:10 karray

Huh you're right, the tiff saver also clips the resolution:

        /* Don't write mad resolutions (eg. zero), it confuses some programs.
         */
        TIFFSetField( tif, TIFFTAG_RESOLUTIONUNIT, wtiff->resunit );
        TIFFSetField( tif, TIFFTAG_XRESOLUTION, VIPS_FCLIP( 0.01, wtiff->xres, 1000000 ) );
        TIFFSetField( tif, TIFFTAG_YRESOLUTION, VIPS_FCLIP( 0.01, wtiff->yres, 1000000 ) );

That'll need changing too. It looks like there's no easy workaround, unfortunately. We should make this change as a bugfix for 8.13.

The tiffsaver writes the same metadata for all pyramid levels, so you'll see the same resolution. Imagine if you were printing the pyramid levels next to each other: would you want the levels to all be printed as the same size on the paper, or would you like the smaller levels to be smaller? It depends.

There's no single correct answer, so libvips avoids automatic resolution changes.

jcupitt avatar Oct 06 '22 11:10 jcupitt

Thinking about this again after a bit more coffee, the xres and yres arguments to copy are in pixels/mm (like DPI). A WSI image might be 100,000 x 100,000 pixels and 2cm each way, so that's 5000 pixels/mm.

Are you perhaps thinking of mm/pixel? I know systems like DICOM use this inverse value.

jcupitt avatar Oct 07 '22 14:10 jcupitt

I use OpenSlide and according to the docs,

mm_x, mm_y - width and height of a pixel in millimeters at the base resolution.

so I think, you are right.

I created a WSI and set the resolution to 0.01 mm:

slide = slide.copy(xres=0.01, yres=0.01)
slide.tiffsave('slide.tiff', compression='jpeg', Q=80, tile=True, tile_width=128, tile_height=128, pyramid=True)

I checked it using exiftool

$ exiftool slide.tiff
...
X Resolution                    : 0.009999999776
Y Resolution                    : 0.009999999776
Resolution Unit                 : cm

and 0.1000000015 cm was converted to 99999998.5098839 nm (~10 cm) in my app:

pixel_size_nm': {
  'x': 99999998.5098839,
  'y': 99999998.5098839,
}

I'm also wondering why 0.01 mm was converted to 0.1000000015 cm (floating-point rounding error?). If I change resolution using exiftool -Xresolution=0.1 -Yresolution=0.1 slide.tiff, exiftool -v1 slide.tiff prints:

  | 7)  XResolution = 0.1 (1/10)
  | 8)  YResolution = 0.1 (1/10)

If using slide.copy:

  | 8)  XResolution = 0.1000000015 (13421773/134217728)
  | 9)  YResolution = 0.1000000015 (13421773/134217728)

karray avatar Oct 10 '22 10:10 karray

So it sounds like everything is correct, is that right?

I'm also wondering why 0.01 mm was converted to 0.1000000015 cm (floating-point rounding error?).

Yes, a rounding error, I think. TIFF only uses a float (32 bits) to store resolution.

jcupitt avatar Oct 10 '22 12:10 jcupitt

Yes, I think I solved my problem. Your comment from another discussion helped me

karray avatar Oct 10 '22 15:10 karray