\chapter{Python Bindings}
\label{chap:pythonbindings}
\indexapi{Python|()}

\section{Overview}

\OpenImageIO provides Python language bindings for much of its
functionality.

\smallskip

You must ensure that the environment variable {\cf PYTHONPATH} includes
the {\cf python} subdirectory of the \OpenImageIO installation.

\smallskip

A Python program must import the {\cf OpenImageIO} package:
\begin{code}
    import OpenImageIO
\end{code}
\noindent In most of our examples below, we assume that for the sake
of brevity, we will alias the package name as follows:
\begin{code}
    import OpenImageIO as oiio
    from OpenImageIO import ImageInput, ImageOutput
    from OpenImageIO import ImageBuf, ImageSpec, ImageBufAlgo
\end{code}

\section{TypeDesc}
\label{sec:pythontypedesc}

The \TypeDesc class that describes data types of pixels and metadata,
described in detail in Section~\ref{sec:TypeDesc}, is replicated for Python.

\apiitem{BASETYPE}
The {\cf BASETYPE} enum corresponds to the C++ {\cf TypeDesc::BASETYPE} and
contains the following values: \\
{\cf UNKNOWN NONE UINT8 INT8 UINT16 INT16 UINT32 INT32 UINT64 INT64 \\
HALF FLOAT DOUBLE STRING PTR} \\
These names are also exported to the {\cf OpenImageIO} namespace.
\apiend

\apiitem{AGGREGATE}
The {\cf AGGREGATE} enum corresponds to the C++ {\cf TypeDesc::AGGREGATE} and
contains the following values: \\
{\cf SCALAR VEC2 VEC3 VEC4 MATRIX44} \\
These names are also exported to the {\cf OpenImageIO} namespace.
\apiend

\apiitem{VECSEMANTICS}
The {\cf VECSEMANTICS} enum corresponds to the C++ {\cf TypeDesc::VECSEMANTICS} and
contains the following values: \\
{\cf NOSEMANTICS COLOR POINT VECTOR NORMAL TIMECODE KEYCODE} \\
These names are also exported to the {\cf OpenImageIO} namespace.
\apiend

\apiitem{TypeDesc () \\
TypeDesc (basetype) \\
TypeDesc (basetype, aggregate) \\
TypeDesc (basetype, aggregate, vecsemantics) \\
TypeDesc (basetype, aggregate, vecsemantics, arraylen) \\
TypeDesc (str)}

Construct a {\cf TypeDesc} object.  

\noindent Examples:
\begin{code}
    import OpenImageIO as oiio

    # make a default (UNKNOWN) TypeDesc
    t = TypeDesc()

    # make a TypeDesc describing an unsigned 8 bit int
    t = TypeDesc(oiio.UINT8)

    # make a TypeDesc describing an array of 14 'half' values
    t = TypeDesc(oiio.HALF, oiio.SCALAR, oiio.NOSEMANTICS, 14)

    # make a TypeDesc describing a float point
    t = TypeDesc(oiio.FLOAT, oiio.VEC3, oiio.POINT)

    # Some constructors from a string description
    t = TypeDesc("uint8")
    t = TypeDesc("half[14]")
    t = TypeDesc("point")     # equiv to FLOAT, VEC3, POINT
\end{code}
\apiend

\apiitem{TypeDesc.TypeFloat() \\
TypeDesc.TypeInt() \\
TypeDesc.TypeString() \\
TypeDesc.TypeColor() \\
TypeDesc.TypePoint() \\
TypeDesc.TypeVector() \\
TypeDesc.TypeNormal() \\
TypeDesc.TypeMatrix()}
Pre-constructed \TypeDesc objects for some common types.

\noindent Example:
\begin{code}
    t = TypeDesc.TypeFloat()
\end{code}
\apiend

\apiitem{string {\ce str} (TypeDesc)}
Returns a string that describes the \TypeDesc.

\noindent Example:
\begin{code}
    print str(TypeDesc(oiio.UINT16))

    > int16
\end{code}
\apiend

\apiitem{TypeDesc.{\ce basetype} \\
TypeDesc.{\ce aggregate} \\
TypeDesc.{\ce vecsemantics} \\
TypeDesc.{\ce arraylen}}
Access to the raw fields in the \TypeDesc.

\noindent Example:
\begin{code}
    t = TypeDesc(...)
    if t.basetype == oiio.FLOAT :
        print "It's made of floats"
\end{code}
\apiend

\apiitem{int TypeDesc.{\ce size} () \\
int TypeDesc.{\ce basesize} () \\
TypeDesc TypeDesc.{\ce elementtype} () \\
int TypeDesc.{\ce numelements} () \\
int TypeDesc.{\ce elementsize} ()}
The {\cf size()} is the size in bytes, of the type described.  The
{\cf basesize()} is the size in bytes of the {\cf basetype}.

The {\cf elementtype()} is the type of each array element, if it is an
array, or just the full type if it is not an array.  The {\cf elementsize()}
is the size, in bytes, of the {\cf elementtype} (thus, returning the same
value as {\cf size()} if the type is not an array).  The {\cf numelements()}
method returns {\cf arraylen} if it is an array, or {\cf 1} if it is not
an array.

\noindent Example:
\begin{code}
    t = TypeDesc("point[2]")
    print "size =", t.size()
    print "elementtype =", t.elementtype()
    print "elementsize =", t.elementsize()

    > size = 24
    > elementtype = point
    > elementsize = 12
\end{code}
\apiend

\apiitem{bool typedesc {\ce ==} typedesc \\
bool typedesc {\ce !=} typedesc \\
bool TypeDesc.{\ce equivalent} (typedesc) \\}
Test for equality or inequality.  The {\cf equivalent()} method is more
forgiving than {\cf ==}, in that it considers {\cf POINT}, {\cf VECTOR},
and {\cf NORMAL} vector semantics to not constitute a difference from one
another.

\noindent Example:
\begin{code}
    f = TypeDesc("float")
    p = TypeDesc("point")
    v = TypeDesc("vector")
    print "float==point?", (f == p)
    print "vector==point?", (v == p)
    print "float.equivalent(point)?", f.equivalent(p)
    print "vector.equivalent(point)?", v.equivalent(p)

    > float==point? False
    > vector==point? False
    > float.equivalent(point)? False
    > vector.equivalent(point)? True
\end{code}
\apiend


\section{ROI}
\label{sec:pythonroi}

The \ROI class that describes an image extent or region of interest,
explained in deail in Section~\ref{sec:ROI}, is replicated for Python.

\apiitem{{\ce ROI} () \\
{\ce ROI} (xbegin, xend, ybegin, yend) \\
{\ce ROI} (xbegin, xend, ybegin, yend, zbegin, zend) \\
{\ce ROI} (xbegin, xend, ybegin, yend, zbegin, zend, chbegin, chend)}
Construct an \ROI with the given bounds.  The constructor with no 
arguments makes an \ROI that is ``undefined.''

\noindent Example:
\begin{code}
    import OpenImageIO as oiio
    ...
    roi = ROI (0, 640, 0, 480, 0, 1, 0, 4)   # video res RGBA
\end{code}
\apiend

\apiitem{int ROI.{\ce xbegin} \\
int ROI.{\ce xend} \\
int ROI.{\ce ybegin} \\
int ROI.{\ce yend} \\
int ROI.{\ce zbegin} \\
int ROI.{\ce zend} \\
int ROI.{\ce chbegin} \\
int ROI.{\ce chend}}
The basic fields of the \ROI.
\apiend

\apiitem{ROI ROI.{\ce All}}
A pre-constructed undefined \ROI.
\apiend

\apiitem{bool ROI.{\ce defined}}
{\cf True} if the \ROI is defined, {\cf False} if the \ROI is undefined.
\apiend

\apiitem{int ROI.{\ce width} \\
int ROI.{\ce height} \\
int ROI.{\ce depth} \\
int ROI.{\ce nchannels}}
The number of pixels in each dimension, and the number of channels,
as described by the \ROI.
\apiend

\apiitem{int ROI.{\ce npixels}}
The total number of pixels in the region described by the \ROI.
\apiend


\apiitem{ROI {\ce get_roi} (imagespec) \\
ROI {\ce get_roi_full} (imagespec)}
Returns the \ROI corresponding to the pixel data window of the given
\ImageSpec, or the display/full window, respectively.

\noindent Example:
\begin{code}
    spec = ImageSpec(...)
    roi = oiio.get_roi(spec)
\end{code}
\apiend

\apiitem{{\ce set_roi} (imagespec, roi) \\
{\ce set_roi_full} (imagespec, roi)}
Alter the \ImageSpec's resolution and offset to match the passed \ROI.

\noindent Example:
\begin{code}
    # spec is an ImageSpec
    # The following sets the full (display) window to be equal to the
    # pixel data window:
    oiio.set_roi_full (spec, oiio.get_roi(spec))
\end{code}
\apiend


\section{ImageSpec}
\label{sec:pythonimagespec}

The \ImageSpec class that describes an image, explained in deail in
Section~\ref{sec:ImageSpec}, is replicated for Python.

\apiitem{{\ce ImageSpec} ()\\
{\ce ImageSpec} (basetype) \\
{\ce ImageSpec} (typedesc) \\
{\ce ImageSpec} (xres, yres, nchannels, basetype) \\
{\ce ImageSpec} (xres, yres, nchannels, typespec)}
Constructors of an \ImageSpec. These correspond directly to the constructors
in the C++ bindings.

\noindent Example:
\begin{code}
    import OpenImageIO as oiio
    ...

    # default ctr
    s = ImageSpec()

    # construct with known pixel type, unknown resolution
    s = ImageSpec(oiio.UINT8)

    # construct with known resolution, channels, pixel data type
    s = ImageSpec(640, 480, 4, oiio.HALF)
\end{code}
\apiend

\apiitem{ImageSpec.{\ce width}, ImageSpec.{\ce height}, ImageSpec.{\ce depth} \\
ImageSpec.{\ce x}, ImageSpec.{\ce y}, ImageSpec.{\ce z}}
Resolution and offset of the image data ({\cf int} values).

\noindent Example:
\begin{code}
    s = ImageSpec (...)
    print "Data window is ({},{})-({},{})".format (s.x, s.x+s.width-1,
                                                   s.y, s.y+s.height-1)
\end{code}
\apiend

\apiitem{ImageSpec.{\ce full_width}, ImageSpec.{\ce full_height}, ImageSpec.{\ce full_depth} \\
ImageSpec.{\ce full_x}, ImageSpec.{\ce full_y}, ImageSpec.{\ce full_z}}
Resolution and offset of the ``full'' display window ({\cf int} values).
\apiend

\apiitem{ImageSpec.{\ce tile_width}, ImageSpec.{\ce tile_height}, ImageSpec.{\ce tile_depth}}
For tiled images, the resolution of the tiles ({\cf int} values).  Will be
{\cf 0} for  untiled images.
\apiend

\apiitem{typedesc ImageSpec.{\ce format}}
A \TypeDesc describing the pixel data.
\apiend

\apiitem{int ImageSpec.{\ce nchannels}}
An {\cf int} giving the number of color channels in the image.
\apiend

\apiitem{ImageSpec.{\ce channelnames}}
A tuple of strings containing the names of each color channel.
\apiend

\apiitem{ImageSpec.{\ce channelformats}}
If all color channels have the same format, that will be {\cf ImageSpec.format},
and {\cf channelformats} will be {\cf None}.  However, if there are different
formats per channel, they will be stored in {\cf channelformats} as a tuple
of BASETYPE values, and {\cf format} will contain the ``widest'' of them.

\noindent Example:
\begin{code}
    if spec.channelformats == None:
        print "All color channels are", str(spec.format)
    else:
        print "Channel formats: "
        for i in range(len(spec.channelformats)):
            print "\t", str(TypeDesc(spec.channelformats[i]))
\end{code}
\apiend

\apiitem{ImageSpec.{\ce alpha_channel} \\
ImageSpec.{\ce z_channel}}
The channel index containing the alpha or depth channel, respectively, or
-1 if either one does not exist or cannot be identified.
\apiend

\apiitem{ImageSpec.{\ce deep}}
Hold {\cf True} if the image is a \emph{deep} (multiple samples per pixel)
image, of {\cf False} if it is an ordinary image.
\apiend

\apiitem{ImageSpec.{\ce set_format} (basetype) \\
ImageSpec.{\ce set_format} (typedesc)}
Given a {\cf BASETYPE} or a \TypeDesc, sets the {\cf format} field and
also sets all the {\cf quantize} fields to the defaults for the given
data format.

\noindent Example:
\begin{code}
    s = ImageSpec ()
    s.set_format (oiio.UINT8)
\end{code}
\apiend

\apiitem{ImageSpec.{\ce default_channel_names} ()}
Sets {\cf channel_names} to the default names given the value of
the {\cf nchannels} field.
\apiend

\apiitem{ImageSpec.{\ce channel_bytes} () \\
ImageSpec.{\ce channel_bytes} (channel, native=False)}
Returns the size of a single channel value, in bytes (as an
{\cf int}).
(Analogous to the C++ member functions, see 
Section~\ref{sec:ImageSpecMemberFuncs} for details.)
\apiend

\apiitem{ImageSpec.{\ce pixel_bytes} () \\
ImageSpec.{\ce pixel_bytes} (native) \\
ImageSpec.{\ce pixel_bytes} (chbegin, chend) \\
ImageSpec.{\ce pixel_bytes} (chbegin, chend, native=False)}
Returns the size of a pixel, in bytes (as an {\cf int}).
(Analogous to the C++ member functions, see 
Section~\ref{sec:ImageSpecMemberFuncs} for details.)
\apiend

\apiitem{ImageSpec.{\ce scanline_bytes} (native=False) \\
ImageSpec.{\ce tile_bytes} (native=False) \\
ImageSpec.{\ce image_bytes} (native=False)}
Returns the size of a scanline, tile, or the full image, in bytes (as an
{\cf int}). (Analogous to the C++ member functions, see 
Section~\ref{sec:ImageSpecMemberFuncs} for details.)
\apiend

\apiitem{ImageSpec.{\ce tile_pixels} () \\
ImageSpec.{\ce image_pixels} ()}
Returns the number of pixels in a tile or the full image, respectively
(as an {\cf int}). (Analogous to the C++ member functions, see 
Section~\ref{sec:ImageSpecMemberFuncs} for details.)
\apiend

\apiitem{ImageSpec.{\ce attribute} (name, int) \\
ImageSpec.{\ce attribute} (name, float) \\
ImageSpec.{\ce attribute} (name, string) \\
ImageSpec.{\ce attribute} (name, typedesc, data) \\}
Sets a metadata value in the {\cf extra_attribs}.  If the metadata item
is a single {\cf int}, {\cf float}, or {\cf string}, you can pass it
directly. For other types, you must pass the \TypeDesc and then the
data (for aggregate types or arrays, pass multiple values as a tuple).

\noindent Example:
\begin{code}
    s = ImageSpec (...)
    s.attribute ("foo_str", "blah")
    s.attribute ("foo_int", 14)
    s.attribute ("foo_float", 3.14)
    s.attribute ("foo_vector", TypeDesc.TypeVector, (1, 0, 11))
    s.attribute ("foo_matrix", TypeDesc.TypeMatrix,
                 (1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 1, 2, 3, 1))
\end{code}
\apiend

\apiitem{ImageSpec.{\ce get_attribute} (name) \\
ImageSpec.{\ce get_attribute} (name, typedesc)}
Retrieves a named metadata value from {\cf extra_attribs}.  The generic
{\cf get_attribute()} function returns it regardless of type, or {\cf None}
if the attribute does not exist.  The typed variety will only succeed
if the attribute is actually of that type specified.

\noindent Example:
\begin{code}
    foo = s.get_attribute ("foo")   # None if not found
    foo = s.get_attribute ("foo", oiio.FLOAT)  # None if not found AND float
\end{code}
\apiend

\apiitem{ImageSpec.{\ce get_int_attribute} (name, defaultval=0) \\
ImageSpec.{\ce get_float_attribute} (name, defaultval=0.0) \\
ImageSpec.{\ce get_string_attribute} (name, defaultval="")}
Retrieves a named metadata value from {\cf extra_attribs}, if it is
found and is of the given type; returns the default value (or a passed
value) if not found.

\noindent Example:
\begin{code}
    # If "foo" is not found, or if it's not an int, return 0
    foo = s.get_int_attribute ("foo")

    # If "foo" is not found, or if it's not a string, return "blah"
    foo = s.get_string_attribute ("foo", "blah")
\end{code}
\apiend

\apiitem{ImageSpec.{\ce extra_attribs}}
Direct access to the {\cf extra_attribs} named metadata, appropriate for
iterating over the entire list rather than searching for a particular named
value.

\vspace{-10pt}
\apiitem{len(extra_attribs)}
\vspace{10pt}
Returns the number of extra attributes.
\apiend
\vspace{-24pt}
\apiitem{extra_attribs[i].name}
\vspace{10pt}
The name of the indexed attribute.
\apiend
\vspace{-24pt}
\apiitem{extra_attribs[i].type}
\vspace{10pt}
The type of the indexed attribute, as a \TypeDesc.
\apiend
\vspace{-24pt}
\apiitem{extra_attribs[i].value}
\vspace{10pt}
The value of the indexed attribute.
\apiend

\noindent Example:
\begin{code}
    s = ImageSpec(...)
    ...
    print "extra_attribs size is", len(s.extra_attribs)
    for i in range(len(s.extra_attribs)) :
        print i, s.extra_attribs[i].name, s.extra_attribs[i].type, " :"
        print "\t", s.extra_attribs[i].value
    print
\end{code}
\apiend


\newpage
\subsection*{Example: Header info}

Here is an illustrative example of the use of \ImageSpec, a working Python
function that opens a file and prints all the relevant header
information:

\begin{tinycode}
#!/usr/bin/env python 
import OpenImageIO as oiio

# Print the contents of an ImageSpec
def print_imagespec (spec, subimage=0, mip=0) :
    if spec.depth <= 1 :
        print ("  resolution %dx%d%+d%+d" % (spec.width, spec.height, spec.x, spec.y))
    else :
        print ("  resolution %dx%d%x%d+d%+d%+d" % 
               (spec.width, spec.height, spec.depth, spec.x, spec.y, spec.z))
    if (spec.width != spec.full_width or spec.height != spec.full_height
        or spec.depth != spec.full_depth) :
        if spec.full_depth <= 1 :
            print ("  full res   %dx%d%+d%+d" % 
                   (spec.full_width, spec.full_height, spec.full_x, spec.full_y))
        else :
            print ("  full res   %dx%d%x%d+d%+d%+d" % 
                   (spec.full_width, spec.full_height, spec.full_depth,
                    spec.full_x, spec.full_y, spec.full_z))
    if spec.tile_width :
        print ("  tile size  %dx%dx%d" % 
               (spec.tile_width, spec.tile_height, spec.tile_depth))
    else :
        print "  untiled"
    if mip >= 1 :
        return
    print "  " + str(spec.nchannels), "channels:", spec.channelnames
    print "  format = ", str(spec.format)
    if spec.channelformats :
        print "  channelformats = ", spec.channelformats
    print "  alpha channel = ", spec.alpha_channel
    print "  z channel = ", spec.z_channel
    print "  deep = ", spec.deep
    for i in range(len(spec.extra_attribs)) :
        if type(spec.extra_attribs[i].value) == str :
            print " ", spec.extra_attribs[i].name, "= \"" + spec.extra_attribs[i].value + "\""
        else :
            print " ", spec.extra_attribs[i].name, "=", spec.extra_attribs[i].value


def poor_mans_iinfo (filename) :
    input = ImageInput.open (filename)
    if not input :
        print 'Could not open "' + filename + '"'
        print "\tError: ", oiio.geterror()
        return
    print 'Opened "' + filename + '" as a ' + input.format_name()
    sub = 0
    mip = 0
    while True :
        if sub > 0 or mip > 0 :
            print "Subimage", sub, "MIP level", mip, ":"
        print_imagespec (input.spec(), mip=mip)
        mip = mip + 1
        if input.seek_subimage (sub, mip) :
            continue    # proceed to next MIP level
        else :
            sub = sub + 1
            mip = 0
            if input.seek_subimage (sub, mip) :
                continue    # proceed to next subimage
        break  # no more MIP levels or subimages
    input.close ()
\end{tinycode}


\section{ImageInput}
\label{sec:pythonimageinput}

See Chapter~\ref{chap:imageinput} for detailed explanations of the
C++ \ImageInput class APIs. The Python APIs are very similar. The biggest
difference is that in C++, the various {\cf read_*} functions write the
pixel values into an already-allocated array that belongs to the caller,
whereas the Python versions allocate and return an array holding the pixel
values (or {\cf None} if the read failed).


\apiitem{ImageInput.{\ce open} (filename) \\
ImageInput.{\ce open} (filename, config_imagespec)}
Creates an \ImageInput object and opens the named file.  Returns the
open \ImageInput upon success, or {\cf None} if it failed to open the
file (after which, {\cf OpenImageIO.geterror()} will contain an error
message).  In the second form, the optional \ImageSpec argument 
{\cf config} contains attributes that may set certain options when opening
the file.

\noindent Example:
\begin{code}
    input = ImageInput.open ("tahoe.jpg")
    if input == None :
        print "Error:", oiio.geterror()
        return
\end{code}
\apiend

\apiitem{bool ImageInput.{\ce close} ()}
Closes an open image file, returning {\cf True} if successful, {\cf False}
otherwise.

\noindent Example:
\begin{code}
    input = ImageInput.open (filename)
    ...
    input.close ()
\end{code}
\apiend


\apiitem{str ImageInput.{\ce format_name} ()}
Returns the format name of the open file.

\noindent Example:
\begin{code}
    input = ImageInput.open (filename)
    if input :
        print filename, "was a", input.format_name(), "file."
        input.close ()
  
\end{code}
\apiend

\apiitem{ImageSpec ImageInput.{\ce spec} ()}
Returns the \ImageSpec corresponding to the currently open subimage and
MIP level of the file.

\noindent Example:
\begin{code}
    input = ImageInput.open (filename)
    spec = input.spec()
    print "resolution ", spec.width, "x", spec.height
\end{code}
\apiend

\apiitem{int ImageInput.{\ce current_subimage} () \\
int ImageInput.{\ce current_miplevel} ()}
Returns the current subimage and/or MIP level of the file.
\apiend

\apiitem{bool ImageInput.{\ce seek_subimage} (subimage, miplevel)}
Repositions the file pointer to the given subimage and MIP level within the
file (starting with {\cf 0}).  This function returns {\cf True} upon success,
{\cf False} upon failure (which may include the file not having the
specified subimage or MIP level).

\noindent Example:
\begin{code}
    input = ImageInput.open (filename)
    mip = 0
    while True :
        ok = input.seek_subimage (0, mip)
        if not ok :
            break
        spec = input.spec()
        print "MIP level", mip, "is", spec.width, "x", spec.height
\end{code}
\apiend

\apiitem{array ImageInput.{\ce read_image} (type=OpenImageIO.UNKNOWN)}
Read the entire image and return the pixels as an array of
$\mathit{width} \times \mathit{height} \times \mathit{depth} \times \mathit{nchannels}$
values (or {\cf None} if an error occurred).  The array will be of the type
specified by the {\cf TypeDesc} or {\cf BASETYPE} argument, or  if the
argument is missing or is {\cf TypeDesc(oiio.UNKNOWN)}, it will choose an
appropriate data type that is the ``smallest'' type that can hold the actual
types in the file.

\noindent Example:
\begin{code}
    input = ImageInput.open (filename)
    spec = input.spec ()
    pixels = input.read_image (oiio.FLOAT)
    print "The first pixel is", pixels[0:spec.nchannels]
    print "The second pixel is", pixels[spec.nchannels:(2*spec.nchannels)]
    input.close ()
\end{code}
\apiend

