or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-features.mdcpp-api.mdfile-io.mdimage-data.mdindex.mdmetadata.md

metadata.mddocs/

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

```