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

image-data.mddocs/

0

# Image Data Structures

1

2

Channel-based image representation supporting arbitrary pixel types, subsampling patterns, and deep compositing data structures for professional VFX and animation workflows.

3

4

## Capabilities

5

6

### Pixel Type System

7

8

OpenEXR supports multiple pixel data types optimized for different content and precision requirements.

9

10

```python { .api }

11

# Pixel type constants

12

OpenEXR.UINT: int # 32-bit unsigned integer (0 to 4,294,967,295)

13

OpenEXR.HALF: int # 16-bit IEEE 754 floating point (half precision)

14

OpenEXR.FLOAT: int # 32-bit IEEE 754 floating point (single precision)

15

```

16

17

### Channel Architecture

18

19

Individual image channels with flexible configuration for subsampling, pixel types, and linear encoding.

20

21

```python { .api }

22

class Channel:

23

def __init__(self, name: str = None, pixels = None, xSampling: int = 1, ySampling: int = 1, pLinear: bool = False):

24

"""

25

Create image channel with pixel data.

26

27

Args:

28

name: Channel identifier (e.g., "R", "G", "B", "A", "Z", "motion.X")

29

pixels: Pixel data as numpy array

30

xSampling: Horizontal subsampling factor (1 = full resolution)

31

ySampling: Vertical subsampling factor (1 = full resolution)

32

pLinear: True if pixels are perceptually linear encoded

33

"""

34

35

def pixelType(self):

36

"""

37

Get pixel data type based on numpy array dtype.

38

39

Returns:

40

OpenEXR pixel type constant (UINT/HALF/FLOAT)

41

"""

42

43

name: str # Channel name/identifier

44

xSampling: int # Horizontal subsampling (1, 2, 4, ...)

45

ySampling: int # Vertical subsampling (1, 2, 4, ...)

46

pLinear: bool # Perceptual linearity flag

47

pixels: numpy.ndarray # Pixel data array

48

```

49

50

### Array Data Layout

51

52

OpenEXR uses specific array layouts and data type mappings for efficient processing.

53

54

```python { .api }

55

# Numpy dtype mapping to OpenEXR pixel types

56

numpy.uint32 -> OpenEXR.UINT # 32-bit unsigned integer

57

numpy.float16 -> OpenEXR.HALF # 16-bit floating point (half)

58

numpy.float32 -> OpenEXR.FLOAT # 32-bit floating point (float)

59

60

# Array shapes for different channel configurations

61

single_channel: (height, width) # Grayscale, alpha, depth

62

rgb_packed: (height, width, 3) # RGB as single array

63

rgba_packed: (height, width, 4) # RGBA as single array

64

separate_channels: dict[str, (height, width)] # Individual channel arrays

65

```

66

67

### Geometric Types

68

69

Mathematical types for image bounds, vectors, and coordinate systems from the Imath module.

70

71

```python { .api }

72

# 2D Vector types

73

class V2i:

74

def __init__(self, x: int = 0, y: int = 0): ...

75

x: int

76

y: int

77

78

class V2f:

79

def __init__(self, x: float = 0.0, y: float = 0.0): ...

80

x: float

81

y: float

82

83

# 2D Box types for image bounds

84

class Box2i:

85

def __init__(self, min: V2i = None, max: V2i = None): ...

86

def __init__(self, minX: int, minY: int, maxX: int, maxY: int): ...

87

88

min: V2i # Minimum corner

89

max: V2i # Maximum corner

90

91

def width(self) -> int: ...

92

def height(self) -> int: ...

93

def isEmpty(self) -> bool: ...

94

def hasVolume(self) -> bool: ...

95

96

class Box2f:

97

def __init__(self, min: V2f = None, max: V2f = None): ...

98

def __init__(self, minX: float, minY: float, maxX: float, maxY: float): ...

99

100

min: V2f # Minimum corner

101

max: V2f # Maximum corner

102

```

103

104

### Deep Image Data

105

106

Multi-sample per pixel data structures for advanced compositing workflows.

107

108

```python { .api }

109

# Deep image support (conceptual - implementation varies)

110

class DeepChannel:

111

"""

112

Channel supporting variable samples per pixel.

113

Used for deep compositing workflows.

114

"""

115

116

def __init__(self, name: str, sampleCounts, sampleData):

117

"""

118

Create deep channel.

119

120

Args:

121

name: Channel identifier

122

sampleCounts: Array of sample counts per pixel

123

sampleData: Flattened array of all sample values

124

"""

125

126

name: str # Channel name

127

sampleCounts: numpy.ndarray # Samples per pixel (height, width)

128

sampleData: numpy.ndarray # All sample values (flattened)

129

totalSamples: int # Total number of samples

130

```

131

132

## Usage Examples

133

134

### Working with Pixel Types

135

136