\apiitem{array ImageInput.{\ce read_scanline} (y, z, type=OpenImageIO.UNKNOWN)}
Read scanline number {\cf y} from depth plane {\cf z} from the open file,
returning it as an array of $\mathit{width} \times \mathit{nchannels}$
values (or {\cf None} if an error occurred). The array will be of the type
specified by the {\cf TypeDesc} or {\cf BASETYPE} argument, or if the
argument is missing or is {\cf TypeDesc(oiio.UNKNOWN)}, it will choose an
appropriate data type.

\noindent Example:
\begin{code}
    input = ImageInput.open (filename)
    spec = input.spec ()
    if spec.tile_width == 0 :
        for y in range(spec.y, spec.y+spec.height) :
            pixels = input.read_scanline (y, spec.z, oiio.FLOAT)
            # process the scanline
    else :
        print "It's a tiled file"
    input.close ()
\end{code}
\apiend

\apiitem{array ImageInput.{\ce read_tile} (x, y, z, type=OpenImageIO.UNKNOWN)}
Read the tile whose upper left corner is pixel {\cf (x,y,z)} from the open
file, returning it as an array of
$\mathit{width} \times \mathit{height} \times \mathit{depth} \times \mathit{nchannels}$
values (or {\cf None} if an error occurred). The array will be of the type
specified by the {\cf TypeDesc} or {\cf BASETYPE} argument, or if the
argument is missing or {\cf TypeDesc(oiio.UNKNOWN)}, it will choose an
appropriate data type.

\noindent Example:
\begin{code}
    input = ImageInput.open (filename)
    spec = input.spec ()
    if spec.tile_width > 0 :
        for z in range(spec.z, spec.z+spec.depth, spec.tile_depth) :
            for y in range(spec.y, spec.y+spec.height, spec.tile_height) :
                for x in range(spec.x, spec.x+spec.width, spec.tile_width) :
                    pixels = input.read_tile (x, y, z, oiio.FLOAT)
                    # process the tile
    else :
        print "It's a scanline file"
    input.close ()
\end{code}
\apiend

\apiitem{array ImageInput.{\ce read_scanlines} (ybegin, yend, z, chbegin, chend, \\
\bigspc\bigspc\spc type=OpenImageIO.UNKNOWN) \\
array ImageInput.{\ce read_tiles} (xbegin, xend, ybegin, yend, zbegin, zend, \\
    \bigspc\bigspc\spc chbegin, chend, type=OpenImageIO.UNKNOWN)}
Similar to the C++ routines, these functions read multiple scanlines or 
tiles at once, which in some cases may be more efficient than reading
each scanline or tile separately.  Additionally, they allow you to read only
a subset of channels.

\noindent Example:
\begin{code}
    input = ImageInput.open (filename)
    spec = input.spec ()

    # Read the whole image, the equivalent of
    #     pixels = input.read_image (type)
    # but do it using read_scanlines or read_tiles:
    if spec.tile_width == 0 :
        pixels = input.read_scanlines (spec.y, spec.y+spec.height, 0,
                                       0, spec.nchannels, oiio.FLOAT)
    else :
        pixels = input.read_tiles (spec.x, spec.x+spec.width,
                                   spec.y, spec.y+spec.height,
                                   spec.z, spec.z+spec.depth,
                                   0, spec.nchannels, oiio.FLOAT)
\end{code}
\apiend

\apiitem{str ImageInput.{\ce geterror} ()}
Retrieves the error message from the latest failed operation on an
ImageInput.

\noindent Example:
\begin{code}
    input = ImageInput.open (filename)
    if not input :
        print "Open error:", oiio.geterror()
        # N.B. error on open must be retrieved with the global geterror(),
        # since there is no ImageInput object!
    else :
        pixels = input.read_image (oiio.FLOAT)
        if not pixels :
            print "Read_image error:", input.geterror()
        input.close ()
\end{code}
\apiend

\newpage
\subsection*{Example: Reading pixel values from a file to find min/max}

\begin{code}
#!/usr/bin/env python 
import OpenImageIO as oiio

def find_min_max (filename) :
    input = ImageInput.open (filename)
    if not input :
        print 'Could not open "' + filename + '"'
        print "\tError: ", oiio.geterror()
        return
    spec = input.spec()
    nchans = spec.nchannels
    pixels = input.read_image(oiio.FLOAT)
    if not pixels :
        print "Could not read:", input.geterror()
        return
    input.close()    # we're done with the file at this point
    minval = pixels[0:nchans]   # initialize to the first pixel value
    maxval = pixels[0:nchans]
    i = 0    # absolute index
    for z in range(spec.depth) :
        for y in range(spec.height) :
            for x in range(spec.width) :
                for c in range(nchans) :
                    if pixels[i+c] < minval[c] :
                        minval[c] = pixels[i+c]
                    if pixels[i+c] > maxval[c] :
                        maxval[c] = pixels[i+c]
                i = i + nchans   # advance the index
    print "Min values per channel were", minval
    print "Max values per channel were", maxval
\end{code}
\newpage


\section{ImageOutput}
\label{sec:pythonimageoutput}

See Chapter~\ref{chap:imageoutput} for detailed explanations of the
C++ \ImageOutput class APIs. The Python APIs are very similar.

\apiitem{ImageOutput ImageOutput.{\ce create} (fileformat, plugin_searchpath="")}

Create a new \ImageOutput capable of writing the named file format (which may
also be a file name, with the type deduced from the extension).  There
is an optional parameter giving an colon-separated search path for finding
\ImageOutput plugins.  The function returns an \ImageOutput object, or
{\cf None} upon error (in which case, {OpenImageIO.geterror()} may be used
to retrieve the error message).

\noindent Example:
\begin{code}
    import OpenImageIO as oiio
    output = ImageOutput.create ("myfile.tif")
    if not output :
        print "Error:", oiio.geterror()
\end{code}
\apiend

\apiitem{str ImageOutput.{\ce format_name} ()}
The file format name of a created \ImageOutput.

\noindent Example:
\begin{code}
    output = ImageOutput.create (filename)
    if output :
        print "Created output", filename, "as a", output.format_name()
\end{code}
\apiend

\apiitem{bool ImageOutput.{\ce supports} (feature)}
For a created \ImageOutput, returns {\cf True} if the file format supports
the named feature (such as \qkw{tiles}, \qkw{mipmap}, etc., see
Section~\ref{sec:supportsfeaturelist} for the full list), or {\cf False}
if this file format does not support the feature.

\noindent Example:
\begin{code}
    output = ImageOutput.create (filename)
    if output :
        print output.format_name(), "supports..."
        print "tiles?", output.supports("tiles")
        print "multi-image?", output.supports("multiimage")
        print "MIP maps?", output.supports("mipmap")
        print "per-channel formats?", output.supports("channelformats")
\end{code}
\apiend

\apiitem{bool ImageOutput.{\ce open} (filename, imagespec, mode)}
Opens the named output file, with an \ImageSpec describing the image to
be output.  The {\cf mode} may be one of {\cf OpenImageIO.Create},
{\cf OpenImageIO.AppendSubimage}, or {\cf OpenImageIO.AppendMIPLevel}.
See Section~\ref{sec:imageoutputopen} for details.  Returns {\cf True}
upon success, {\cf False} upon failure (error messages retrieved via
{\cf ImageOutput.geterror()}.)

\noindent Example:
\begin{code}
    output = ImageOutput.create (filename)
    if not output :
        print "Error:", oiio.geterror()
    spec = ImageSpec (640, 480, 3, oiio.UINT8)
    ok = output.open (filename, spec, oiio.Create)
    if not ok :
        print "Could not open", filename, ":", output.geterror()
\end{code}
\apiend

\apiitem{bool ImageOutput.{\ce open} (filename, (imagespec, ...))}
This variety of {\cf open()} is used specifically for multi-subimage files.
A \emph{tuple} of \ImageSpec objects is passed, one for each subimage
that will be written to the file.  After each subimage is written, then
a regular call to {\cf open(name, newspec, {\ce AppendSubimage})} moves
on to the next subimage.
\apiend

\apiitem{bool ImageOutput.{\ce close} ()}
Closes an open output.
\apiend

\apiitem{ImageSpec ImageOutput.{\ce spec} ()}
Retrieves the \ImageSpec of the currently-open output image.
\apiend

\apiitem{bool ImageOutput.{\ce write_image} (typedesc, pixels, xstride=AutoStride, \\
\bigspc\bigspc\spc ystride=AutoStride, zstride=AutoStride)}
Write the currently opened image all at once.  The {\cf pixels} parameter
should be an {\cf array} containing data elements of the type described by
the \TypeDesc.  Optional strides describe data layout in the array (the
default values of {\cf AutoStride} are used if the data are contiguous).
Returns {\cf True} upon success, {\cf False} upon failure.

\noindent Example:
\begin{code}
    # This example reads a scanline file, then converts it to tiled
    # and writes to the same name.

    input = ImageInput.open (filename)
    spec = input.spec ()
    pixels = input.read_image (oiio.FLOAT)
    input.close ()

    output = ImageOutput.create (filename)
    if output.supports("tiles") :
        spec.tile_width = 64
        spec.tile_height = 64
        output.open (filename, spec, oiio.Create)
        output.write_image (oiio.FLOAT, pixels)
        output.close ()
\end{code}
\apiend

\apiitem{bool ImageOutput.{\ce write_scanline} (y, z, typesdesc, pixels, \\
\bigspc\bigspc xstride=AutoStride) \\
bool ImageOutput.{\ce write_scanlines} (ybegin, yend, z, typesdesc, pixels, \\
\bigspc\bigspc xstride=AutoStride)}

Write one or many scanlines to the currently open file.

\noindent Example:
\begin{code}
    # Copy a TIFF image to JPEG by copying scanline by scanline.
    input = ImageInput.open ("in.tif")
    spec = input.spec ()
    output = ImageOutput.create ("out.jpg")
    output.open (filename, spec, oiio.Create)
    for z in range(spec.z, spec.z+spec.depth) :
        for y in range(spec.y, spec.y+spec.height) :
            pixels = input.read_scanline (y, z, oiio.FLOAT)
            output.write_scanline (y, z, oiio.FLOAT, pixels)
    output.close ()
    input.close ()

    # The same example, but copying a whole "plane" of scanlines at a time:
    ...
    for z in range(spec.z, spec.z+spec.depth) :
        pixels = input.read_scanlines (spec.y, spec.y+spec.height,
                                       z, oiio.FLOAT)
        output.write_scanlines (spec.y, spec.y+spec.height,
                                z, oiio.FLOAT, pixels)
    ...
\end{code}
\apiend

\apiitem{bool ImageOutput.{\ce write_tile} (x, y, z, typesdesc, pixels, \\
\bigspc\bigspc\spc xstride=AutoStride, ystride=AutoStride, \\
\bigspc\bigspc\spc zstride=AutoStride) \\
bool ImageOutput.{\ce write_tiles} (xbegin, xend, ybegin, yend, zbegin, zend, \\
\bigspc\bigspc\spc  xstride=AutoStride, ystride=AutoStride, \\
\bigspc\bigspc\spc  zstride=AutoStride)}

Write one or many tiles to the currently open file.

\noindent Example:
\begin{code}
    input = ImageInput.open (in_filename)
    spec = input.spec ()
    output = ImageOutput.create (out_filename)
    output.open (out_filename, spec, oiio.Create)
    for z in range(spec.z, spec.z+spec.depth, spec.tile_depth) :
        for y in range(spec.y, spec.y+spec.height, spec.tile_height) :
            for x in range(spec.x, spec.x+spec.width, spec.tile_width) :
                pixels = input.read_tile (x, y, z, oiio.FLOAT)
                output.read_tile (x, y, z, oiio.FLOAT, pixels)
    output.close ()
    input.close ()

    # The same example, but copying a whole row of of tiles at a time:
    ...
    for z in range(spec.z, spec.z+spec.depth, spec.tile_depth) :
        for y in range(spec.y, spec.y+spec.height, spec.tile_height) :
            pixels = input.read_tiles (spec.x, spec.x+spec.width,
                                       y, y+tile_width,
                                       z, z+tile_width, oiio.FLOAT)
            output.read_tile (spec.x, spec.x+spec.width,
                              y, y+tile_width, z, z+tile_width,
                              oiio.FLOAT, pixels)
    ...
