0
# Metadata and Headers
1
2
Extensible attribute system for storing comprehensive image metadata, including standard attributes for color management, camera data, rendering parameters, and custom application-specific information.
3
4
## Capabilities
5
6
### Standard Header Attributes
7
8
Core metadata attributes required or commonly used in EXR files.
9
10
```python { .api }
11
# Required header attributes (automatically managed)
12
header = {
13
"compression": int, # Compression method constant
14
"type": int, # Image type (scanline/tiled/deep)
15
"channels": dict, # Channel definitions (auto-generated)
16
"dataWindow": tuple, # Data bounds (minX, minY, maxX, maxY)
17
"displayWindow": tuple, # Display bounds (minX, minY, maxX, maxY)
18
"lineOrder": int, # Scanline order (INCREASING_Y/DECREASING_Y/RANDOM_Y)
19
"pixelAspectRatio": float, # Pixel aspect ratio (width/height)
20
"screenWindowCenter": tuple, # Screen window center (x, y)
21
"screenWindowWidth": float, # Screen window width
22
}
23
24
# Optional standard attributes
25
header.update({
26
"owner": str, # Image creator/owner
27
"comments": str, # Descriptive comments
28
"capDate": str, # Capture date (YYYY:MM:DD HH:MM:SS)
29
"utcOffset": float, # UTC offset in seconds
30
"software": str, # Creating software name/version
31
"artist": str, # Artist/creator name
32
"copyright": str, # Copyright notice
33
"contact": str, # Contact information
34
"jobName": str, # Production job name
35
"project": str, # Project name
36
})
37
```
38
39
### Tiling and Multi-Resolution
40
41
Attributes for tiled images and multi-resolution formats.
42
43
```python { .api }
44
# Tiled image attributes
45
header.update({
46
"tiles": dict, # Tile description
47
# {
48
# "xSize": int, # Tile width
49
# "ySize": int, # Tile height
50
# "mode": int, # Level mode (ONE_LEVEL/MIPMAP_LEVELS/RIPMAP_LEVELS)
51
# "roundingMode": int # Level rounding mode (ROUND_DOWN/ROUND_UP)
52
# }
53
})
54
55
# Multi-resolution level modes
56
from OpenEXR import Imath
57
58
Imath.LevelMode.ONE_LEVEL: int # Single resolution level
59
Imath.LevelMode.MIPMAP_LEVELS: int # Mipmapped levels (power of 2)
60
Imath.LevelMode.RIPMAP_LEVELS: int # Ripmapped levels (independent X/Y)
61
62
Imath.LevelRoundingMode.ROUND_DOWN: int # Round dimensions down
63
Imath.LevelRoundingMode.ROUND_UP: int # Round dimensions up
64
```
65
66
### Color and Display Attributes
67
68
Color management and display-related metadata.
69
70
```python { .api }
71
# Color space and display attributes
72
header.update({
73
"chromaticities": tuple, # Color primaries (8 floats: rx,ry,gx,gy,bx,by,wx,wy)
74
"whiteLuminance": float, # White point luminance (cd/m²)
75
"adoptedNeutral": tuple, # Adopted neutral (x, y) chromaticity
76
77
# Display and viewing
78
"displayWindow": tuple, # Display bounds (minX, minY, maxX, maxY)
79
"screenWindowCenter": tuple, # Screen window center (x, y)
80
"screenWindowWidth": float, # Screen window width
81
"pixelAspectRatio": float, # Pixel aspect ratio
82
83
# Gamma and transfer
84
"gamma": float, # Display gamma (if applicable)
85
"exposure": float, # Exposure compensation
86
})
87
88
# Standard chromaticities
89
from OpenEXR import Imath
90
91
rec709_chromaticities = Imath.Chromaticities(
92
# Red, Green, Blue, White points (x, y coordinates)
93
Imath.V2f(0.64, 0.33), # Red primary
94
Imath.V2f(0.30, 0.60), # Green primary
95
Imath.V2f(0.15, 0.06), # Blue primary
96
Imath.V2f(0.3127, 0.3290) # White point (D65)
97
)
98
```
99
100
### Camera and Lens Metadata
101
102
Camera-specific metadata for visual effects and photography workflows.
103
104
```python { .api }
105
# Camera and lens attributes
106
header.update({
107
"camera": str, # Camera model/name
108
"lens": str, # Lens model/name
109
"focalLength": float, # Focal length (mm)
110
"aperture": float, # F-stop/aperture value
111
"focus": float, # Focus distance (m)
112
"iso": int, # ISO sensitivity
113
"shutterSpeed": float, # Shutter speed (seconds)
114
115
# Camera position and orientation
116
"cameraPosition": tuple, # World position (x, y, z)
117
"cameraOrientation": tuple, # Rotation quaternion (x, y, z, w)
118
"cameraTransform": tuple, # 4x4 transformation matrix (16 floats)
119
120
# Projection parameters
121
"nearClip": float, # Near clipping plane
122
"farClip": float, # Far clipping plane
123
"fieldOfView": float, # Field of view (degrees)
124
"projection": str, # Projection type ("perspective", "orthographic")
125
})
126
```
127
128
### Rendering and VFX Metadata
129
130
Metadata specific to 3D rendering and visual effects workflows.
131
132
```python { .api }
133
# Rendering attributes
134
header.update({
135
"renderTime": float, # Render time (seconds)
136
"samples": int, # Sample count per pixel
137
"renderer": str, # Rendering software
138
"renderLayer": str, # Render layer name
139
"renderPass": str, # Render pass type
140
141
# Scene information
142
"scene": str, # Scene file name
143
"shot": str, # Shot identifier
144
"frame": int, # Frame number
145
"frameRate": float, # Frames per second
146
147
# Quality settings
148
"quality": str, # Quality preset ("draft", "preview", "final")
149
"motionBlur": bool, # Motion blur enabled
150
"depthOfField": bool, # Depth of field enabled
151
"globalIllumination": bool, # Global illumination enabled
152
})
153
154
# Multi-part type identification
155
header.update({
156
"type": int, # Part type constant
157
"name": str, # Part name (for multi-part files)
158
"view": str, # Stereo view ("left", "right", "center")
159
})
160
```
161
162
### Custom Attributes
163
164
Application-specific metadata using the extensible attribute system.
165
166
```python { .api }
167
# Custom application attributes (examples)
168
header.update({
169
# Pipeline-specific
170
"pipeline.version": str, # Pipeline version
171
"pipeline.jobId": str, # Job identifier
172
"pipeline.userId": str, # User identifier
173
"pipeline.department": str, # Creating department
174
175
# Workflow metadata
176
"workflow.status": str, # Approval status
177
"workflow.reviewNotes": str, # Review comments
178
"workflow.version": int, # Version number
179
180
# Technical metadata
181
"tech.hostName": str, # Rendering machine
182
"tech.renderEngine": str, # Render engine version
183
"tech.memoryUsage": float, # Peak memory usage (GB)
184
"tech.renderNodes": int, # Distributed render nodes
185
186
# Color pipeline
187
"color.inputSpace": str, # Input color space
188
"color.workingSpace": str, # Working color space
189
"color.outputSpace": str, # Output color space
190
"color.lut": str, # Color LUT file path
191
})
192
```
193
194
### Geometric and Math Types
195
196
Complex geometric types for advanced metadata.
197
198
```python { .api }
199
from OpenEXR import Imath
200
201
# Vector types
202
vector2i = Imath.V2i(x, y) # 2D integer vector
203
vector2f = Imath.V2f(x, y) # 2D float vector
204
vector3i = Imath.V3i(x, y, z) # 3D integer vector
205
vector3f = Imath.V3f(x, y, z) # 3D float vector
206
207
# Matrix types
208
matrix33f = Imath.M33f() # 3x3 float matrix
209
matrix44f = Imath.M44f() # 4x4 float matrix
210
211
# Specialized types
212
timecode = Imath.TimeCode( # SMPTE timecode
213
hours, minutes, seconds, frame,
214
dropFrame=False, colorFrame=False,
215
fieldPhase=False, bgf0=False, bgf1=False, bgf2=False
216
)
217
218
keycode = Imath.KeyCode( # Film keycode
219
filmMfcCode, filmType, prefix,
220
count, perfOffset, perfsPerFrame, perfsPerCount
221
)
222
223
rational = Imath.Rational(numerator, denominator) # Rational number
224
225
preview = Imath.PreviewImage(width, height, pixels) # Preview thumbnail
226
```
227
228
## Usage Examples
229
230
### Basic Header Management
231
232
```python
233
import OpenEXR
234
import numpy as np
235
236
# Create image with comprehensive metadata
237
height, width = 1080, 1920
238
rgb_data = np.random.rand(height, width, 3).astype('f')
239
240
# Build complete header
241
header = {
242
# Required
243
"compression": OpenEXR.ZIP_COMPRESSION,
244
"type": OpenEXR.scanlineimage,
245
246
# Display and quality
247
"pixelAspectRatio": 1.0,
248
249
# Production metadata
250
"software": "MyRenderer v2.1.0",
251
"artist": "John Doe",
252
"project": "Feature Film XYZ",
253
"shot": "SEQ010_SH020",
254
"frame": 1001,
255
"comments": "Hero beauty pass with atmospheric effects",
256
257
# Camera information
258
"camera": "RED Dragon 6K",
259
"lens": "Zeiss Master Prime 50mm T1.3",
260
"focalLength": 50.0,
261
"aperture": 2.8,
262
"iso": 800,
263
264
# Rendering details
265
"renderer": "Arnold 7.2.1.1",
266
"renderTime": 145.7,
267
"samples": 256,
268
"quality": "final"
269
}
270
271
channels = {"RGB": rgb_data}
272
273
with OpenEXR.File(header, channels) as outfile:
274
outfile.write("metadata_example.exr")
275
276
# Read back and examine metadata
277
with OpenEXR.File("metadata_example.exr") as infile:
278
header = infile.header()
279
280
print("Production Info:")
281
print(f" Project: {header.get('project', 'Unknown')}")
282
print(f" Shot: {header.get('shot', 'Unknown')}")
283
print(f" Frame: {header.get('frame', 'Unknown')}")
284
print(f" Artist: {header.get('artist', 'Unknown')}")
285
286
print("\nTechnical Info:")
287
print(f" Software: {header.get('software', 'Unknown')}")
288
print(f" Renderer: {header.get('renderer', 'Unknown')}")
289
print(f" Render Time: {header.get('renderTime', 0):.1f}s")
290
print(f" Samples: {header.get('samples', 'Unknown')}")
291
```
292
293
### Color Management Metadata
294
295
```python
296
import OpenEXR
297
from OpenEXR import Imath
298
import numpy as np
299
300
# Define color space with chromaticities
301
rec2020_chromaticities = Imath.Chromaticities(
302
Imath.V2f(0.708, 0.292), # Red primary
303
Imath.V2f(0.170, 0.797), # Green primary
304
Imath.V2f(0.131, 0.046), # Blue primary
305
Imath.V2f(0.3127, 0.3290) # White point D65
306
)
307
308
# HDR metadata
309
height, width = 2160, 3840 # 4K
310
hdr_data = np.random.exponential(2.0, (height, width, 3)).astype('f')
311
312
hdr_header = {
313
"compression": OpenEXR.DWAA_COMPRESSION,
314
"type": OpenEXR.scanlineimage,
315
316
# Color management
317
"chromaticities": (
318
# Extract values from Chromaticities object
319
rec2020_chromaticities.red.x, rec2020_chromaticities.red.y,
320
rec2020_chromaticities.green.x, rec2020_chromaticities.green.y,
321
rec2020_chromaticities.blue.x, rec2020_chromaticities.blue.y,
322
rec2020_chromaticities.white.x, rec2020_chromaticities.white.y
323
),
324
"whiteLuminance": 10000.0, # 10,000 cd/m² for HDR
325
"adoptedNeutral": (0.3127, 0.3290), # D65 white point
326
327
# HDR workflow
328
"color.inputSpace": "scene-linear Rec.2020",
329
"color.workingSpace": "ACES AP0",
330
"color.outputSpace": "Rec.2020 PQ",
331
"exposure": 0.0, # No exposure compensation
332
333
# Technical
334
"software": "HDR Pipeline v1.0",
335
"comments": "HDR content for wide gamut displays"
336
}
337
338
channels = {"RGB": hdr_data}
339
340
with OpenEXR.File(hdr_header, channels) as outfile:
341
outfile.write("hdr_color_managed.exr")
342
```
343
344
### VFX Pipeline Metadata
345
346
```python
347
import OpenEXR
348
import numpy as np
349
from datetime import datetime
350
351
def create_vfx_metadata(shot_info, render_settings, technical_info):
352
"""Create comprehensive VFX pipeline metadata."""
353
354
# Get current timestamp
355
now = datetime.now()
356
timestamp = now.strftime("%Y:%m:%d %H:%M:%S")
357
utc_offset = now.utcoffset().total_seconds() if now.utcoffset() else 0
358
359
header = {
360
# Core format
361
"compression": OpenEXR.DWAA_COMPRESSION,
362
"type": OpenEXR.scanlineimage,
363
364
# Basic metadata
365
"capDate": timestamp,
366
"utcOffset": utc_offset,
367
"software": f"VFX Pipeline {technical_info['pipeline_version']}",
368
369
# Shot identification
370
"project": shot_info["project"],
371
"shot": shot_info["shot"],
372
"sequence": shot_info["sequence"],
373
"frame": shot_info["frame"],
374
"frameRate": shot_info["fps"],
375
"renderLayer": shot_info["layer"],
376
"renderPass": shot_info["pass"],
377
378
# Artist and approval workflow
379
"artist": shot_info["artist"],
380
"department": shot_info["department"],
381
"workflow.version": shot_info["version"],
382
"workflow.status": shot_info.get("status", "pending"),
383
384
# Render settings
385
"renderer": render_settings["engine"],
386
"samples": render_settings["samples"],
387
"renderTime": render_settings.get("time", 0),
388
"quality": render_settings["quality"],
389
"motionBlur": render_settings.get("motion_blur", False),
390
"depthOfField": render_settings.get("dof", False),
391
392
# Technical environment
393
"tech.hostName": technical_info["hostname"],
394
"tech.renderEngine": technical_info["engine_version"],
395
"tech.memoryUsage": technical_info.get("memory_gb", 0),
396
"tech.renderNodes": technical_info.get("nodes", 1),
397
398
# Pipeline specific
399
"pipeline.jobId": technical_info["job_id"],
400
"pipeline.buildNumber": technical_info["build"],
401
"pipeline.configHash": technical_info.get("config_hash", ""),
402
403
# Custom tracking
404
"tracking.submitTime": technical_info.get("submit_time", timestamp),
405
"tracking.queueTime": technical_info.get("queue_time", 0),
406
"tracking.renderCost": technical_info.get("cost", 0.0)
407
}
408
409
return header
410
411
# Example usage
412
shot_data = {
413
"project": "FeatureFilm2024",
414
"sequence": "SEQ_100",
415
"shot": "SH_010",
416
"frame": 1001,
417
"fps": 24.0,
418
"layer": "beauty",
419
"pass": "diffuse",
420
"artist": "jane.smith",
421
"department": "lighting",
422
"version": 3
423
}
424
425
render_data = {
426
"engine": "Arnold",
427
"engine_version": "7.2.1.1",
428
"samples": 512,
429
"quality": "final",
430
"motion_blur": True,
431
"dof": True,
432
"time": 342.5
433
}
434
435
tech_data = {
436
"pipeline_version": "2.1.0",
437
"hostname": "render-node-042",
438
"job_id": "JOB_20240910_001234",
439
"build": "2.1.0-stable-abc123",
440
"memory_gb": 64.2,
441
"nodes": 8
442
}
443
444
# Create image with VFX metadata
445
height, width = 2048, 2048
446
beauty_data = np.random.rand(height, width, 3).astype('f')
447
448
vfx_header = create_vfx_metadata(shot_data, render_data, tech_data)
449
channels = {"RGB": beauty_data}
450
451
with OpenEXR.File(vfx_header, channels) as outfile:
452
outfile.write("shot_with_pipeline_metadata.exr")
453
454
# Verify metadata was stored
455
with OpenEXR.File("shot_with_pipeline_metadata.exr") as infile:
456
header = infile.header()
457
458
print("Shot Information:")
459
print(f" {header['project']}/{header['sequence']}/{header['shot']} v{header['workflow.version']}")
460
print(f" Frame {header['frame']} - {header['renderLayer']}.{header['renderPass']}")
461
print(f" Artist: {header['artist']} ({header['department']})")
462
463
print("\nRender Information:")
464
print(f" Engine: {header['renderer']}")
465
print(f" Samples: {header['samples']}")
466
print(f" Time: {header['renderTime']:.1f}s")
467
print(f" Quality: {header['quality']}")
468
469
print("\nTechnical:")
470
print(f" Host: {header['tech.hostName']}")
471
print(f" Job: {header['pipeline.jobId']}")
472
print(f" Memory: {header['tech.memoryUsage']:.1f} GB")
473
```
474
475
### Custom Attribute Types
476
477
```python
478
import OpenEXR
479
from OpenEXR import Imath
480
import numpy as np
481
482
# Create image with complex custom attributes
483
height, width = 1080, 1920
484
image_data = np.random.rand(height, width, 3).astype('f')
485
486
# Camera transformation matrix (4x4)
487
camera_matrix = Imath.M44f(
488
1.0, 0.0, 0.0, 0.0,
489
0.0, 1.0, 0.0, 0.0,
490
0.0, 0.0, 1.0, 10.0, # 10 units back from origin
491
0.0, 0.0, 0.0, 1.0
492
)
493
494
# SMPTE timecode
495
frame_timecode = Imath.TimeCode(
496
hours=1, minutes=23, seconds=45, frame=12,
497
dropFrame=False, colorFrame=False
498
)
499
500
# Film keycode
501
film_keycode = Imath.KeyCode(
502
filmMfcCode=12345,
503
filmType=6789,
504
prefix=123,
505
count=456789,
506
perfOffset=12,
507
perfsPerFrame=4,
508
perfsPerCount=64
509
)
510
511
# Build header with complex types
512
header = {
513
"compression": OpenEXR.ZIP_COMPRESSION,
514
"type": OpenEXR.scanlineimage,
515
516
# Standard metadata
517
"software": "Advanced Pipeline v3.0",
518
"comments": "Complex metadata example",
519
520
# Geometric attributes (stored as tuples/arrays)
521
"cameraTransform": tuple(camera_matrix.getValue()), # 16 floats
522
"cameraPosition": (0.0, 0.0, 10.0), # World position
523
"cameraTarget": (0.0, 0.0, 0.0), # Look-at target
524
525
# Timecode information
526
"timeCode": str(frame_timecode), # Convert to string representation
527
"keyCode": str(film_keycode), # Convert to string representation
528
529
# Rational numbers (as tuples)
530
"frameRate": (24000, 1001), # 23.976 fps as rational
531
"shutterAngle": (180, 1), # 180 degree shutter
532
533
# Custom workflow data
534
"workflow.checkpoints": [ # List as string
535
"modeling_approved",
536
"rigging_complete",
537
"animation_final",
538
"lighting_review"
539
],
540
541
# Bounding box information
542
"geometry.boundingBox": (-10.0, -5.0, -2.0, 10.0, 5.0, 8.0), # min/max XYZ
543
544
# Color correction parameters
545
"cc.lift": (0.0, 0.0, 0.0), # Lift adjustment
546
"cc.gamma": (1.0, 1.0, 1.0), # Gamma adjustment
547
"cc.gain": (1.0, 1.0, 1.0), # Gain adjustment
548
"cc.saturation": 1.0, # Saturation multiplier
549
}
550
551
channels = {"RGB": image_data}
552
553
with OpenEXR.File(header, channels) as outfile:
554
outfile.write("complex_metadata.exr")
555
556
# Read and parse complex metadata
557
with OpenEXR.File("complex_metadata.exr") as infile:
558
header = infile.header()
559
560
# Extract camera transform
561
if "cameraTransform" in header:
562
matrix_values = header["cameraTransform"]
563
print("Camera Transform Matrix:")
564
for i in range(4):
565
row = matrix_values[i*4:(i+1)*4]
566
print(f" {row[0]:8.3f} {row[1]:8.3f} {row[2]:8.3f} {row[3]:8.3f}")
567
568
# Extract workflow checkpoints
569
if "workflow.checkpoints" in header:
570
checkpoints = header["workflow.checkpoints"]
571
print(f"\nWorkflow Checkpoints: {checkpoints}")
572
573
# Extract color correction
574
cc_attrs = {k: v for k, v in header.items() if k.startswith("cc.")}
575
if cc_attrs:
576
print("\nColor Correction:")
577
for attr, value in cc_attrs.items():
578
print(f" {attr}: {value}")
579
```