0
# Properties and Metadata
1
2
Access and manipulation of image properties, metadata, and introspection capabilities for understanding image characteristics and processing pipelines. PyVips provides comprehensive metadata support including EXIF, IPTC, XMP, ICC profiles, and custom properties.
3
4
## Capabilities
5
6
### Basic Image Properties
7
8
Access fundamental image characteristics that define the image structure.
9
10
```python { .api }
11
# Core dimensions
12
@property
13
def width(self) -> int:
14
"""Image width in pixels."""
15
16
@property
17
def height(self) -> int:
18
"""Image height in pixels."""
19
20
@property
21
def bands(self) -> int:
22
"""Number of bands (channels) in the image."""
23
24
# Format and interpretation
25
@property
26
def format(self) -> str:
27
"""
28
Pixel format.
29
Values: 'uchar', 'char', 'ushort', 'short', 'uint', 'int',
30
'float', 'complex', 'double', 'dpcomplex'
31
"""
32
33
@property
34
def interpretation(self) -> str:
35
"""
36
Color space interpretation.
37
Values: 'multiband', 'b-w', 'histogram', 'xyz', 'lab', 'cmyk',
38
'rgb', 'srgb', 'yxy', 'fourier', 'rgb16', 'grey16', etc.
39
"""
40
41
@property
42
def coding(self) -> str:
43
"""
44
Pixel coding method.
45
Values: 'none', 'labq', 'rad'
46
"""
47
48
# Resolution information
49
@property
50
def xres(self) -> float:
51
"""Horizontal resolution in pixels per unit."""
52
53
@property
54
def yres(self) -> float:
55
"""Vertical resolution in pixels per unit."""
56
57
@property
58
def xoffset(self) -> int:
59
"""Horizontal offset."""
60
61
@property
62
def yoffset(self) -> int:
63
"""Vertical offset."""
64
```
65
66
Example usage:
67
68
```python
69
# Basic image info
70
print(f"Dimensions: {image.width} x {image.height}")
71
print(f"Bands: {image.bands}")
72
print(f"Format: {image.format}")
73
print(f"Color space: {image.interpretation}")
74
print(f"Resolution: {image.xres} x {image.yres}")
75
76
# Check image characteristics
77
if image.bands == 1:
78
print("Grayscale image")
79
elif image.bands == 3:
80
print("RGB image")
81
elif image.bands == 4:
82
print("RGBA image")
83
84
# Format information
85
if image.format in ['uchar', 'char']:
86
print("8-bit image")
87
elif image.format in ['ushort', 'short']:
88
print("16-bit image")
89
elif image.format == 'float':
90
print("32-bit float image")
91
```
92
93
### Property Access Methods
94
95
Generic methods for getting and setting image properties and metadata.
96
97
```python { .api }
98
def get(self, name: str):
99
"""
100
Get property value.
101
102
Parameters:
103
- name: str, property name
104
105
Returns:
106
Property value (type varies by property)
107
"""
108
109
def set(self, name: str, value) -> None:
110
"""
111
Set property value.
112
113
Parameters:
114
- name: str, property name
115
- value: property value (type varies)
116
117
Returns:
118
None (modifies image metadata)
119
"""
120
121
def get_fields(self) -> list:
122
"""
123
Get list of all property names.
124
125
Returns:
126
List of property name strings
127
"""
128
129
def remove(self, name: str) -> None:
130
"""
131
Remove property.
132
133
Parameters:
134
- name: str, property name to remove
135
136
Returns:
137
None (modifies image metadata)
138
"""
139
140
def get_typeof(self, name: str) -> str:
141
"""
142
Get property type.
143
144
Parameters:
145
- name: str, property name
146
147
Returns:
148
str, GType name for the property
149
"""
150
151
def set_type(self, gtype: int, name: str, value) -> None:
152
"""
153
Set property with explicit type.
154
155
Parameters:
156
- gtype: int, GType for the property
157
- name: str, property name
158
- value: property value
159
160
Returns:
161
None (modifies image metadata)
162
"""
163
```
164
165
Example usage:
166
167
```python
168
# List all properties
169
fields = image.get_fields()
170
print(f"Available properties: {len(fields)}")
171
for field in fields[:10]: # Show first 10
172
print(f" {field}: {image.get(field)}")
173
174
# Get specific properties
175
try:
176
orientation = image.get('orientation')
177
print(f"Image orientation: {orientation}")
178
except:
179
print("No orientation metadata")
180
181
# Set custom metadata
182
image_copy = image.copy()
183
image_copy.set('custom-property', 'my value')
184
image_copy.set('processing-date', '2024-01-15')
185
186
# Remove metadata
187
image_copy.remove('exif-ifd0-Orientation')
188
189
# Check property types
190
if 'xres' in image.get_fields():
191
prop_type = image.get_typeof('xres')
192
print(f"xres type: {prop_type}")
193
```
194
195
### EXIF Metadata
196
197
Access and manipulate EXIF (Exchangeable Image File Format) metadata commonly found in digital photos.
198
199
```python { .api }
200
# Common EXIF properties (examples)
201
# Camera information
202
'exif-ifd0-Make' # Camera manufacturer
203
'exif-ifd0-Model' # Camera model
204
'exif-ifd0-Software' # Software used
205
'exif-ifd0-DateTime' # File modification date
206
'exif-ifd0-Orientation' # Image orientation (1-8)
207
208
# Photo settings
209
'exif-exif-ExposureTime' # Shutter speed
210
'exif-exif-FNumber' # Aperture (f-stop)
211
'exif-exif-ISO' # ISO sensitivity
212
'exif-exif-FocalLength' # Focal length
213
'exif-exif-Flash' # Flash settings
214
'exif-exif-WhiteBalance' # White balance setting
215
216
# GPS information
217
'exif-gps-GPSLatitude' # Latitude
218
'exif-gps-GPSLongitude' # Longitude
219
'exif-gps-GPSAltitude' # Altitude
220
'exif-gps-GPSTimeStamp' # GPS timestamp
221
```
222
223
Example usage:
224
225
```python
226
# Read EXIF data
227
def get_camera_info(image):
228
camera_info = {}
229
try:
230
camera_info['make'] = image.get('exif-ifd0-Make')
231
camera_info['model'] = image.get('exif-ifd0-Model')
232
camera_info['datetime'] = image.get('exif-ifd0-DateTime')
233
camera_info['orientation'] = image.get('exif-ifd0-Orientation')
234
except:
235
pass # Property not available
236
return camera_info
237
238
def get_photo_settings(image):
239
settings = {}
240
try:
241
settings['exposure'] = image.get('exif-exif-ExposureTime')
242
settings['aperture'] = image.get('exif-exif-FNumber')
243
settings['iso'] = image.get('exif-exif-ISO')
244
settings['focal_length'] = image.get('exif-exif-FocalLength')
245
except:
246
pass
247
return settings
248
249
# Use EXIF data
250
camera = get_camera_info(image)
251
if camera:
252
print(f"Camera: {camera.get('make', 'Unknown')} {camera.get('model', 'Unknown')}")
253
254
# Handle orientation
255
orientation = image.get('exif-ifd0-Orientation') if 'exif-ifd0-Orientation' in image.get_fields() else 1
256
if orientation in [3, 6, 8]: # Common rotation values
257
if orientation == 3:
258
image = image.rotate(180)
259
elif orientation == 6:
260
image = image.rotate(90)
261
elif orientation == 8:
262
image = image.rotate(270)
263
264
# Set EXIF data
265
image_copy = image.copy()
266
image_copy.set('exif-ifd0-Artist', 'Photographer Name')
267
image_copy.set('exif-ifd0-Copyright', '© 2024 My Company')
268
image_copy.set('exif-ifd0-Software', 'PyVips Processing')
269
```
270
271
### ICC Color Profiles
272
273
Access and manipulate ICC (International Color Consortium) color profiles for accurate color management.
274
275
```python { .api }
276
# ICC profile properties
277
'icc-profile-data' # Raw ICC profile data (bytes)
278
'icc-profile-description' # Profile description
279
'icc-profile-manufacturer' # Profile manufacturer
280
'icc-profile-model' # Profile model
281
'icc-profile-copyright' # Profile copyright
282
```
283
284
Example usage:
285
286
```python
287
# Check for ICC profile
288
has_profile = 'icc-profile-data' in image.get_fields()
289
if has_profile:
290
profile_data = image.get('icc-profile-data')
291
print(f"ICC profile size: {len(profile_data)} bytes")
292
293
# Get profile description
294
try:
295
description = image.get('icc-profile-description')
296
print(f"Profile: {description}")
297
except:
298
print("No profile description")
299
300
# Embed ICC profile
301
with open('srgb_profile.icc', 'rb') as f:
302
profile_data = f.read()
303
304
image_with_profile = image.copy()
305
image_with_profile.set('icc-profile-data', profile_data)
306
307
# Remove ICC profile
308
image_no_profile = image.copy()
309
if 'icc-profile-data' in image_no_profile.get_fields():
310
image_no_profile.remove('icc-profile-data')
311
```
312
313
### XMP and IPTC Metadata
314
315
Access XMP (Extensible Metadata Platform) and IPTC (International Press Telecommunications Council) metadata.
316
317
```python { .api }
318
# XMP properties (examples)
319
'xmp-dc-title' # Title
320
'xmp-dc-description' # Description
321
'xmp-dc-creator' # Creator
322
'xmp-dc-subject' # Keywords/subjects
323
'xmp-dc-rights' # Rights/copyright
324
325
# IPTC properties (examples)
326
'iptc-Application2-Caption' # Caption/description
327
'iptc-Application2-Keywords' # Keywords
328
'iptc-Application2-Byline' # Photographer
329
'iptc-Application2-Copyright' # Copyright notice
330
'iptc-Application2-City' # City
331
'iptc-Application2-Country' # Country
332
```
333
334
Example usage:
335
336
```python
337
# Read XMP metadata
338
def get_xmp_info(image):
339
xmp_info = {}
340
fields = image.get_fields()
341
342
xmp_fields = [f for f in fields if f.startswith('xmp-')]
343
for field in xmp_fields:
344
try:
345
xmp_info[field] = image.get(field)
346
except:
347
pass
348
return xmp_info
349
350
# Read IPTC metadata
351
def get_iptc_info(image):
352
iptc_info = {}
353
fields = image.get_fields()
354
355
iptc_fields = [f for f in fields if f.startswith('iptc-')]
356
for field in iptc_fields:
357
try:
358
iptc_info[field] = image.get(field)
359
except:
360
pass
361
return iptc_info
362
363
# Set metadata for web publishing
364
def add_web_metadata(image, title, description, keywords, author):
365
web_image = image.copy()
366
367
# XMP metadata
368
web_image.set('xmp-dc-title', title)
369
web_image.set('xmp-dc-description', description)
370
web_image.set('xmp-dc-creator', author)
371
web_image.set('xmp-dc-subject', keywords)
372
373
# IPTC metadata
374
web_image.set('iptc-Application2-Caption', description)
375
web_image.set('iptc-Application2-Keywords', keywords)
376
web_image.set('iptc-Application2-Byline', author)
377
378
return web_image
379
380
# Use metadata functions
381
xmp_data = get_xmp_info(image)
382
if xmp_data:
383
print("XMP metadata found:")
384
for key, value in xmp_data.items():
385
print(f" {key}: {value}")
386
387
# Add metadata to processed image
388
processed = add_web_metadata(
389
image,
390
"Sunset Landscape",
391
"Beautiful sunset over mountains",
392
"sunset, landscape, mountains, nature",
393
"John Photographer"
394
)
395
```
396
397
### Image Scale and Offset
398
399
Access scale and offset properties used for pixel value interpretation.
400
401
```python { .api }
402
def get_scale(self) -> float:
403
"""
404
Get scale property.
405
406
Returns:
407
float, scale factor applied to pixel values
408
"""
409
410
def get_offset(self) -> float:
411
"""
412
Get offset property.
413
414
Returns:
415
float, offset added to pixel values
416
"""
417
418
# Properties
419
'scale' # Scale factor for pixel values
420
'offset' # Offset for pixel values
421
```
422
423
Example usage:
424
425
```python
426
# Check scale and offset
427
scale = image.get_scale()
428
offset = image.get_offset()
429
print(f"Scale: {scale}, Offset: {offset}")
430
431
# Pixel values are interpreted as: (raw_value * scale) + offset
432
if scale != 1.0 or offset != 0.0:
433
print("Image has scale/offset transformation")
434
435
# Set scale and offset for calibrated images
436
calibrated = image.copy()
437
calibrated.set('scale', 2.5) # Each unit represents 2.5 real units
438
calibrated.set('offset', -10.0) # Zero point is shifted by -10
439
```
440
441
### Custom Properties
442
443
Add application-specific metadata for processing workflows and custom applications.
444
445
```python { .api }
446
# Custom property naming conventions
447
'custom-*' # Application-specific properties
448
'processing-*' # Processing history
449
'workflow-*' # Workflow information
450
'user-*' # User-defined properties
451
```
452
453
Example usage:
454
455
```python
456
# Processing history
457
def add_processing_history(image, operation, parameters):
458
processed = image.copy()
459
460
# Add timestamp
461
import datetime
462
timestamp = datetime.datetime.now().isoformat()
463
processed.set('processing-timestamp', timestamp)
464
465
# Add operation info
466
processed.set('processing-operation', operation)
467
processed.set('processing-parameters', str(parameters))
468
469
# Increment processing count
470
try:
471
count = processed.get('processing-count')
472
processed.set('processing-count', count + 1)
473
except:
474
processed.set('processing-count', 1)
475
476
return processed
477
478
# Quality assessment
479
def add_quality_metrics(image, sharpness, noise_level, exposure_quality):
480
metrics = image.copy()
481
metrics.set('quality-sharpness', sharpness)
482
metrics.set('quality-noise', noise_level)
483
metrics.set('quality-exposure', exposure_quality)
484
return metrics
485
486
# Workflow tracking
487
def add_workflow_info(image, stage, version, user):
488
workflow = image.copy()
489
workflow.set('workflow-stage', stage)
490
workflow.set('workflow-version', version)
491
workflow.set('workflow-user', user)
492
return workflow
493
494
# Use custom properties
495
processed = add_processing_history(image, 'resize', {'scale': 0.5})
496
with_metrics = add_quality_metrics(processed, 8.5, 2.1, 7.8)
497
final = add_workflow_info(with_metrics, 'final-review', '1.2', 'editor@example.com')
498
```
499
500
## Metadata Preservation
501
502
Control which metadata is preserved during image operations.
503
504
```python
505
# Copy with all metadata
506
exact_copy = image.copy()
507
508
# Copy image data only (no metadata)
509
data_only = pyvips.Image.new_from_memory(
510
image.write_to_memory(),
511
image.width, image.height, image.bands, image.format
512
)
513
514
# Copy with selective metadata
515
selective_copy = image.copy()
516
for field in ['icc-profile-data', 'exif-ifd0-Orientation']:
517
if field in image.get_fields():
518
selective_copy.set(field, image.get(field))
519
520
# Preserve metadata through operations
521
def preserve_metadata(original, processed):
522
"""Copy important metadata to processed image."""
523
result = processed.copy()
524
525
# Core metadata to preserve
526
important_fields = [
527
'icc-profile-data',
528
'exif-ifd0-Orientation',
529
'exif-ifd0-Artist',
530
'exif-ifd0-Copyright',
531
'xmp-dc-creator',
532
'xmp-dc-rights'
533
]
534
535
for field in important_fields:
536
if field in original.get_fields():
537
try:
538
result.set(field, original.get(field))
539
except:
540
pass # Skip if can't set
541
542
return result
543
544
# Use metadata preservation
545
resized = image.resize(0.5)
546
final = preserve_metadata(image, resized)
547
```