Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

having trouble using set #133

Closed
kayarre opened this issue Nov 9, 2019 · 6 comments
Closed

having trouble using set #133

kayarre opened this issue Nov 9, 2019 · 6 comments
Labels

Comments

@kayarre
Copy link

kayarre commented Nov 9, 2019

using pyvips 2.1.8

I am doing the simple operation from a loaded .svs file

test.set("xres", 0.2525)

but when I type:

test.get("xres")

it returns 1.0

is there something I am missing?

I am planning to write the output to a simple tiff format but I am having a hard time figuring out how to include the meta data with the file.

pyvips is a great tool thank you!

After looking through the documentation there appears that the image metadata is immutable, so what is the point of the set method if can't do what a set method normally does?

say I want to add a bunch of different items as metadata?

would I do something like this

new_image = image.copy().set('icc-profile-data', new_profile).set('xres', 0.2525)

or could I do a for loop, probably need check to see if that object exists

for key, meta in meta_dict.items():
    new = new.copy().set(key, meta)
@jcupitt
Copy link
Member

jcupitt commented Nov 10, 2019

Hello @kayarre,

It's a bit confusing -- libvips has two different types of image metadata. Fields like width and xres are built in, and are properties of the underlying GObject. You can read them like this:

$ python3
Python 3.7.5rc1 (default, Oct  8 2019, 16:47:45) 
[GCC 9.2.1 20191008] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyvips
>>> x = pyvips.Image.new_from_file("k2.jpg")
>>> x.width
1450
>>> x.xres
0.9999999999068677

There are 10 or so of these core properties. They are immutable and you can only set them with .copy() like this:

>>> y = x.copy(xres=12)
>>> y.xres
12.0
>>> x.xres
0.9999999999068677

There's a full list here:

https://libvips.github.io/pyvips/vimage.html#pyvips.Image.copy

All other image metadata is implemented as a set of name-value pairs in a hash table attached to each image. These are very flexible: you can add any type of data. You can see everything attached to an image with .get_fields():

>>> x.get_fields()
['width', 'height', 'bands', 'format', 'coding', 'interpretation', 'xoffset', 
'yoffset', 'xres', 'yres', 'filename', 'vips-loader', 'jpeg-multiscan', 
'jpeg-chroma-subsample', 'exif-data', 'resolution-unit', 'exif-ifd0-Orientation', 
'exif-ifd0-XResolution', 'exif-ifd0-YResolution', 'exif-ifd0-ResolutionUnit', 
'exif-ifd0-YCbCrPositioning', 'exif-ifd2-ExifVersion', 
'exif-ifd2-ComponentsConfiguration', 
'exif-ifd2-FlashPixVersion', 'exif-ifd2-ColorSpace', 'exif-ifd2-PixelXDimension', 
'exif-ifd2-PixelYDimension', 'orientation']

The 10 built-in fields are listed, but after that there are a lot of others. These extra fields can be changed with get and set.

>>> x.get('orientation')
1
>>> x.set('orientation', 99)
>>> x.get('orientation')
99

But be careful changing them --- I would copy the image first, or you may have problems with caching.

You can make new fields too:

>>> x.set_type(pyvips.GValue.guint64_type, 'banana', 12)
>>> x.get('banana')
12

Makes a new 64-bit int field.

@kayarre
Copy link
Author

kayarre commented Nov 10, 2019

Thank you @jcupitt
I was actually in the midst of adding an update. Thank you it is much clearer now.

I figured out essentially what you described. The core parts are immutable but any of the others there is no issue.

I can do this:

test = invert.copy(xres = 1000.0/(float(im.get("openslide.mpp-x"))),
                                 yres = 1000.0/(float(im.get("openslide.mpp-y"))),
                       )

and it gives the correct thing.

one weird thing though is that when I add this metadata
test.set_type(pyvips.GValue.gstr_type, "resolution-unit", "mm")

and then save to tiff file i.e.

test.tiffsave("test2.tif", compression = "VIPS_FOREIGN_TIFF_COMPRESSION_DEFLATE",
properties=True, strip=False)

vipsheader gives me this

$ vipsheader -a test2.tif 
test2.tif: 16384x16384 uchar, 1 band, b-w, tiffload
width: 16384
height: 16384
bands: 1
format: uchar
coding: none
interpretation: b-w
xoffset: 0
yoffset: 0
xres: 3960.43
yres: 3960.43
filename: test2.tif
vips-loader: tiffload
n-pages: 1
image-description: <?xml version="1.0"?>
<image xmlns="http://www.vips.ecs.soton.ac.uk//dzsave" date="2019-11-10T03:25:56.958661Z" version="8.8.1">
  <properties>
    <property>
      <name>width</name>
      <value type="gint">16384</value>
    </property>
    <property...
resolution-unit: cm
orientation: 1

Why does it show resolution-unit: cm ?

the answer is that tiffsave has a flag resunit and can be either "cm" or "in"

is there reason "mm" couldn't be added to the enum?

Another Question is that it appears that none of the meta data gets written out?

jcupitt added a commit to libvips/libvips that referenced this issue Nov 10, 2019
resolution-unit metadata was not being checked correctly on tiff save,
thanks @kayarre

see libvips/pyvips#133
@jcupitt
Copy link
Member

jcupitt commented Nov 10, 2019

Oh no! I think you've found a bug. I pushed a fix, and I now see:

$ python3
Python 3.7.5rc1 (default, Oct  8 2019, 16:47:45) 
[GCC 9.2.1 20191008] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyvips
>>> x = pyvips.Image.new_from_file("k2.tif")
>>> x.get("resolution-unit")
'cm'
>>> x.set("resolution-unit", "in")
>>> x.write_to_file("x.tif")
>>> 
$ tiffinfo x.tif
TIFF Directory at offset 0x87f008 (8908808)
  Image Width: 1450 Image Length: 2048
  Resolution: 71.9988, 71.9988 pixels/inch
  Bits/Sample: 8
  Sample Format: unsigned integer
  Compression Scheme: None
  Photometric Interpretation: RGB color
  Orientation: row 0 top, col 0 lhs
  Samples/Pixel: 3
  Rows/Strip: 128
  Planar Configuration: single image plane

Thanks for pointing out this dumbness.

@jcupitt
Copy link
Member

jcupitt commented Nov 10, 2019

As a workaround, you can set the res unit from the args to tiffsave:

x.write_to_file("x.tif", resunit="inch")

@tamkaho
Copy link

tamkaho commented Feb 19, 2020

Oh no! I think you've found a bug. I pushed a fix, and I now see:

$ python3
Python 3.7.5rc1 (default, Oct  8 2019, 16:47:45) 
[GCC 9.2.1 20191008] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyvips
>>> x = pyvips.Image.new_from_file("k2.tif")
>>> x.get("resolution-unit")
'cm'
>>> x.set("resolution-unit", "in")
>>> x.write_to_file("x.tif")
>>> 
$ tiffinfo x.tif
TIFF Directory at offset 0x87f008 (8908808)
  Image Width: 1450 Image Length: 2048
  Resolution: 71.9988, 71.9988 pixels/inch
  Bits/Sample: 8
  Sample Format: unsigned integer
  Compression Scheme: None
  Photometric Interpretation: RGB color
  Orientation: row 0 top, col 0 lhs
  Samples/Pixel: 3
  Rows/Strip: 128
  Planar Configuration: single image plane

Thanks for pointing out this dumbness.

In which version has set_type been fixed?

@jcupitt
Copy link
Member

jcupitt commented Feb 19, 2020

It's in libvips 8.9.0 onwards.

@jcupitt jcupitt closed this as completed Feb 19, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants