0
# File I/O Operations
1
2
Comprehensive file reading and writing operations for EXR images, supporting scanline and tiled formats, multi-part files, and streaming operations with configurable threading.
3
4
## Capabilities
5
6
### Primary File Interface
7
8
The main interface for EXR file operations, providing context management and flexible construction for different use cases.
9
10
```python { .api }
11
class File:
12
def __init__(self, filename: str, separate_channels: bool = False, header_only: bool = False):
13
"""
14
Open existing EXR file for reading.
15
16
Args:
17
filename: Path to EXR file
18
separate_channels: Read channels as separate arrays
19
header_only: Read only header information
20
"""
21
22
def __init__(self, header: dict, channels: dict):
23
"""
24
Create EXR file for writing with single part.
25
26
Args:
27
header: Image metadata and format settings
28
channels: Channel data dictionary
29
"""
30
31
def __init__(self, parts: list):
32
"""
33
Create multi-part EXR file for writing.
34
35
Args:
36
parts: List of Part objects
37
"""
38
39
def __enter__(self):
40
"""Context manager entry."""
41
42
def __exit__(self, *args):
43
"""Context manager exit with cleanup."""
44
45
def header(self, part_index: int = 0) -> dict:
46
"""
47
Get header information for specified part.
48
49
Args:
50
part_index: Part index for multi-part files
51
52
Returns:
53
Header dictionary with metadata
54
"""
55
56
def channels(self, part_index: int = 0) -> dict:
57
"""
58
Get channel data for specified part.
59
60
Args:
61
part_index: Part index for multi-part files
62
63
Returns:
64
Dictionary mapping channel names to Channel objects
65
"""
66
67
def write(self, filename: str) -> None:
68
"""
69
Write EXR data to file.
70
71
Args:
72
filename: Output file path
73
"""
74
75
def width(self) -> int:
76
"""Get image width in pixels."""
77
78
def height(self) -> int:
79
"""Get image height in pixels."""
80
```
81
82
### Part Management
83
84
Individual parts within multi-part EXR files, enabling complex compositing workflows with multiple image layers.
85
86
```python { .api }
87
class Part:
88
def __init__(self, header: dict, channels: dict, name: str):
89
"""
90
Create image part for multi-part files.
91
92
Args:
93
header: Part-specific header information
94
channels: Channel data for this part
95
name: Human-readable part name
96
"""
97
98
def name(self) -> str:
99
"""Get part name."""
100
101
def shape(self) -> tuple:
102
"""Get image dimensions as (height, width)."""
103
104
def width(self) -> int:
105
"""Get image width in pixels."""
106
107
def height(self) -> int:
108
"""Get image height in pixels."""
109
110
def compression(self):
111
"""Get compression method for this part."""
112
113
def type(self):
114
"""Get image type (scanline/tiled/deep)."""
115
116
def typeString(self) -> str:
117
"""Get human-readable image type string."""
118
119
header: dict # Header attributes
120
channels: dict # Channel data mapping
121
part_index: int # Zero-based part index
122
```
123
124
### Channel Representation
125
126
Individual image channels with flexible pixel types and sampling parameters.
127
128
```python { .api }
129
class Channel:
130
def __init__(self, name: str = None, pixels = None, xSampling: int = 1, ySampling: int = 1, pLinear: bool = False):
131
"""
132
Create image channel.
133
134
Args:
135
name: Channel identifier
136
pixels: Pixel data as numpy array
137
xSampling: Horizontal subsampling factor
138
ySampling: Vertical subsampling factor
139
pLinear: Whether pixels are perceptually linear
140
"""
141
142
def pixelType(self):
143
"""Get pixel data type (UINT/HALF/FLOAT)."""
144
145
name: str # Channel name
146
xSampling: int # Horizontal subsampling
147
ySampling: int # Vertical subsampling
148
pLinear: bool # Perceptual linearity flag
149
pixels: numpy.ndarray # Pixel data array
150
```
151
152
## Usage Examples
153
154
### Basic Reading Operations
155
156
```python
157
import OpenEXR
158
import numpy as np
159
160
# Read complete EXR file
161
with OpenEXR.File("input.exr") as infile:
162
header = infile.header()
163
channels = infile.channels()
164
165
# Access specific channels
166
rgb_data = channels["RGB"].pixels
167
alpha_data = channels["A"].pixels if "A" in channels else None
168
169
print(f"Image: {infile.width()}x{infile.height()}")
170
print(f"Channels: {list(channels.keys())}")
171
print(f"Compression: {header['compression']}")
172
173
# Read header only for fast metadata access
174
with OpenEXR.File("input.exr", header_only=True) as infile:
175
header = infile.header()
176
print(f"Image type: {header['type']}")
177
print(f"Data window: {header['dataWindow']}")
178
179
# Read with separate channel arrays
180
with OpenEXR.File("input.exr", separate_channels=True) as infile:
181
channels = infile.channels()
182
r_channel = channels["R"].pixels
183
g_channel = channels["G"].pixels
184
b_channel = channels["B"].pixels
185
```
186
187
### Basic Writing Operations
188
189
```python
190
import OpenEXR
191
import numpy as np
192
193
# Create RGB image data
194
height, width = 1080, 1920
195
rgb_data = np.random.rand(height, width, 3).astype('f')
196
197
# Write single-part file
198
header = {
199
"compression": OpenEXR.ZIP_COMPRESSION,
200
"type": OpenEXR.scanlineimage,
201
"pixelAspectRatio": 1.0
202
}
203
channels = {"RGB": rgb_data}
204
205
with OpenEXR.File(header, channels) as outfile:
206
outfile.write("output.exr")
207
208
# Write separate channels
209
r_data = np.random.rand(height, width).astype('f')
210
g_data = np.random.rand(height, width).astype('f')
211
b_data = np.random.rand(height, width).astype('f')
212
a_data = np.ones((height, width), dtype='f')
213
214
channels = {
215
"R": r_data,
216
"G": g_data,
217
"B": b_data,
218
"A": a_data
219
}
220
221
with OpenEXR.File(header, channels) as outfile:
222
outfile.write("rgba_output.exr")
223
```
224
225
### Multi-Part File Operations
226
227
```python
228
import OpenEXR
229
import numpy as np
230
231
# Create multiple image parts
232
height, width = 1080, 1920
233
234
# Beauty pass
235
beauty_data = np.random.rand(height, width, 3).astype('f')
236
beauty_header = {
237
"compression": OpenEXR.DWAA_COMPRESSION,
238
"type": OpenEXR.scanlineimage
239
}
240
beauty_channels = {"RGB": beauty_data}
241
242
# Depth pass
243
depth_data = np.random.rand(height, width).astype('f')
244
depth_header = {
245
"compression": OpenEXR.ZIP_COMPRESSION,
246
"type": OpenEXR.scanlineimage
247
}
248
depth_channels = {"Z": depth_data}
249
250
# Motion vector pass
251
motion_data = np.random.rand(height, width, 2).astype('f')
252
motion_header = {
253
"compression": OpenEXR.ZIP_COMPRESSION,
254
"type": OpenEXR.scanlineimage
255
}
256
motion_channels = {"motion": motion_data}
257
258
# Create multi-part file
259
parts = [
260
OpenEXR.Part(beauty_header, beauty_channels, "beauty"),
261
OpenEXR.Part(depth_header, depth_channels, "depth"),
262
OpenEXR.Part(motion_header, motion_channels, "motion")
263
]
264
265
with OpenEXR.File(parts) as outfile:
266
outfile.write("multipart_output.exr")
267
268
# Read multi-part file
269
with OpenEXR.File("multipart_output.exr") as infile:
270
# Access different parts
271
beauty_channels = infile.channels(0) # Beauty pass
272
depth_channels = infile.channels(1) # Depth pass
273
motion_channels = infile.channels(2) # Motion pass
274
275
# Get part-specific headers
276
beauty_header = infile.header(0)
277
depth_header = infile.header(1)
278
motion_header = infile.header(2)
279
```
280
281
### High-Performance Streaming
282
283
```python
284
import OpenEXR
285
import numpy as np
286
287
def process_large_exr(input_path, output_path):
288
"""Process large EXR files with memory efficiency."""
289
290
with OpenEXR.File(input_path) as infile:
291
header = infile.header()
292
293
# Modify header for output
294
output_header = header.copy()
295
output_header["compression"] = OpenEXR.DWAA_COMPRESSION
296
297
# Process in chunks for memory efficiency
298
channels = infile.channels()
299
300
# Apply processing to channel data
301
processed_channels = {}
302
for name, channel in channels.items():
303
# Example: gamma correction
304
processed_pixels = np.power(channel.pixels, 1.0/2.2)
305
processed_channels[name] = processed_pixels
306
307
# Write processed result
308
with OpenEXR.File(output_header, processed_channels) as outfile:
309
outfile.write(output_path)
310
311
# Process file
312
process_large_exr("large_input.exr", "processed_output.exr")
313
```
314
315
### Error Handling
316
317
```python
318
import OpenEXR
319
320
def safe_exr_read(filename):
321
"""Safely read EXR file with proper error handling."""
322
323
try:
324
with OpenEXR.File(filename) as infile:
325
header = infile.header()
326
channels = infile.channels()
327
return header, channels
328
329
except IOError as e:
330
print(f"File I/O error: {e}")
331
return None, None
332
333
except Exception as e:
334
print(f"EXR format error: {e}")
335
return None, None
336
337
def safe_exr_write(filename, header, channels):
338
"""Safely write EXR file with validation."""
339
340
# Validate header
341
required_keys = ["compression", "type"]
342
for key in required_keys:
343
if key not in header:
344
raise ValueError(f"Missing required header key: {key}")
345
346
# Validate channels
347
if not channels:
348
raise ValueError("No channels provided")
349
350
try:
351
with OpenEXR.File(header, channels) as outfile:
352
outfile.write(filename)
353
print(f"Successfully wrote: {filename}")
354
355
except Exception as e:
356
print(f"Write error: {e}")
357
raise
358
359
# Usage
360
header, channels = safe_exr_read("input.exr")
361
if header and channels:
362
safe_exr_write("output.exr", header, channels)
363
```