```python

137

import OpenEXR

138

import numpy as np

139

140

# Create data with different pixel types

141

height, width = 1080, 1920

142

143

# HALF precision (16-bit float) - common for HDR images

144

half_data = np.random.rand(height, width, 3).astype(np.float16)

145

half_channels = {"RGB": half_data}

146

147

# FLOAT precision (32-bit float) - maximum precision

148

float_data = np.random.rand(height, width, 3).astype(np.float32)

149

float_channels = {"RGB": float_data}

150

151

# UINT (32-bit unsigned int) - for ID mattes, masks

152

uint_data = np.random.randint(0, 255, (height, width), dtype=np.uint32)

153

uint_channels = {"ID": uint_data}

154

155

# Check pixel types

156

print(f"Half pixel type: {OpenEXR.Channel(pixels=half_data).pixelType()}") # OpenEXR.HALF

157

print(f"Float pixel type: {OpenEXR.Channel(pixels=float_data).pixelType()}") # OpenEXR.FLOAT

158

print(f"UINT pixel type: {OpenEXR.Channel(pixels=uint_data).pixelType()}") # OpenEXR.UINT

159

```

160

161

### Channel Configurations

162

163

```python

164

import OpenEXR

165

import numpy as np

166

167

height, width = 1080, 1920

168

169

# RGB channels as single packed array

170

rgb_packed = np.random.rand(height, width, 3).astype('f')

171

channels_packed = {"RGB": rgb_packed}

172

173

# RGB channels as separate arrays

174

r_data = np.random.rand(height, width).astype('f')

175

g_data = np.random.rand(height, width).astype('f')

176

b_data = np.random.rand(height, width).astype('f')

177

channels_separate = {"R": r_data, "G": g_data, "B": b_data}

178

179

# RGBA with alpha channel

180

rgba_data = np.random.rand(height, width, 4).astype('f')

181

rgba_data[:, :, 3] = 1.0 # Set alpha to 1.0

182

channels_rgba = {"RGBA": rgba_data}

183

184

# Mixed precision channels

185

beauty_rgb = np.random.rand(height, width, 3).astype(np.float16) # HALF for beauty

186

depth_z = np.random.rand(height, width).astype(np.float32) # FLOAT for depth

187

mask_id = np.random.randint(0, 10, (height, width), dtype=np.uint32) # UINT for ID

188

189

mixed_channels = {

190

"RGB": beauty_rgb,

191

"Z": depth_z,

192

"ID": mask_id

193

}

194

```

195

196

### Subsampled Channels

197

198

```python

199

import OpenEXR

200

import numpy as np

201

202

height, width = 1080, 1920

203

204

# Full resolution luminance

205

y_data = np.random.rand(height, width).astype('f')

206

207

# Subsampled chrominance (half resolution)

208

# Create data at full resolution then subsample

209

chroma_full = np.random.rand(height, width, 2).astype('f')

210

chroma_subsampled = chroma_full[::2, ::2, :] # Every other pixel

211

212

# Create channels with subsampling

213

channels = {

214

"Y": OpenEXR.Channel("Y", y_data, xSampling=1, ySampling=1),

215

"RY": OpenEXR.Channel("RY", chroma_subsampled[:,:,0], xSampling=2, ySampling=2),

216

"BY": OpenEXR.Channel("BY", chroma_subsampled[:,:,1], xSampling=2, ySampling=2)

217

}

218

219

# Write YC format image

220

header = {

221

"compression": OpenEXR.ZIP_COMPRESSION,

222

"type": OpenEXR.scanlineimage

223

}

224

225

with OpenEXR.File(header, channels) as outfile:

226

outfile.write("yc_subsampled.exr")

227

```

228

229

### Working with Image Bounds

230

231

```python

232

from OpenEXR import Imath

233

import OpenEXR

234

import numpy as np

235

236

# Define image windows using Box2i

237

display_window = Imath.Box2i(

238

Imath.V2i(0, 0), # min corner

239

Imath.V2i(1919, 1079) # max corner (1920x1080)

240

)

241

242

# Data window can be subset of display window

243

data_window = Imath.Box2i(

244

Imath.V2i(100, 100), # Cropped region

245

Imath.V2i(1819, 979) # 1720x880 actual data

246

)

247

248

# Calculate dimensions

249

data_width = data_window.width() # 1720

250

data_height = data_window.height() # 880

251

display_width = display_window.width() # 1920

252

display_height = display_window.height() # 1080

253

254

print(f"Display: {display_width}x{display_height}")

255

print(f"Data: {data_width}x{data_height}")

256

257

# Create image data for actual data window size

258

rgb_data = np.random.rand(data_height, data_width, 3).astype('f')

259

260

# Header with window information

261

header = {

262

"compression": OpenEXR.ZIP_COMPRESSION,

263

"type": OpenEXR.scanlineimage,

264

"displayWindow": (display_window.min.x, display_window.min.y,

265

display_window.max.x, display_window.max.y),

266

"dataWindow": (data_window.min.x, data_window.min.y,

267

data_window.max.x, data_window.max.y)

268

}

269

270

channels = {"RGB": rgb_data}

271

272

with OpenEXR.File(header, channels) as outfile:

273

outfile.write("windowed_image.exr")

274

```