\end{code}
\apiend

\apiitem{bool ImageOutput.{\ce copy_image} (imageinput)}
Copy the current image of the open input to the open output. (The reason
this may be preferred in some circumstances is that, if input and
output were the same kind of input file format, they may have a special
efficient technique to copy pixels unaltered, for example by avoiding the 
decompression/recompression round trip.)

\noindent Example:
\begin{code}
    input = ImageInput.open (in_filename)
    spec = input.spec ()
    output = ImageOutput.create (out_filename)
    output.open (filename, spec, oiio.Create)
    output.copy_image (input)
    output.close ()
    input.close ()
\end{code}
\apiend

\apiitem{str ImageOuput.{\ce geterror} ()}
Retrieves the error message from the latest failed operation on an open
file.

\noindent Example:
\begin{code}
    output = ImageOutput.create (filename)
    if not output :
        print "Create error:", oiio.geterror()
        # N.B. error on create must be retrieved with the global geterror(),
        # since there is no ImageOutput object!
    else :
        ok = output.open (filename, spec, oiio.Create)
        if not ok :
            print "Open error:", output.geterror()
        ok = output.write_image (oiio.FLOAT, pixels)
        if not ok :
            print "Write error:", output.geterror()
        output.close ()
\end{code}
\apiend



\section{ImageBuf}
\label{sec:pythonimagebuf}

See Chapter~\ref{chap:imagebuf} for detailed explanations of the
C++ \ImageBuf class APIs. The Python APIs are very similar.

\apiitem{ImageBuf {\ce ImageBuf} ()}
Construct a new, empty \ImageBuf. The \ImageBuf is uninitialized and is
awaiting a call to {\cf reset()} or {\cf copy()} before it is useful.
\apiend

\apiitem{ImageBuf {\ce ImageBuf} (filename) \\
ImageBuf {\ce ImageBuf} (filename, subimage, miplevel)}

Construct a read-only \ImageBuf that will read from the named file.
Optionally, a specific subimage or MIP level may be specified (defaulting to
0).

\noindent Example:
\begin{code}
    import OpenImageIO as oiio
    ...
    buf = ImageBuf ("grid.tif")
\end{code}
\apiend

\apiitem{ImageBuf {\ce ImageBuf} (imagespec)}

Construct a writeable \ImageBuf of the dimensions and data format specified
by an \ImageSpec.

\noindent Example:
\begin{code}
    spec = ImageSpec (640, 480, 3, oiio.FLOAT)
    buf = ImageBuf (spec)
\end{code}
\apiend

\apiitem{ImageBuf.{\ce clear} ()}
Return the \ImageBuf to its pristine, uninitialized state.

\noindent Example:
\begin{code}
    buf = ImageBuf (...)

    # The following two commands are equivalent:
    buf = ImageBuf()     # 1 - assign a new blank ImageBuf
    buf.clear()               # 2 - clear the existing ImageBuf
\end{code}
\apiend

\apiitem{ImageBuf.{\ce reset} (filename) \\
ImageBuf.{\ce reset} (filename, subimage, miplevel) \\
ImageBuf.{\ce reset} (imagespec)}
Restore the \ImageBuf to its newly-constructed state, either to read from
a filename (optionally specifying a subimage and MIP level) or a writeable
\ImageBuf specified by an \ImageSpec.
\apiend

\apiitem{bool ImageBuf.{\ce read} (subimage=0, miplevel=0, force=False, convert=oiio.UNKNOWN)}
Explicitly read the image from the file (of a file-reading \ImageBuf), optionally
specifying a particular subimage and MIP level.  If {\cf force} is {\cf True},
will force an allocation of memory and a full read (versus the default of
relying on an underlying \ImageCache).  If {\cf convert} is not
the default of {\cf UNKNOWN}, it will force the \ImageBuf to convert the
image to the specified data format (versus keeping it in the native 
format or relying on the \ImageCache to make a data formatting decision).

Note that a call to {\cf read()} is not necessary --- any \ImageBuf API call
that accesses pixel values will trigger a file read if it has not yet been
done.

The {\cf read()} method will return {\cf True} for success, or {\cf False}
if the read could not be performed (in which case, a {\cf geterror()} call
will retrieve the specific error message).

\noindent Example:
\begin{code}
    buf = ImageBuf ("mytexture.exr")
    buf.read (0, 2, True)
    # That forces an allocation and read of MIP level 2
\end{code}
\apiend

\apiitem{bool ImageBuf.{\ce init_spec} (filename, subimage=0, miplevel=0)}

Explicitly read just the header from a file-reading \ImageBuf (if the header
has not yet been read), optionally specifying a particular subimage and MIP
level. The {\cf init_spec()} method will return {\cf True} for success, or
{\cf False} if the read could not be performed (in which case, a {\cf
geterror()} call will retrieve the specific error message).

Note that a call to {\cf init_spec()} is not necessary --- any \ImageBuf API
call that accesses the spec will read it automatically it has not yet been
done.
\apiend

\apiitem{bool ImageBuf.{\ce write} (filename, fileformat="")}
Write the contents of the \ImageBuf to the named file.  Optionally, {\cf
fileformat} can specify a particular file format to use (by default, it
will infer it from the extension of the file name).

\noindent Example:
\begin{code}
    # No-frills conversion of a TIFF file to JPEG
    buf = ImageBuf ("in.tif")
    buf.write ("out.jpg")
\end{code}
\apiend

\apiitem{bool ImageBuf.{\ce set_write_format} (format=oiio.UNKNOWN) \\
bool ImageBuf.{\ce set_write_tiles} (width=0, height=0, depth=0)}
Override the data format or tile size in a subsequent call to {\cf write()}.

\noindent Example:
\begin{code}
    # Conversion to a tiled float file
    buf = ImageBuf ("in.tif")
    buf.set_write_format (oiio.FLOAT)
    buf.set_write_tiles (64, 64)
    buf.write ("out.tif")
\end{code}
\apiend

\apiitem{ImageSpec ImageBuf.{\ce spec}() \\
ImageSpec ImageBuf.{\ce nativespec}()}
{\cf ImageBuf.spec()} returns the \ImageSpec that describes the contents of
the \ImageBuf.  {\cf ImageBuf.nativespec()} returns an \ImageSpec that
describes the contents of the file that the \ImageBuf was read from (this
may differ from {\cf ImageBuf.spec()} due to format conversions, or any
changes made to the \ImageBuf after the file was read, such as adding
metadata).

Handy rule of thumb: {\cf spec()} describes the buffer, {\cf nativespec()}
describes the original file it came from.

\noindent Example:
\begin{code}
    buf = ImageBuf ("in.tif")
    print "Resolution is", buf.spec().width, "x", buf.spec().height
\end{code}
\apiend

\apiitem{ImageSpec ImageBuf.{\ce specmod}()}
{\cf ImageBuf.specmod()} provides writeable access to the \ImageSpec that
describes the contents of the \ImageBuf.  Be very careful!  It is safe
to modify certain metadata, but if you change the data format or resolution
fields, you will get the chaos you deserve.

\noindent Example:
\begin{code}
    # Read an image, add a caption metadata, write it back out in place
    buf = ImageBuf ("file.tif")
    buf.specmod().attribute ("ImageDescription", "my photo")
    buf.write ("file.tif")
\end{code}
\apiend

\apiitem{str ImageBuf.{\ce name} \\
str ImageBuf.{\ce file_format_name}}
The file name and name of the file format of the image.
\apiend

\apiitem{int ImageBuf.{\ce subimage} \\
int ImageBuf.{\ce miplevel} \\
int ImageBuf.{\ce nsubimages} \\
int ImageBuf.{\ce nmiplevels}}
Several fields giving information about the current subimage and MIP
level, and the total numbers thereof in the file.
\apiend

\apiitem{int ImageBuf.{\ce xbegin} \\
int ImageBuf.{\ce xend} \\
int ImageBuf.{\ce ybegin} \\
int ImageBuf.{\ce yend} \\
int ImageBuf.{\ce zbegin} \\
int ImageBuf.{\ce zend}}
The range of valid pixel data window. Remember that the {\cf end} is 
\emph{one past} the last pixel.
\apiend

\apiitem{int ImageBuf.{\ce xmin} \\
int ImageBuf.{\ce xmax} \\
int ImageBuf.{\ce ymin} \\
int ImageBuf.{\ce ymax} \\
int ImageBuf.{\ce zmin} \\
int ImageBuf.{\ce zmax}}
The minimum and maximum (inclusive) coordinates of the pixel data window.
\apiend

\apiitem{int ImageBuf.{\ce orientation} \\
int ImageBuf.{\ce oriented_width} \\
int ImageBuf.{\ce oriented_height} \\
int ImageBuf.{\ce oriented_x} \\
int ImageBuf.{\ce oriented_y} \\
int ImageBuf.{\ce oriented_full_width} \\
int ImageBuf.{\ce oriented_full_height} \\
int ImageBuf.{\ce oriented_full_x} \\
int ImageBuf.{\ce oriented_full_y}}
The {\cf orientation} field gives the suggested display oriententation of
the image (see Section~\ref{metadata:orientation}).

The other fields are helpers that give the width, height, and origin
(as well as ``full'' or ``display'' resolution and origin), taking the
intended orientation into consideration.
\apiend

\apiitem{ROI ImageBuf.{\ce roi} \\
ROI ImageBuf.{\ce roi_full}}
These fields return an \ROI description of the pixel data window
({\cf roi}) and the full (a.k.a.\ ``display'') window ({\cf roi_full}).

\noindent Example:
\begin{code}
    buf = ImageBuf ("tahoe.jpg")
    print "Resolution is", buf.roi.width, "x", buf.roi.height
\end{code}
\apiend

\apiitem{ImageBuf.{\ce set_full} (roi)}
Changes the ``full'' (a.k.a. ``display'') window to the specified ROI.

\noindent Example:
\begin{code}
    newroi = ROI (0, 1024, 0, 768)
    buf.set_full (newroi)
\end{code}
\apiend

