Skip to content

Commit

Permalink
restore README.md edits
Browse files Browse the repository at this point in the history
  • Loading branch information
cary-ilm committed Jul 25, 2024
1 parent dcab86c commit e1637ed
Showing 1 changed file with 137 additions and 36 deletions.
173 changes: 137 additions & 36 deletions src/wrappers/python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,13 @@ package.

## Python Module

The OpenEXR python module provides rudimentary support for reading and
writing basic scanline image data. Many features of the file format
are not yet supported, including:

- Writing of tiled images
- Multiresoltion images
- Deep image data
- Some attribute types
- Nonunity channel sampling frequencies
- No support for interleaved channel data
The OpenEXR python module provides full support for reading and
writing all types of ``.exr`` image files, including scanline, tiled,
deep, mult-part, multi-view, and multi-resolution images with pixel
types of unsigned 32-bit integers and 16- and 32-bit floats. It
provides access to pixel data through numpy arrays, as either one
array per channel or with R, G, B, and A interleaved into a single
array RGBA array.

## Project Governance

Expand All @@ -69,31 +66,135 @@ for more information.

# Quick Start

<!-- this code is replicated as a test in -->
<!-- src/wrappers/python/test/test_readme.py -->

The "hello, world" image writer:

import OpenEXR, Imath
from array import array

width = 10
height = 10
size = width * height

h = OpenEXR.Header(width,height)
h['channels'] = {'R' : Imath.Channel(Imath.PixelType(Imath.PixelType.FLOAT)),
'G' : Imath.Channel(Imath.PixelType(Imath.PixelType.FLOAT)),
'B' : Imath.Channel(Imath.PixelType(Imath.PixelType.FLOAT)),
'A' : Imath.Channel(Imath.PixelType(Imath.PixelType.FLOAT))}
o = OpenEXR.OutputFile("hello.exr", h)
r = array('f', [n for n in range(size*0,size*1)]).tobytes()
g = array('f', [n for n in range(size*1,size*2)]).tobytes()
b = array('f', [n for n in range(size*2,size*3)]).tobytes()
a = array('f', [n for n in range(size*3,size*4)]).tobytes()
channels = {'R' : r, 'G' : g, 'B' : b, 'A' : a}
o.writePixels(channels)
o.close()
The "Hello, World" image writer:

# Generate a 3D NumPy array for RGB channels with random values
height, width = (20, 10)
RGB = np.random.rand(height, width, 3).astype('f')

channels = { "RGB" : RGB }
header = { "compression" : OpenEXR.ZIP_COMPRESSION,
"type" : OpenEXR.scanlineimage }

with OpenEXR.File(header, channels) as outfile:
outfile.write("readme.exr")

Or alternatively, construct the same output file via separate pixel arrays
for each channel:

# Generate arrays for R, G, and B channels with random values
height, width = (20, 10)
R = np.random.rand(height, width).astype('f')
G = np.random.rand(height, width).astype('f')
B = np.random.rand(height, width).astype('f')
channels = { "R" : R, "G" : G, "B" : B }
header = { "compression" : OpenEXR.ZIP_COMPRESSION,
"type" : OpenEXR.scanlineimage }

with OpenEXR.File(header, channels) as outfile:
outfile.write("readme.exr")

The corresponding example of reading an image is:

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

RGB = infile.channels()["RGB"].pixels
height, width, _ = RGB.shape
for y in range(height):
for x in range(width):
pixel = tuple(RGB[y, x])
print(f"pixel[{y}][{x}]={pixel}")

Or alternatively, read the data as separate arrays for each channel:

with OpenEXR.File("readme.exr", separate_channels=True) as infile:

header = infile.header()
print(f"type={header['type']}")
print(f"compression={header['compression']}")

R = infile.channels()["R"].pixels
G = infile.channels()["G"].pixels
B = infile.channels()["B"].pixels
height, width = R.shape
for y in range(height):
for x in range(width):
pixel = (R[y, x], G[y, x], B[y, x])
print(f"pixel[{y}][{x}]={pixel}")

To modify the header metadata in a file:

with OpenEXR.File("readme.exr") as f:
f.header()["displayWindow"] = ((3,4),(5,6))
f.header()["screenWindowCenter"] = np.array([1.0,2.0],'float32')
f.header()["comments"] = "test image"
f.header()["longitude"] = -122.5
f.write("readme_modified.exr")

with OpenEXR.File("readme_modified.exr") as o:
dw = o.header()["displayWindow"]
assert (tuple(dw[0]), tuple(dw[1])) == ((3,4),(5,6))
swc = o.header()["screenWindowCenter"]
assert tuple(swc) == (1.0, 2.0)
assert o.header()["comments"] == "test image"
assert o.header()["longitude"] == -122.5

Note that OpenEXR's Imath-based vector and matrix attribute values
appear in the header dictionary as 2-element, 3-element, 3x3, 4x4
numpy arrays, and bounding boxes appear as tuples of 2-element arrays,
or tuples for convenience.

To read and write a multi-part file, use a list of ``Part`` objects:

height, width = (20, 10)
Z0 = np.zeros((height, width), dtype='f')
Z1 = np.ones((height, width), dtype='f')

P0 = OpenEXR.Part({}, {"Z" : Z0 })
P1 = OpenEXR.Part({}, {"Z" : Z1 })

f = OpenEXR.File([P0, P1])
f.write("readme_2part.exr")

with OpenEXR.File("readme_2part.exr") as o:
assert o.parts[0].name() == "Part0"
assert o.parts[0].width() == 10
assert o.parts[0].height() == 20
assert o.parts[1].name() == "Part1"
assert o.parts[1].width() == 10
assert o.parts[1].height() == 20

Deep data is stored in a numpy array whose entries are numpy
arrays. Construct a numpy array with a ``dtype`` of ``object``, and
assign each entry a numpy array holding the samples. Each pixel can
have a different number of samples, including ``None`` for no data,
but all channels in a given part must have the same number of samples.

height, width = (20, 10)

Z = np.empty((height, width), dtype=object)
for y in range(height):
for x in range(width):
Z[y, x] = np.array([y*width+x], dtype='uint32')

channels = { "Z" : Z }
header = { "compression" : OpenEXR.ZIPS_COMPRESSION,
"type" : OpenEXR.deepscanline }
with OpenEXR.File(header, channels) as outfile:
outfile.write("readme_test_tiled_deep.exr")

To read a deep file:

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

Z = infile.channels()["Z"].pixels
height, width = Z.shape
for y in range(height):
for x in range(width):
for z in Z[y,x]:
print(f"deep sample at {y},{x}: {z}")


# Community

Expand Down Expand Up @@ -126,7 +227,7 @@ The "hello, world" image writer:

- Sign the [Contributor License
Agreement](https://contributor.easycla.lfx.linuxfoundation.org/#/cla/project/2e8710cb-e379-4116-a9ba-964f83618cc5/user/564e571e-12d7-4857-abd4-898939accdd7)

- Submit a Pull Request: https://github.com/AcademySoftwareFoundation/openexr/pulls

# Resources
Expand Down

0 comments on commit e1637ed

Please sign in to comment.