275

276

### Multi-Channel VFX Data

277

278

```python

279

import OpenEXR

280

import numpy as np

281

282

height, width = 1080, 1920

283

284

# Beauty pass (RGB)

285

beauty = np.random.rand(height, width, 3).astype(np.float16)

286

287

# Depth pass (Z)

288

depth = np.random.exponential(10.0, (height, width)).astype(np.float32)

289

290

# Motion vectors (2D)

291

motion_x = np.random.normal(0, 2, (height, width)).astype(np.float32)

292

motion_y = np.random.normal(0, 2, (height, width)).astype(np.float32)

293

294

# Normal vectors (3D)

295

normals = np.random.normal(0, 1, (height, width, 3)).astype(np.float32)

296

# Normalize to unit vectors

297

norm_length = np.sqrt(np.sum(normals**2, axis=2, keepdims=True))

298

normals = normals / norm_length

299

300

# Object ID (integer)

301

object_ids = np.random.randint(0, 100, (height, width)).astype(np.uint32)

302

303

# Material ID (integer)

304

material_ids = np.random.randint(0, 50, (height, width)).astype(np.uint32)

305

306

# Coverage/alpha (float)

307

coverage = np.random.rand(height, width).astype(np.float32)

308

309

# Create comprehensive channel set

310

vfx_channels = {

311

# Beauty

312

"RGB": beauty,

313

314

# Geometry

315

"Z": depth,

316

"N": normals, # Normals as packed RGB

317

"N.X": normals[:,:,0], # Or separate components

318

"N.Y": normals[:,:,1],

319

"N.Z": normals[:,:,2],

320

321

# Motion

322

"motion": np.stack([motion_x, motion_y], axis=2), # Packed 2D

323

"motion.X": motion_x, # Or separate components

324

"motion.Y": motion_y,

325

326

# IDs

327

"objectID": object_ids,

328

"materialID": material_ids,

329

330

# Coverage

331

"A": coverage

332

}

333

334

# VFX-optimized header

335

vfx_header = {

336

"compression": OpenEXR.DWAA_COMPRESSION, # Good for mixed content

337

"type": OpenEXR.scanlineimage,

338

"pixelAspectRatio": 1.0,

339

340

# Custom attributes

341

"software": "VFX Pipeline v1.0",

342

"comments": "Multi-pass render with motion vectors"

343

}

344

345

with OpenEXR.File(vfx_header, vfx_channels) as outfile:

346

outfile.write("vfx_multipass.exr")

347

348

# Read back and verify channel types

349

with OpenEXR.File("vfx_multipass.exr") as infile:

350

channels = infile.channels()

351

352

for name, channel in channels.items():

353

pixel_type = channel.pixelType()

354

shape = channel.pixels.shape

355

dtype = channel.pixels.dtype

356

357

print(f"{name}: {shape} {dtype} -> OpenEXR.{pixel_type}")

358

```

359

360

### Memory Optimization

361

362

```python

363

import OpenEXR

364

import numpy as np

365

366

def create_memory_efficient_channels(height, width):

367

"""Create channels with memory-conscious data types."""

368

369

# Use HALF precision for beauty (saves 50% memory vs FLOAT)

370

beauty_data = np.random.rand(height, width, 3).astype(np.float16)

371

372

# Use FLOAT only when precision is critical (depth, motion)

373

depth_data = np.random.rand(height, width).astype(np.float32)

374

motion_data = np.random.rand(height, width, 2).astype(np.float32)

375

376

# Use UINT for discrete data (IDs, masks)

377

id_data = np.random.randint(0, 1000, (height, width), dtype=np.uint32)

378

379

# Calculate memory usage

380

beauty_mb = beauty_data.nbytes / (1024 * 1024)

381

depth_mb = depth_data.nbytes / (1024 * 1024)

382

motion_mb = motion_data.nbytes / (1024 * 1024)

383

id_mb = id_data.nbytes / (1024 * 1024)

384

385

total_mb = beauty_mb + depth_mb + motion_mb + id_mb

386

387

print(f"Memory usage:")

388

print(f" Beauty (HALF): {beauty_mb:.1f} MB")

389

print(f" Depth (FLOAT): {depth_mb:.1f} MB")

390

print(f" Motion (FLOAT): {motion_mb:.1f} MB")

391

print(f" ID (UINT): {id_mb:.1f} MB")

392

print(f" Total: {total_mb:.1f} MB")

393

394

return {

395

"RGB": beauty_data,

396

"Z": depth_data,

397

"motion": motion_data,

398

"ID": id_data

399

}

400

401

# Create 4K image with memory-efficient types

402

channels = create_memory_efficient_channels(2160, 3840) # 4K resolution

403

404

header = {

405

"compression": OpenEXR.DWAA_COMPRESSION,

406

"type": OpenEXR.scanlineimage

407

}

408

409

with OpenEXR.File(header, channels) as outfile:

410

outfile.write("4k_memory_efficient.exr")

411

```