\apiitem{bool ImageBuf.{\ce pixels_valid}}
Will be {\cf True} if the file has already been read and the pixels are
valid. (It is always {\cf True} for writeable \ImageBuf's.)
There should be few good reasons to access these, since the spec and pixels
will be automatically be read when they are needed. 
\apiend

\apiitem{TypeDesc ImageBuf.{\ce pixeltype}}
Returns the description of the data type of the pixels stored within the
\ImageBuf.
\apiend

\apiitem{bool ImageBuf.{\ce deep}}
Will be {\cf True} if the file contains ``deep'' pixel data, or {\cf False}
for an ordinary images.
\apiend

\apiitem{ImageBuf.{\ce copy_metadata} (other_imagebuf)}
Replaces the metadata (all \ImageSpec items, except for the data format
and pixel data window size) with the corresponding metadata from the
other \ImageBuf.
\apiend

\apiitem{ImageBuf.{\ce copy_pixels} (other_imagebuf)}
Replace the pixels in this \ImageBuf with the values from the other
\ImageBuf.
\apiend

\apiitem{ImageBuf.{\ce copy} (other_imagebuf)}
Make this \ImageBuf a complete copy of the other \ImageBuf.

\noindent Example:
\begin{code}
    A = ImageBuf()
    B = ImageBuf("B.tif")
    A.copy (B)
    # Now A is a separate, duplicate copy of "B.tif"
\end{code}
\apiend

\apiitem{ImageBuf.{\ce swap} (other_imagebuf)}
Swaps the content of this \ImageBuf and the other \ImageBuf.

\noindent Example:
\begin{code}
    A = ImageBuf("A.tif")
    B = ImageBuf("B.tif")
    A.swap (B)
    # Now B contains the "A.tif" image and A contains the "B.tif" image
\end{code}
\apiend

\apiitem{tuple ImageBuf.{\ce getpixel} (x, y, z=0, wrap=oiio.WrapBlack)}
Retrieves pixel $(x,y,z)$ from the buffer and return it as a tuple of
{\cf float} values, one for each color channel.  The {\cf x, y, z} values
are {\cf int} pixel coordinates.  The optional {\cf wrap} parameter
describes what should happen if the coordinates are outside the pixel data
window (and may be: {\cf WrapBlack, WrapClamp, WrapPeriodic, WrapMirror}).

\noindent Example:
\begin{code}
    buf = ImageBuf ("tahoe.jpg")
    p = buf.getpixel (50, 50)
    print p

    > (0.37, 0.615, 0.97)
\end{code}
\apiend

\apiitem{float ImageBuf.{\ce getchannel} (x, y, z, channel, wrap=oiio.WrapBlack)}
Retrieves just a single channel value from pixel $(x,y,z)$ from the buffer
and returns it as a {\cf float} value.  The optional {\cf wrap} parameter
describes what should happen if the coordinates are outside the pixel data
window (and may be: {\cf WrapBlack, WrapClamp, WrapPeriodic, WrapMirror}).

\noindent Example:
\begin{code}
    buf = ImageBuf ("tahoe.jpg")
    green = buf.getchannel (50, 50, 0, 1)
\end{code}
\apiend

\apiitem{tuple ImageBuf.{\ce interppixel} (x, y, wrap=oiio.WrapBlack)}
Interpolates the image value at coordinates $(x,y)$ and return it as a tuple
of {\cf float} values, one for each color channel.  The {\cf x, y} values
are continuous {\cf float} coordinates in ``pixel space.''   The optional
{\cf wrap} parameter describes what should happen if the coordinates are
outside the pixel data window (and may be: {\cf WrapBlack, WrapClamp,
WrapPeriodic, WrapMirror}).

\noindent Example:
\begin{code}
    buf = ImageBuf ("tahoe.jpg")
    midx = float(buf.xbegin + buf.xend) / 2.0
    midy = float(buf.ybegin + buf.yend) / 2.0
    p = buf.interpixel (midx, midy)
    # Now p is the interpolated value from right in the center of
    # the data window
\end{code}
\apiend

\apiitem{tuple ImageBuf.{\ce interppixel_NDC_full} (x, y, wrap=oiio.WrapBlack)}

Interpolates the image value at coordinates $(x,y)$ and return it as a tuple
of {\cf float} values, one for each color channel.  The {\cf x, y} values
are continuous, normalized {\cf float} coordinates in ``NDC space,'' where
{\cf (0,0)} is the upper left corner of the full (a.k.a.\ ``display'')
window, and {\cf (1,1)} is the lower right corner of the full/display
window. The  {\cf wrap} parameter describes what should happen if the
coordinates are outside the pixel data window (and may be: {\cf WrapBlack,
WrapClamp, WrapPeriodic, WrapMirror}).

\noindent Example:
\begin{code}
    buf = ImageBuf ("tahoe.jpg")
    p = buf.interpixel_NDC_full (0.5, 0.5)
    # Now p is the interpolated value from right in the center of
    # the display window
\end{code}
\apiend

\apiitem{ImageBuf.{\ce setpixel} (x, y, pixel_value) \\
ImageBuf.{\ce setpixel} (x, y, z, pixel_value)}
Sets pixel $(x,y,z)$ to be the {\cf pixel_value}, expressed as a tuple of
{\cf float}s (one for each color channel).

\noindent Example:
\begin{code}
    buf = ImageBuf (ImageSpec (640, 480, 3, oiio.UINT8))

    # Set the whole image to red (the dumb slow way, but it works):
    for y in range(buf.ybegin, buf.yend) :
        for x in range(buf.xbegin, buf.xend) :
            buf.setpixel (x, y, (1.0, 0.0, 0.0))
\end{code}
\apiend

\apiitem{bool ImageBuf.{\ce has_error} \\
str ImageBuf.{\ce geterror} ()}
The {\cf ImageBuf.has_error} field will be {\cf True} if an error has
occurred in the \ImageBuf, in which case the {\cf geterror()} method will
retrieve the error message (and clear it afterwards).

\noindent Example:
\begin{code}
    buf = ImageBuf ("in.tif")
    buf.read ()   # force a read
    if buf.has_error :
        print "Error reading the file:", buf.geterror()
    buf.write ("out.jpg")
    if buf.has_error :
        print "Could not convert the file:", buf.geterror()
\end{code}
\apiend


\newpage
\section{ImageBufAlgo}
\label{sec:pythonimagebufalgo}

The C++ \IBA functions are described in detail in
Chapter~\ref{chap:imagebufalgo}.  They are also exposed to Python.
For the majority of \IBA functions, their use in Python is identical
to C++; in those cases, we will keep our descriptions of the Python
bindings minimal and refer you to Chapter~\ref{chap:imagebufalgo}, saving
the extended descriptions for those functions that differ from the C++
counterparts.

A few things about the paramters of the \IBA function calls are identical
among the functions, so we will explain once here rather than separately for
each function:

\begin{itemize}
\item {\cf dst} is an existing \ImageBuf, which will be modified (it may be
an uninitialized \ImageBuf, but it must be an \ImageBuf).
\item {\cf src} parameter is an initialized \ImageBuf, which will not be
modified (unless it happens to refer to the same image as {\cf dst}.
\item {\cf roi}, if supplied, is an {\cf ROI} specifying a region of interst
over which to operate. If omitted, the region will be the entire size of the
source image(s).
\item {\cf nthreads} is the maximum number of threads to use. If not
supplied, it defaults to 0, meaning to use as many threads as hardware cores
available.
\end{itemize}

Just as with the C++ \IBA functions, if {\cf dst} is an uninitialized
\ImageBuf, it will be sized to reflect the {\cf roi} (which, in turn, if
undefined, will be sized to be the union of the ROI's of the source 
images).

\subsection{Pattern generation}
\label{sec:iba:py:patterns}

\apiitem{bool ImageBufAlgo.{\ce zero} (dst, roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!zero} \indexapi{zero}

Zero out the destination buffer (or a specific region of it).

\smallskip
\noindent Example:
\begin{code}
    # Initialize buf to a 640x480 3-channel FLOAT buffer, zero it out
    buf = ImageBuf (ImageSpec (640, 480, 3, oiio.FLOAT))
    ImageBufAlgo.zero (buf)
\end{code}
\apiend

\apiitem{bool ImageBufAlgo.{\ce fill} (dst, values, roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!fill} \indexapi{fill}

Set the pixels in {\cf dst} within the ROI to the
values in the tuple {\cf values}.

\smallskip
\noindent Examples:
\begin{code}
    # Draw a red rectangle into buf
    buf = ImageBuf (ImageSpec(640, 480, 3, TypeDesc.FLOAT)
    ImageBufAlgo.fill (buf, (1,0,0), ROI(50, 100, 75, 85))
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce checker} (dst, width, height, depth, 
 color1, color2, \\ \bigspc xoffset=0, yoffset=0, zoffset=0, 
 roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!checker} \indexapi{checker}

Fill {\cf dst} with a checkerboard pattern. The colors are specified as
tuples giving the values for each color channel.

\smallskip
\noindent Examples:
\begin{code}
    buf = ImageBuf(ImageSpec(640, 480, 3, oiio.UINT8))
    ImageBufAlgo.checker (buf, 64, 64, 1, (0.1,0.1,0.1), (0.4,0.4,0.4))
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce render_text} (dst, x, y, text, fontsize=16, \\
  \bigspc\bigspc  fontname="", textcolor=(1,1,1,1))}
\index{ImageBufAlgo!render_text} \indexapi{render_text}

Render antialiased text into {\cf dst}.  The {\cf textcolor} (if supplied)
is a tuple giving the per-channel colors.

\smallskip
\noindent Examples:
\begin{code}
    buf = ImageBuf(ImageSpec (640, 480, 4, oiio.FLOAT))
    ImageBufAlgo.render_text (buf, 100, 200, "Go Big Red!",
                               60, "Arial Bold", (1,0,0,1))
\end{code}
\apiend



\subsection{Image transformations and data movement}
\label{sec:iba:py:transforms}

\apiitem{bool ImageBufAlgo.{\ce channels} (dst, src, channelorder, newchannelnames=(), \\
        \bigspc\bigspc shuffle_channel_names=False)}
\index{ImageBufAlgo!channels} \indexapi{channels}

Copy {\cf src} to {\cf dst}, but with channels in
the order specified by the tuple {\cf channelorder}. 
The length of {\cf channelorder} specifies the number of channels to copy.
Each element in the tuple {\cf channelorder} may be one of the following:
\begin{itemize}
\item {}
\item {\cf int} : specifies the index (beginning at 0) of the channel
    to copy.
\item {\cf str} : specifies the name of the channel to copy.
\item {\cf float} : specifies a constant value to use for that channel.
\end{itemize}

Does not support in-place operation -- that is, {\cf dst} and {\cf src} must
be different \ImageBuf's.

If {\cf newchannelnames} is supplied, it is a tuple of new channel
names. (See the C++ version for more full explanation.)

\smallskip
\noindent Examples:
\begin{code}
    # Copy the first 3 channels of an RGBA, drop the alpha
    RGBA = ImageBuf("rgba.tif")
    RGB = ImageBuf()
    ImageBufAlgo.channels (RGB, RGBA, (0,1,2))

    # Copy just the alpha channel, making a 1-channel image
    Alpha = ImageBuf()
    ImageBufAlgo.channels (Alpha, RGBA, ("A",))

    # Swap the R and B channels
    BGRA = ImageBuf()
    ImageBufAlgo.channels (BRGA, RGBA, (2, 1, 0, 3))

    # Add an alpha channel with value 1.0 everywhere to an RGB image
    ImageBufAlgo.channels (RGBA, RGB, ("R", "G", "B", 1.0),
                            ("R", "G", "B", "A"))
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce channel_append} (dst, A, B, roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!channel_append} \indexapi{channel_append}
Append the channels of images {\cf A} and {\cf B} together into {\cf dst} over
the region of interest.

\smallskip
\noindent Examples:
\begin{code}
    RGBA = ImageBuf ("rgba.exr")
    Z = ImageBuf ("z.exr")
    RGBAZ = ImageBuf()
    ImageBufAlgo.channel_append (RGBAZ, RGBA, Z)
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce flatten} (dst, src, roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!flatten} \indexapi{flatten} \index{deep images}

Composite the depth samples within each pixel of ``deep'' \ImageBuf\ {\cf
src} and store the single  resulting value per pixel in ``flat'' \ImageBuf
{\cf dst}.

\smallskip
\noindent Examples:
\begin{code}
    Flat = ImageBuf()
    ImageBufAlgo.flatten (Flat, ImageBuf("deepalpha.exr"))
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce crop} (dst, src, roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!crop} \indexapi{crop}
Reset {\cf dst} to be the specified region of {\cf src}.

\smallskip
\noindent Examples:
\begin{code}
    # Set B to be the upper left 200x100 region of A
    A = ImageBuf ("a.tif")
    B = ImageBuf()
    ImageBufAlgo.crop (B, A, ROI(0,200,0,100))
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce cut} (dst, src, roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!cut} \indexapi{cut}
\NEW % 1.4
Reset {\cf dst} to be the specified region of {\cf src}, but moved so
that the resulting new image has its pixel data at the image plane origin.

\smallskip
\noindent Examples:
\begin{code}
    # Set B to be the lower left 200x100 region of A, moved to the origin
    A = ImageBuf ("a.tif")
    B = ImageBuf()
    ImageBufAlgo.cut (B, A, ROI(0,200,380,480))
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce paste} (dst, xbegin, ybegin, zbegin,
  chbegin, \\
  \bigspc\bigspc src, ROI srcroi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!paste} \indexapi{paste}
Copy the specified region of {\cf src} into {\cf dst} beginning at 
offset {\cf (xbegin, ybegin, zbegin)}.

\smallskip
\noindent Examples:
\begin{code}
    # Paste small.exr on top of big.exr at offset (100,100)
    Big = ImageBuf ("big.exr")
    Small = ImageBuf ("small.exr")
    ImageBufAlgo.paste (Big, 100, 100, 0, 0, Small)
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce flip} (dst, src, roi=ROI.All, nthreads=0) \\
bool ImageBufAlgo.{\ce flop} (dst, src, roi=ROI.All, nthreads=0) \\
bool ImageBufAlgo.{\ce flipflop} (dst, src, roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!flip} \indexapi{flip}
\index{ImageBufAlgo!flop} \indexapi{flop}
\index{ImageBufAlgo!flipflop} \indexapi{flipflop}
Copy while reversing orientation vertically (flip), horizontally (flop),
or both vertically and horizontally (flipflip).

\smallskip
\noindent Examples:
\begin{code}
    A = ImageBuf ("tahoe.exr")
    B = ImageBuf()
    ImageBufAlgo.flip (B, A)
\end{code}
\apiend



\apiitem{bool ImageBufAlgo.{\ce transpose} (dst, src, oi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!transpose} \indexapi{transpose}
Copy while transposing ($x \leftrightarrow y$) pixels.

\smallskip
\noindent Examples:
\begin{code}
    A = ImageBuf ("tahoe.exr")
    B = ImageBuf()
    ImageBufAlgo.transpose (B, A)
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce circular_shift} (dst, src, xshift, yshift, zshift=0, \\
        \bigspc  roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!circular_shift} \indexapi{circular_shift}

Copy while circularly shifting by the given amount. 

\smallskip
\noindent Examples:
\begin{code}
    A = ImageBuf ("tahoe.exr")
    B = ImageBuf()
    ImageBufAlgo.circular_shift (B, A, 200, 100)
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce resize} (dst, src, filtername="", filtersize=0.0, \\
        \bigspc\bigspc  roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!resize} \indexapi{resize}
Set {\cf dst}, over the ROI, to be a high-quality resized version of the
corresponding portion of {\cf src}.  If the filter and size are not
specified, an appropriate default will be chosen.

\smallskip
\noindent Examples:
\begin{code}
    # Resize the image to 640x480, using the default filter
    Src = ImageBuf ("tahoe.exr")
    Dst = ImageBuf (ImageSpec (640, 480, 3, OpenImageIO.FLOAT))
    ImageBufAlgo.resize (Dst, Src)
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce resample} (dst, src, interpolate=True, \\
        \bigspc\bigspc roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!resample} \indexapi{resample}
Set {\cf dst}, over the ROI, to be a low-quality (but fast) resized version
of the corresponding portion of {\cf src}, either using a simple ``closest
pixel'' choice or by bilinaerly interpolating (depending on {\cf
interpolate}).

\smallskip
\noindent Examples:
\begin{code}
    # Resample quickly to 320x240 to make a low-quality thumbnail
    Src = ImageBuf ("tahoe.exr")
    Dst = ImageBuf (ImageSpec (320, 240, 3, OpenImageIO.UINT8))
    ImageBufAlgo.resample (Dst, Src)
\end{code}
\apiend



\subsection{Image arithmetic}
\label{sec:iba:py:arith}

\apiitem{bool ImageBufAlgo.{\ce add} (dst, A, B, roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!add} \indexapi{add}

Compute {\cf dst = A + B}.  {\cf A} is an \ImageBuf, and {\cf B}  may
be an \ImageBuf, a {\cf float} (added to all channels) or a tuple giving a
{\cf float} for each color channel.

\smallskip
\noindent Examples:
\begin{code}
    # Add two images
    buf = ImageBuf ()
    ImageBufAlgo.add (buf, ImageBuf("a.exr"), ImageBuf("b.exr"))

    # Add 0.2 to channels 0-2 
    ImageBufAlgo.add (buf, buf, (0.2,0.2,0.2,0))
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce sub} (dst, A, B, roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!sub} \indexapi{sub}

Compute {\cf dst = A - B}.  {\cf A} is an \ImageBuf, and {\cf B}  may
be an \ImageBuf, a {\cf float} (added to all channels) or a tuple giving a
{\cf float} for each color channel.

\smallskip
\noindent Examples:
\begin{code}
    buf = ImageBuf ()
    ImageBufAlgo.sub (buf, ImageBuf("a.exr"), ImageBuf("b.exr"))
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce mul} (dst, A, B, roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!mul} \indexapi{mul}

Compute {\cf dst} = {\cf A * B} (channel-by-channel multiplication).  {\cf A}
is an \ImageBuf, and {\cf B}  may be an \ImageBuf, a {\cf float} (scaling
all channels) or a tuple giving a {\cf float} for each color channel.

\smallskip
\noindent Examples:
\begin{code}
    # Multiply the two images
    buf = ImageBuf ()
    ImageBufAlgo.mul (buf, ImageBuf("a.exr"), ImageBuf("b.exr"))

    # Reduce intensity of buf's channels 0-2 by 50%, in place
    ImageBufAlgo.mul (buf, buf, (0.5, 0.5, 0.5, 1))
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce pow} (dst, A, B, roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!pow} \indexapi{pow}

\NEW % 1.4
Compute {\cf dst} = {\cf pow (A, B} (channel-by-channel exponentiation).
{\cf A} is an \ImageBuf, and {\cf B} may be a {\cf float} (a single power
for all channels) or a tuple giving a {\cf float} for each color channel.

\smallskip
\noindent Examples:
\begin{code}
    # Linearize a 2.2 gamma-corrected image (channels 0-2 only)
    img = ImageBuf ("a.exr")
    buf = ImageBuf ()
    ImageBufAlgo.pow (buf, img, (2.2, 2.2, 2.2, 1.0))
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce channel_sum} (dst, src, 
        weights=(), \\ \bigspc\bigspc roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!channel_sum} \indexapi{channel_sum}
Converts a multi-channel image into a 1-channel image via a weighted sum
of channels. The {\cf weights} is a tuple providing the weight for each 
channel (if not supplied, all channels will have weight 1.0).

\smallskip
\noindent Examples:
\begin{code}
    # Compute luminance via a weighted sum of R,G,B
    # (assuming Rec709 primaries and a linear scale)
    luma_weights = (.2126, .7152, .0722)
    ImageBufAlgo.channel_sum (luma, ImageBuf("a.exr"), luma_weights)
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce clamp} (dst, src, min, max,
  bool clampalpha01=False, \\ \bigspc\bigspc roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!clamp} \indexapi{clamp}

Copy pixels while clamping
between the {\cf min} and {\cf max} values.  The {\cf min} and {\cf max}
may either be tuples (one min and max value per channel), or single
{\cf floats} (same value for all channels).  Additionally, if
{\cf clampalpha01} is {\cf True}, then any alpha 
channel is clamped to the 0--1 range.

\smallskip
\noindent Examples:
\begin{code}
    # Clamp image buffer A in-place to the [0,1] range for all channels.
    ImageBufAlgo.clamp (A, A, 0.0, 1.0)
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce rangecompress} (dst, src, useluma=False, \\
   \bigspc\bigspc     roi=ROI.All, nthreads=0) \\
bool ImageBufAlgo.{\ce rangeexpand} (dst, src, useluma=False, \\
    \bigspc\bigspc    roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!rangecompress} \indexapi{rangecompress}
\index{ImageBufAlgo!rangeexpand} \indexapi{rangeexpand}

Copy from {\cf src} to {\cf dst}, compressing (logarithmically) or expanding
(by the inverse of the compressive transformation) the range of pixel
values.  Alpha and z channels are copied but not transformed.

If {\cf useluma} is {\cf True}, the luma of the first three channels (presumed
to be R, G, and B) are used to compute a single scale factor for all
color channels, rather than scaling all channels individually (which
could result in a big color shift when performing {\cf rangecompress}
and {\cf rangeexpand}).

\smallskip
\noindent Examples:
\begin{code}
    # Resize the image to 640x480, using a Lanczos3 filter, which
    # has negative lobes. To prevent those negative lobes from
    # producing ringing or negative pixel values for HDR data,
    # do range compression, then resize, then re-expand the range.

    # 1. Read the original image
    Src = ImageBuf ("tahoeHDR.exr")

    # 2. Range compress to a logarithmic scale
    Compressed = ImageBuf ()
    ImageBufAlgo.rangecompress (Compressed, Src)

    # 3. Now do the resize
    Dst = ImageBuf ()
    roi = ROI (0, 640, 0, 480, 0, 1, 0, Compressed.nchannels)
    ImageBufAlgo.resize (Dst, Compressed, "lanczos3", 6.0, roi)

    # 4. Expand range to be linear again (operate in-place)
    ImageBufAlgo.rangeexpand (Dst, Dst)
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce over} (dst, A, B, roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!over} \indexapi{over}

Composite \ImageBuf\ {\cf A} \emph{over} \ImageBuf\ {\cf B}.

\smallskip
\noindent Examples:
\begin{code}
    Composite = ImageBuf()
    ImageBufAlgo.over (Composite, ImageBuf("fg.exr"), ImageBuf("bg.exr"))
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce zover} (dst, A, B, bool z_zeroisinf=False,\\
        \bigspc\bigspc roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!zover} \indexapi{zover}

Composite \ImageBuf\ {\cf A} and \ImageBuf\ {\cf B} using their respective
$Z$ channels to decide which is in front on a pixel-by-pixel basis.

\smallskip
\noindent Examples:
\begin{code}
    Composite = ImageBuf()
    ImageBufAlgo.zover (Composite, ImageBuf("fg.exr"), ImageBuf("bg.exr"))
\end{code}
\apiend



\subsection{Image comparison and statistics}
\label{sec:iba:py:stats}

\begin{comment}   % Not figured out yet

\apiitem{bool ImageBufAlgo.{\ce computePixelStats} (PixelStats \&stats, src, \\
   \bigspc\bigspc  roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!computePixelStats} \indexapi{computePixelStats}

Compute statistics about the ROI of the image {\cf src}, storing results
in {\cf stats} (each of the vectors within {\cf stats} will be
automatically resized to the number of channels in the image).  A return
value of {\cf true} indicates success, {\cf false} indicates that it was
not possible to complete the operation.
 The {\cf PixelStats} structure is defined as follows:
\begin{code}
struct PixelStats {
    std::vector<float> min
    std::vector<float> max
    std::vector<float> avg
    std::vector<float> stddev
    std::vector<imagesize_t> nancount
    std::vector<imagesize_t> infcount
    std::vector<imagesize_t> finitecount
    std::vector<double> sum, sum2  # for intermediate calculation
}
\end{code}

\smallskip
\noindent Examples:
\begin{code}
    ImageBuf A ("a.exr")
    ImageBufAlgo.PixelStats stats
    ImageBufAlgo.computePixelStats (stats, A)
    for (int c = 0;  c < A.nchannels();  ++c) {
        print "Channel ", c, ":"
        print "   min = ", stats.min[c]
        print "   max = ", stats.max[c]
        print "   average = ", stats.avg[c]
        print "   standard deviation  = ", stats.stddev[c]
        print "   # NaN values    = ", stats.nancount[c]
        print "   # Inf values    = ", stats.infcount[c]
        print "   # finite values = ", stats.finitecount[c]
    }
\end{code}
\apiend
\end{comment}


\apiitem{bool ImageBufAlgo.{\ce compare} (A, B, failthresh, warnthresh, 
  result, \\ \bigspc\bigspc roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!compare} \indexapi{compare}

Numerically compare two \ImageBuf's, {\cf A} and {\cf B}. The {\cf failthresh}
and {\cf warnthresh} supply failure and warning difference thresholds.
The {\cf result} parameter must refer to a {\cf CompareResults} object,
which is defined as a class having the following members:
\begin{code}
    meanerror, rms_error, PSNR, maxerror  # error statistics
    maxx, maxy, maxz, maxc                # pixel of biggest difference
    nwarn, nfail                          # number of warnings and failures
\end{code}

\smallskip
\noindent Examples:
\begin{code}
    A = ImageBuf ("a.exr")
    B = ImageBuf ("b.exr")
    comp = OpenImageIO.CompareResults()
    ImageBufAlgo.compare (A, B, 1.0/255.0, 0.0, comp)
    if comp.nwarn == 0 and comp.nfail == 0 :
        print "Images match within tolerance"
    else :
        print comp.nfail, "failures,", comp.nwarn, " warnings."
        print "Average error was " , comp.meanerror
        print "RMS error was" , comp.rms_error
        print "PSNR was" , comp.PSNR
        print "largest error was ", comp.maxerror
        print "  on pixel", (comp.maxx, comp.maxy, comp.maxz)
        print "  channel", comp.maxc
\end{code}
\apiend


\apiitem{tuple ImageBufAlgo.{\ce isConstantColor} (src, roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!isConstantColor} \indexapi{isConstantColor}

If all pixels of {\cf src} within the ROI have the same values (for the
subset of channels described by {\cf roi}), return a tuple giving that
color (one {\cf float} for each channel), otherwise return {\cf None}.

\smallskip
\noindent Examples:
\begin{code}
    A = ImageBuf ("a.exr")
    color = ImageBufAlgo.isConstantColor (A)
    if color != None :
        print "The image has the same value in all pixels:", color
    else :
        print "The image is not a solid color."
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce isConstantChannel} (src, channel, val, \\
 \bigspc\bigspc         roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!isConstantChannel} \indexapi{isConstantChannel}

Returns {\cf True} if all pixels of {\cf src} within the ROI have the
given {\cf channel} value {\cf val}.

\smallskip
\noindent Examples:
\begin{code}
    A = ImageBuf ("a.exr")
    alpha = A.spec.alpha_channel
    if alpha < 0 :
        print "The image does not have an alpha channel"
    elif ImageBufAlgo.isConstantChannel (A, alpha, 1.0) :
        print "The image has alpha = 1.0 everywhere"
    else :
        print "The image has alpha < 1 in at least one pixel"
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce isMonochrome} (src, 
          roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!isMonochrome} \indexapi{isMonochrome}

Returns {\cf True} if the image is monochrome within the ROI.

\smallskip
\noindent Examples:
\begin{code}
    A = ImageBuf ("a.exr")
    roi = A.roi
    roi.chend = min (3, roi.chend)  # only test RGB, not alpha
    if ImageBufAlgo.isMonochrome (A, roi) :
        print "a.exr is really grayscale"
\end{code}
\apiend


\begin{comment}
\apiitem{bool ImageBufAlgo.{\ce color_count} (src, imagesize_t *count,\\
        \bigspc  int ncolors, const float *color, const float *eps=NULL,  \\
        \bigspc  roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!color_count} \indexapi{color_count}

Count how many pixels in the image (within the ROI) match a list of colors.
The colors to match are in:

\begin{code}
  colors[0 ... nchans-1]
  colors[nchans ... 2*nchans-1]
  ...
  colors[(ncolors-1)*nchans ... (ncolors*nchans)-1]
\end{code}

\noindent and so on, a total of {\cf ncolors} consecutively stored
colors of {\cf nchans} channels each ({\cf nchans} is the number of
channels in the image, itself, it is not passed as a parameter).


The values in {\cf eps[0..nchans-1]} are the error tolerances for a
match, for each channel.  Setting {\cf eps[c]} to 
{\cf numeric_limits<float>::max()} will effectively make it ignore the
channel.  Passing {\cf eps == NULL} will be interpreted as a tolerance
of 0.001 for all channels (requires exact matches for 8 bit images, but
allows a wee bit of imprecision for {\cf float} images.

\smallskip
\noindent Examples:
\begin{code}
    A = ImageBuf ("a.exr")
    n = A.nchannels

    # Try to match two colors: pure red and green
    std::vector<float> colors (2*n, numeric_limits<float>::max());
    colors[0] = 1.0; colors[1] = 0.0; colors[2] = 0.0;
    colors[n+0] = 0.0; colors[n+1] = 1.0; colors[n+2] = 0.0;

    const int ncolors = 2;
    imagesize_t count[ncolors];
    ImageBufAlgo.color_count (A, count, ncolors);
    print "Number of red pixels   : ", count[0]
    print "Number of green pixels : ", count[1]
\end{code}
\apiend


\apiitem{bool {\ce color_range_check} (src, 
   imagesize_t *lowcount, \\ \bigspc imagesize_t *highcount, imagesize_t
  *inrangecount, \\
  \bigspc const float *low, const float *high, \\
        \bigspc  roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!color_range_check} \indexapi{color_range_check}

Count how many pixels in the image (within the ROI) are outside the
value range described by {\cf low[roi.chbegin..roi.chend-1]} and
{\cf high[roi.chbegin..roi.chend-1]} 
as the low and high acceptable values for each color channel.  

The number of pixels containing values that fall below the lower bound
will be stored in {\cf *lowcount}, the number of pixels containing
values that fall above the upper bound will be stored in 
{\cf *highcount}, and the number of pixels for which all channels fell
within the bounds will be stored in {\cf *inrangecount}.  Any of these
may be NULL, which simply means that the counts need not be collected or
stored.

\smallskip
\noindent Examples:
\begin{code}
    A = ImageBuf ("a.exr")
    ROI roi = get_roi (A.spec())
    roi.chend = std::min (roi.chend, 4);  # only compare RGBA

    float low[] = {0, 0, 0, 0};
    float high[] = {1, 1, 1, 1};

    imagesize_t lowcount, highcount, inrangecount;
    ImageBufAlgo.color_range_check (A, &lowcount, &highcount, &inrangecount,
                                     low, high, roi);
    print lowcount, " pixels had components < 0"
    print highcount, " pixels had components > 1"
    print inrangecount, " pixels were fully within [0,1] range"
\end{code}
\apiend
\end{comment}


\apiitem{std::string ImageBufAlgo.{\ce computePixelHashSHA1} (src, 
  extrainfo = "", \\
  \bigspc\bigspc  roi=ROI.All, blocksize=0, nthreads=0)}
\index{ImageBufAlgo!computePixelHashSHA1} \indexapi{computePixelHashSHA1}

Compute the SHA-1 byte hash for all the pixels in the ROI of {\cf src}.

\smallskip
\noindent Examples:
\begin{code}
    A = ImageBuf ("a.exr")
    hash = ImageBufAlgo.computePixelHashSHA1 (A, blocksize=64)
\end{code}
\apiend


\begin{comment}
\apiitem{bool {\ce histogram} (src, int channel, \\
  \bigspc std::vector<imagesize_t> \&histogram, int bins=256, \\
  \bigspc float min=0, float max=1, imagesize_t *submin=NULL, \\
  \bigspc imagesize_t *supermax=NULL, roi=ROI.All)}
\index{ImageBufAlgo!histogram} \indexapi{histogram}
\apiend
\end{comment}



\subsection{Convolutions}
\label{sec:iba:py:convolutions}

\apiitem{bool ImageBufAlgo.{\ce make_kernel} (dst, name, width, height, \\
  \bigspc\bigspc depth=1.0, normalize=True)}
\index{ImageBufAlgo!make_kernel} \indexapi{make_kernel}
Initialize {\cf dst} to be a 1-channel {\cf float} image of the named kernel
and dimensions.  If {\cf normalize} is {\cf True}, the values will be
normalized so that they sum to $1.0$.

If {\cf depth} $> 1$, a volumetric kernel will be created.  Use with
caution!

Kernel names can be: \qkw{gaussian}, \qkw{sharp-gaussian}, \qkw{box},
\qkw{triangle}, \qkw{mitchell}, \qkw{blackman-harris}, \qkw{b-spline},
\qkw{catmull-rom}, \qkw{lanczos3}, \qkw{cubic}, \qkw{keys}, \qkw{simon},
\qkw{rifman}, \qkw{disk}, \qkw{binomial}. Note that
\qkw{catmull-rom} and \qkw{lanczos3} are fixed-size kernels that don't
scale with the width, and are therefore probably less useful in most
cases.

\smallskip
\noindent Examples:
\begin{code}
    K = ImageBuf()
    ImageBufAlgo.make_kernel (K, "gaussian", 5.0, 5.0)
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce convolve} (dst, src, kernel, normalize=True, \\
  \bigspc\bigspc  roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!convolve} \indexapi{convolve}
Replace the given ROI of {\cf dst} with the convolution of {\cf src} and
a kernel (also an \ImageBuf).

\smallskip
\noindent Examples:
\begin{code}
    # Blur an image with a 5x5 Gaussian kernel
    Src = ImageBuf ("tahoe.exr")
    K = ImageBuf ()
    ImageBufAlgo.make_kernel (K, "gaussian", 5.0, 5.0)
    Blurred = ImageBuf ()
    ImageBufAlgo.convolve (Blurred, Src, K)
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce fft} (dst, src, roi=ROI.All, nthreads=0) \\
        bool ImageBufAlgo.{\ce ifft} (dst, src, roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!fft} \indexapi{fft}
\index{ImageBufAlgo!ifft} \indexapi{ifft}

Compute the forward or inverse discrete Fourier Transform.

\smallskip
\noindent Examples:
\begin{code}
    Src = ImageBuf ("tahoe.exr")

    # Take the DFT of the first channel of Src
    Freq = ImageBuf ()
    ImageBufAlgo.fft (Freq, Src)

    # At this point, Freq is a 2-channel float image (real, imag)
    # Convert it back from frequency domain to a spatial iamge
    Spatial = ImageBuf ()
    ImageBufAlgo.ifft (Spatial, Freq)
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce complex_to_polar} (dst, src, roi=ROI.All, nthreads=0) \\
        bool ImageBufAlgo.{\ce polar_to_complex} (dst, src, roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!polar_to_complex} \indexapi{polar_to_complex}
\index{ImageBufAlgo!complex_to_polar} \indexapi{complex_to_polar}
\NEW  % 1.4

Transform a 2-channel image from complex (real, imaginary) representation
to polar (amplitude, phase), or vice versa.

\smallskip
\noindent Examples:
\begin{code}
    Polar = ImageBuf ("polar.exr")

    Complex = ImageBuf ()
    ImageBufAlgo.polar_to_complex (Complex, Polar)

    # At this point, Complex is a 2-channel complex image (real, imag)
    # Convert it back from frequency domain to a spatial iamge
    Spatial = ImageBuf ()
    ImageBufAlgo.ifft (Spatial, Complex)
\end{code}
\apiend



\subsection{Image Enhancement / Restoration}
\label{sec:iba:py:enhance}

\apiitem{bool ImageBufAlgo.{\ce fixNonFinite} (dst, src,
  mode=NONFINITE_BOX3, \\ \bigspc\spc roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!fixNonFinite} \indexapi{fixNonFinite}

Copy pixel values from {\cf src} to {\cf dst} (within the pixel and channel
range designated by {\cf roi}), and repair any non-finite ({\cf NaN} or {\cf
Inf}) pixels.

How the non-finite values are repaired is specified by one of the
following modes: \\
{\cf OpenImageIO.NONFINITE_NONE}, \\
{\cf OpenImageIO.NONFINITE_BLACK} \\ 
{\cf OpenImageIO.NONFINITE_BOX3}

\smallskip
\noindent Examples:
\begin{code}
    Src = ImageBuf ("tahoe.exr")
    ImageBufAlgo.fixNonFinite (Src, Src, OpenImageIO.NONFINITE_BOX3)
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce fillholes_pushpull} (dst, src, roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!fillholes_pushpull} \indexapi{fillholes_pushpull}
Copy the specified ROI of {\cf src} to {\cf dst} and fill any 
holes (pixels where alpha $< 1$) with plausible values using a push-pull
technique.  The {\cf src} image must have
an alpha channel.  The dst image will end up with a copy of src, but
will have an alpha of 1.0 everywhere, and any place where the alpha
of src was < 1, dst will have a pixel color that is a plausible
``filling'' of the original alpha hole.

\smallskip
\noindent Examples:
\begin{code}
    Src = ImageBuf ("holes.exr")
    Filled = ImageBuf ()
    ImageBufAlgo.fillholes_pushpull (Filled, Src)
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce unsharp_mask} (dst, src, kernel="gaussian", \\
  \bigspc\spc width=3.0, contrast=1.0, threshold=0.0, \\
  \bigspc\spc  roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!unsharp_mask} \indexapi{unsharp_mask}

Replace the given ROI of {\cf dst} with a sharpened version of the
corresponding region of {\cf src} using the ``unsharp mask'' technique.

\smallskip
\noindent Examples:
\begin{code}
    Blurry = ImageBuf ("tahoe.exr")
    Sharp = ImageBuf ()
    ImageBufAlgo.unsharp_mask (Sharp, Blurry, "gaussian", 5.0)
\end{code}
\apiend



\subsection{Color manipulation}
\label{sec:iba:py:color}

\apiitem{bool ImageBufAlgo.{\ce colorconvert} (dst, src, from, to,
  unpremult=False, \\ \bigspc\bigspc  roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!colorconvert} \indexapi{colorconvert}
Copy pixels from {\cf src} to {\cf dst} (within the ROI), while
applying a color transform to the pixel values.
In-place operations ({\cf dst} and {\cf src} being the same image)
are supported.

\smallskip
\noindent Examples:
\begin{code}
    Src = ImageBuf ("tahoe.jpg")
    Dst = ImageBuf ()
    ImageBufAlgo.colorconvert (Dst, Src, "vd8", "lnf")
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce ociolook} (dst, src, looks, from, to,  \\
  \bigspc\spc  inverse=False, unpremult=False, key="", value="", \\
  \bigspc\spc roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!ociolook} \indexapi{ociolook}
Copy pixels from {\cf src} to {\cf dst} (within the ROI), while
applying an OpenColorIO ``look'' transform to the pixel values.
In-place operations ({\cf dst} and {\cf src} being the same image)
are supported.

\smallskip
\noindent Examples:
\begin{code}
    Src = ImageBuf ("tahoe.jpg")
    Dst = ImageBuf ()
    ImageBufAlgo.ociolook (Dst, Src, "look", "vd8", "lnf", False, False,
                            key="SHOT", value="pe0012")
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce ociodisplay} (dst, src, display, view, \\
  \bigspc\spc from=None, looks=None, unpremult=False, key="", value="", \\
  \bigspc\spc roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!ociodisplay} \indexapi{ociodisplay}
Copy pixels from {\cf src} to {\cf dst} (within the ROI), while
applying an OpenColorIO ``display'' transform to the pixel values.
In-place operations ({\cf dst} and {\cf src} being the same image)
are supported.

\smallskip
\noindent Examples:
\begin{code}
    Src = ImageBuf ("tahoe.exr")
    Dst = ImageBuf ()
    ImageBufAlgo.ociodisplay (Dst, Src, "sRGB", "Film", "lnf", False,
                              key="SHOT", value="pe0012")
\end{code}
\apiend


\apiitem{bool ImageBufAlgo.{\ce unpremult} (dst, src, roi=ROI.All, nthreads=0) \\
bool ImageBufAlgo.{\ce premult} (dst, src, roi=ROI.All, nthreads=0)}
\index{ImageBufAlgo!unpremult} \indexapi{unpremult}
\index{ImageBufAlgo!premult} \indexapi{premult}
Copy pixels from {\cf src} to {\cf dst}, and un-premultiply (or
premultiply) the colors by alpha.

\smallskip
\noindent Examples:
\begin{code}
    # Convert in-place from associated alpha to unassociated alpha
    A = ImageBuf ("a.exr")
    ImageBufAlgo.unpremult (A, A)
\end{code}
\apiend



\subsection{Import / export}
\label{sec:iba:py:importexport}

\apiitem{bool ImageBufAlgo.{\ce make_texture} (mode, input,\\
\bigspc\bigspc outputfilename, config=ImageSpec())}
\index{ImageBufAlgo!make_texture} \indexapi{make_texture}

Turn an input image (either an \ImageBuf or a string giving a filename)
into a tiled, MIP-mapped, texture file and write to the
file named by ({\cf outputfilename}).  The {\cf mode} describes what type of texture file we
are creating and may be one of the following:

\noindent \begin{tabular}{p{4in}}
{\cf OpenImageIO.MakeTxTexture} \\
{\cf OpenImageIO.MakeTxEnvLatl} \\
{\cf OpenImageIO.MakeTxEnvLatlFromLightProbe} \\
\end{tabular}

The {\cf config}, if supplied, is an \ImageSpec that contains all the
information and special instructions for making the texture. The full list
of supported configuration options is given in
Section~\ref{sec:iba:importexport}.

\smallskip
\noindent Examples:
\begin{code}
    # This command line:
    #    maketx in.exr --hicomp --filter lanczos3 --opaque-detect \
    #             -o texture.exr
    # is equivalent to:

    Input = ImageBuf ("in.exr")
    config = ImageSpec()
    config.attribute ("maketx:highlightcomp", 1)
    config.attribute ("maketx:filtername", "lanczos3")
    config.attribute ("maketx:opaquedetect", 1)
    ImageBufAlgo.make_texture (oiio.MakeTxTexture, Input,
                               "texture.exr", config)
\end{code}
\apiend


\apiitem{bool ImageBufAlgo::{\ce capture_image} (dst, cameranum, \\
        \bigspc\bigspc  convert = OpenImageIO.UNKNOWN)}
\index{ImageBufAlgo!capture_image} \indexapi{capture_image}
Capture a still image from a designated camera. 

\smallskip
\noindent Examples:
\begin{code}
    WebcamImage = ImageBuf()
    ImageBufAlgo.capture_image (WebcamImage, 0, OpenImageIO.UINT8)
    WebcamImage.save ("webcam.jpg")
\end{code}
\apiend


\newpage
\section{Miscellaneous Utilities}
\label{sec:pythonmiscapi}

In the main {\cf OpenImageIO} module, there are a number of values and
functions that are useful.  These correspond to the C++ API functions
explained in Section~\ref{sec:miscapi}, please refer there for details.

\apiitem{int {\ce openimageio_version}}
The \product version number, 10000 for each
major version, 100 for each minor version, 1 for each patch.  For
example, \product 1.2.3 would return a value of 10203.
\apiend

\apiitem{str {\ce geterror} ()}
Retrieves the latest global error.
\apiend

\apiitem{bool {\ce attribute} (name, typedesc, value) \\
bool {\ce attribute} (name, int_value) \\
bool {\ce attribute} (name, float_value) \\
bool {\ce attribute} (name, str_value)}
Sets a global attribute (see Section~\ref{sec:miscapi} for details),
returning {\cf True} upon success, or {\cf False} if it was not a
recognized attribute.  

\noindent Example:
\begin{code}
    oiio.attribute ("threads", 0)
\end{code}
\apiend


\section{Python Recipes}
\label{sec:pythonrecipes}

This section illustrates the Python syntax for doing many common image
operations from Python scripts, but that aren't already given as examples
in the earlier function descriptions.  All example code fragments assume the
following boilerplate:

\begin{code}
    #!/usr/bin/env python 
    
    import OpenImageIO as oiio
    from OpenImageIO import ImageBuf, ImageSpec, ImageBufAlgo
\end{code}


\subsubsection*{Subroutine to create a constant-colored image}
\begin{code}
    # Create an ImageBuf holding a n image of constant color, given the
    # resolution, data format (defaulting to UINT8), fill value, and image
    # origin.
    def make_constimage (xres, yres, chans=3, format=oiio.UINT8, value=(0,0,0),
                         xoffset=0, yoffset=0) :
        spec = ImageSpec (xres,yres,chans,format)
        spec.x = xoffset
        spec.y = yoffset
        b = ImageBuf (spec)
        oiio.ImageBufAlgo.fill (b, value)
        return b
\end{code}

\noindent The image is returned as an \ImageBuf, then up to the caller 
what to do with it next.

\subsubsection*{Subroutine to save an image to disk, printing errors}
\begin{code}
    # Save an ImageBuf to a given file name, with optional forced image format
    # and error handling.
    def write_image (image, filename, format=oiio.UNKNOWN) :
        if not image.has_error :
            image.set_write_format (format)
            image.write (filename)
        if image.has_error :
            print "Error writing", filename, ":", image.geterror()
\end{code}


\subsubsection*{Converting between file formats}

\begin{code}
    img = ImageBuf ("input.png")
    write_image (img, "output.tif")
\end{code}


\subsubsection*{Comparing two images and writing a difference image}

\begin{code}
    A = ImageBuf ("A.tif")
    B = ImageBuf ("B.tif")
    compresults = oiio.CompareResults()
    ImageBufAlgo.compare (A, B, 1.0e-6, 1.0e-6, compresults)
    if compresults.nfail > 0 :
        print "Images did not match, writing difference image diff.tif"
        diff = ImageBuf()
        ImageBufAlgo.sub (diff, A, B)
        ImageBufAlgo.abs (diff, diff)
        image_write (diff, "diff.tif")
\end{code}


\subsubsection*{Changing the data format or bit depth}

\begin{code}
    img = ImageBuf ("input.exr")
    # presume that it's a "half" OpenEXR file
    # write it back out as a "float" file:
    write_image (img, "output.exr", oiio.FLOAT)
\end{code}


\subsubsection*{Changing the compression}

The following command converts writes a TIFF file, specifically using
LZW compression:

\begin{code}
    img = ImageBuf ("in.tif")
    img.specmod().attribute ("compression", "lzw")
    write_image (img, "compressed.tif")
\end{code}

The following command writes its results as a JPEG file at a 
compression quality of 50 (pretty severe compression):

\begin{code}
    img = ImageBuf ("big.jpg")
    img.specmod().attribute ("quality", 50)
    write_image (img, "small.jpg")
\end{code}


\subsubsection*{Converting between scanline and tiled images}

\begin{code}
    img = ImageBuf ("scan.tif")
    img.set_write_tiles (16, 16)
    write_image (img, "tile.tif")

    img = ImageBuf ("tile.tif")
    img.set_write_tiles (0, 0)
    write_image (img, "scan.tif")
\end{code}


\subsubsection*{Adding captions or metadata}

\begin{code}
    img = ImageBuf ("foo.jpg")
    # Add a caption:
    img.specmod().attribute ("ImageDescription", "Hawaii vacation")
    # Add keywords:
    img.specmod().attribute ("keywords", "volcano,lava")
    write_image (img, "foo.jpg")
\end{code}


\subsubsection*{Changing image boundaries}

\noindent Change the origin of the pixel data window:
\begin{code}
    img = ImageBuf ("in.exr")
    img.specmod().x = 256
    img.specmod().y = 80
    write_image (img, "offset.exr")
\end{code}

\noindent Change the display window:
\begin{code}
    img = ImageBuf ("in.exr")
    img.set_full (16, 1040, 16, 784)
    write_image (img, "out.exr")
\end{code}

\noindent Change the display window to match the data window:
\begin{code}
    img = ImageBuf ("in.exr")
    img.set_full (img.roi())
    write_image (img, "out.exr")
\end{code}

\noindent Cut (trim and extract) a 128x128 region whose upper left corner
is at location (900,300), moving the result to the origin (0,0) of the image
plane and setting the display window to the new pixel data window:
\begin{code}
    img = ImageBuf ("in.exr")
    b = ImageBuf ()
    ImageBufAlgo.cut (b, img, oiio.ROI(900,1028,300,428))
    write_image (b, "out.exr")
\end{code}


\subsubsection*{Extract just the named channels from a complicted many-channel
image, and add an alpha channel that is 1 everywhere}
\begin{code}
    img = ImageBuf ("allmyaovs.exr")
    b = ImageBuf ()
    ImageBufAlgo.channels (b, img, ("spec.R", "spec.G", "spec.B", 1.0))
    write_image (b, "spec.tif")
\end{code}


\subsubsection*{Fade 30\% of the way between two images}

\begin{code}
    a = ImageBuf ()
    ImageBufAlgo.mul (a, ImageBuf("A.exr"), 0.7)
    b = ImageBuf ()
    ImageBufAlgo.mul (b, ImageBuf("B.exr"), 0.3)
    fade = ImageBuf ()
    ImageBufAlgo.add (fade, a, b)
    write_image (fade, "fade.exr")
\end{code}


\subsubsection*{Composite of small foreground over background, with offset}

\begin{code}
    fg = ImageBuf ("fg.exr")
    fg.specmod().x = 512
    fg.specmod().y = 89
    bg = ImageBuf ("bg.exr")
    comp = ImageBuf ()
    ImageBufAlgo.over (comp, fg, bg)
    write_image (fade, "composite.exr")
\end{code}




\index{Python|)}

\chapwidthend
