ImageBuf: Image Buffers

ImageBuf Introduction and Theory of Operation

ImageBuf is a utility class that stores an entire image. It provides a nice API for reading, writing, and manipulating images as a single unit, without needing to worry about any of the details of storage or I/O.

All I/O involving ImageBuf (that is, calls to read or write) are implemented underneath in terms of ImageCache, ImageInput, and ImageOutput, and so support all of the image file formats supported by OIIO.

The ImageBuf class definition requires that you:

#include <OpenImageIO/imagebuf.h>
enum OIIO::ImageBuf::IBStorage

An ImageBuf can store its pixels in one of several ways (each identified by an IBStorage enumerated value):

Values:

UNINITIALIZED

An ImageBuf that doesn’t represent any image at all (either because it is newly constructed with the default constructor, or had an error during construction).

LOCALBUFFER

“Local storage” is allocated to hold the image pixels internal to the ImageBuf. This memory will be freed when the ImageBuf is destroyed.

APPBUFFER

The ImageBuf “wraps” pixel memory already allocated and owned by the calling application. The caller will continue to own that memory and be responsible for freeing it after the ImageBuf is destroyed.

IMAGECACHE

The ImageBuf is “backed” by an ImageCache, which will automatically be used to retrieve pixels when requested, but the ImageBuf will not allocate separate storage for it. This brings all the advantages of the ImageCache, but can only be used for read-only ImageBuf’s that reference a stored image file.

Constructing, destructing, resetting an ImageBuf

There are several ways to construct an ImageBuf. Each constructor has a corresponding reset method that takes the same arguments. Calling reset on an existing ImageBuf is equivalent to constructing a new ImageBuf from scratch (even if the ImageBuf, prior to reset, previously held an image).

Making an empty or uninitialized ImageBuf

OIIO::ImageBuf::ImageBuf()

Default constructor makes an empty/uninitialized ImageBuf. There isn’t much you can do with an uninitialized buffer until you call reset(). The storage type of a default-constructed ImageBuf is IBStorage::UNINITIALIZED.

void OIIO::ImageBuf::reset()

Destroy any previous contents of the ImageBuf and re-initialize it to resemble a freshly constructed ImageBuf using the default constructor (holding no image, with storage IBStorage::UNINITIALIZED).

Constructing a readable ImageBuf

OIIO::ImageBuf::ImageBuf(string_view name, int subimage = 0, int miplevel = 0, ImageCache *imagecache = nullptr, const ImageSpec *config = nullptr)

Construct a read-only ImageBuf that will be used to read the named file (at the given subimage and MIP-level, defaulting to the first in the file). But don’t read it yet! The image will actually be read lazily, only when other methods need to access the spec and/or pixels, or when an explicit call to init_spec() or read() is made, whichever comes first.

The implementation may end up either reading the entire image internally owned memory (if so, the storage will be LOCALBUFFER), or it may rely on being backed by an ImageCache (in this case, the storage will be IMAGECACHE) depending on the image size and other factors.

Parameters
  • name: The image to read.

  • subimage/miplevel: The subimage and MIP level to read (defaults to the first subimage of the file, highest-res MIP level).

  • imagecache: Optionally, a particular ImageCache to use. If nullptr, the default global/shared image cache will be used.

  • config: Optionally, a pointer to an ImageSpec whose metadata contains configuration hints that set options related to the opening and reading of the file.

void OIIO::ImageBuf::reset(string_view name, int subimage, int miplevel, ImageCache *imagecache = nullptr, const ImageSpec *config = nullptr)

Destroy any previous contents of the ImageBuf and re-initialize it as if newly constructed with the same arguments, as a read-only representation of an existing image file.

Constructing a writeable ImageBuf

OIIO::ImageBuf::ImageBuf(const ImageSpec &spec, InitializePixels zero = InitializePixels::Yes)

Construct a writable ImageBuf with the given specification (including resolution, data type, metadata, etc.). The ImageBuf will allocate and own its own pixel memory and will free that memory automatically upon destruction, clear(), or reset(). Upon successful initialization, the storage will be reported as LOCALBUFFER.

Parameters
  • spec: An ImageSpec describing the image and its metadata. If not enough information is given to know how much memory to allocate (width, height, depth, channels, and data format), the ImageBuf will remain in an UNINITIALIZED state and will have no local pixel storage.

  • zero: After a successful allocation of the local pixel storage, this parameter controls whether the pixels will be initialized to hold zero (black) values (InitializePixels::Yes) or if the pixel memory will remain uninitialized (InitializePixels::No) and thus may hold nonsensical values. Choosing No may save the time of writing to the pixel memory if you know for sure that you are about to overwrite it completely before you will need to read any pixel values.

void OIIO::ImageBuf::reset(const ImageSpec &spec, InitializePixels zero = InitializePixels::Yes)

Destroy any previous contents of the ImageBuf and re-initialize it as if newly constructed with the same arguments, as a read/write image with locally allocated storage that can hold an image as described by spec. The optional zero parameter controls whether the pixel values are filled with black/empty, or are left uninitialized after being allocated.

Note that if the ImageSpec does not contain enough information to specify how much memory to allocate (width, height, channels, and data format), the ImageBuf will remain uninitialized (regardless of how zero is set).

bool OIIO::ImageBuf::make_writeable(bool keep_cache_type = false)

Make the ImageBuf be writable. That means that if it was previously backed by an ImageCache (storage was IMAGECACHE), it will force a full read so that the whole image is in local memory. This will invalidate any current iterators on the image. It has no effect if the image storage is not IMAGECACHE.

Return

Return true if it works (including if no read was necessary), false if something went horribly wrong.

Parameters
  • keep_cache_type: If true, preserve any ImageCache-forced data types (you might want to do this if it is critical that the apparent data type doesn’t change, for example if you are calling make_writeable() from within a type-specialized function).

Constructing an ImageBuf that “wraps” an application buffer

OIIO::ImageBuf::ImageBuf(const ImageSpec &spec, void *buffer)

Construct a writable ImageBuf that “wraps” existing pixel memory owned by the calling application. The ImageBuf does not own the pixel storage and will will not free/delete that memory, even when the ImageBuf is destroyed. Upon successful initialization, the storage will be reported as APPBUFFER.

Parameters
  • spec: An ImageSpec describing the image and its metadata. If not enough information is given to know the “shape” of the image (width, height, depth, channels, and data format), the ImageBuf will remain in an UNINITIALIZED state.

  • buffer: A pointer to the caller-owned memory containing the storage for the pixels. It must be already allocated with enough space to hold a full image as described by spec.

void OIIO::ImageBuf::reset(const ImageSpec &spec, void *buffer)

Destroy any previous contents of the ImageBuf and re-initialize it as if newly constructed with the same arguments, to “wrap” existing pixel memory owned by the calling application.

Reading and Writing disk images

bool OIIO::ImageBuf::read(int subimage = 0, int miplevel = 0, bool force = false, TypeDesc convert = TypeDesc::UNKNOWN, ProgressCallback progress_callback = nullptr, void *progress_callback_data = nullptr)

Read the particular subimage and MIP level of the image. Generally, this will skip the expensive read if the file has already been read into the ImageBuf (at the specified subimage and MIP level). It will clear and re-allocate memory if the previously allocated space was not appropriate for the size or data type of the image being read.

In general, read() will try not to do any I/O at the time of the read() call, but rather to have the ImageBuf “backed” by an ImageCache, which will do the file I/O on demand, as pixel values are needed, and in that case the ImageBuf doesn’t actually allocate memory for the pixels (the data lives in the ImageCache). However, there are several conditions for which the ImageCache will be bypassed, the ImageBuf will allocate “local” memory, and the disk file will be read directly into allocated buffer at the time of the read() call: (a) if the force parameter is true; (b) if the convert parameter requests a data format conversion to a type that is not the native file type and also is not one of the internal types supported by the ImageCache (specifically, float and UINT8); (c) if the ImageBuf already has local pixel memory allocated, or “wraps” an application buffer.

Note that read() is not strictly necessary. If you are happy with the filename, subimage and MIP level specified by the ImageBuf constructor (or the last call to reset()), and you want the storage to be backed by the ImageCache (including storing the pixels in whatever data format that implies), then the file contents will be automatically read the first time you make any other ImageBuf API call that requires the spec or pixel values. The only reason to call read() yourself is if you are changing the filename, subimage, or MIP level, or if you want to use force = true or a specific convert value to force data format conversion.

Return

true upon success, or false if the read failed (in which case, you should be able to retrieve an error message via geterror()).

Parameters
  • subimage/miplevel: The subimage and MIP level to read.

  • force: If true, will force an immediate full read into ImageBuf-owned local pixel memory (yielding a LOCALPIXELS storage buffer). Otherwise, it is up to the implementation whether to immediately read or have the image backed by an ImageCache (storage IMAGECACHE.)

  • convert: If set to a specific type (notUNKNOWN), the ImageBuf memory will be allocated for that type specifically and converted upon read.

  • progress_callback/progress_callback_data: If progress_callback is non-NULL, the underlying read, if expensive, may make several calls to progress_callback(progress_callback_data, portion_done) which allows you to implement some sort of progress meter. Note that if the ImageBuf is backed by an ImageCache, the progress callback will never be called, since no actual file I/O will occur at this time (ImageCache will load tiles or scanlines on demand, as individual pixel values are needed).

bool OIIO::ImageBuf::read(int subimage, int miplevel, int chbegin, int chend, bool force, TypeDesc convert, ProgressCallback progress_callback = nullptr, void *progress_callback_data = nullptr)

Read the file, if possible only allocating and reading a subset of channels, [chbegin..chend-1]. This can be a performance and memory improvement for some image file formats, if you know that any use of the ImageBuf will only access a subset of channels from a many-channel file.

Additional parameters:

Parameters
  • chbegin/chend: The subset (a range with “exclusive end”) of channels to read, if the implementation is able to read only a subset of channels and have a performance advantage by doing so. If chbegin is 0 and chend is either negative or greater than the number of channels in the file, all channels will be read. Please note that it is “advisory” and not guaranteed to be honored by the underlying implementation.

bool OIIO::ImageBuf::init_spec(string_view filename, int subimage, int miplevel)

Read the ImageSpec for the given file, subimage, and MIP level into the ImageBuf, but will not read the pixels or allocate any local storage (until a subsequent call to read()). This is helpful if you have an ImageBuf and you need to know information about the image, but don’t want to do a full read yet, and maybe won’t need to do the full read, depending on what’s found in the spec.

Note that init_spec() is not strictly necessary. If you are happy with the filename, subimage and MIP level specified by the ImageBuf constructor (or the last call to reset()), then the spec will be automatically read the first time you make any other ImageBuf API call that requires it. The only reason to call read() yourself is if you are changing the filename, subimage, or MIP level, or if you want to use force=true or a specific convert value to force data format conversion.

Return

true upon success, or false if the read failed (in which case, you should be able to retrieve an error message via geterror()).

Parameters
  • filename: The filename to read from (should be the same as the filename used when the ImageBuf was constructed or reset.)

  • subimage/miplevel: The subimage and MIP level to read.

bool OIIO::ImageBuf::write(string_view filename, TypeDesc dtype = TypeUnknown, string_view fileformat = string_view(), ProgressCallback progress_callback = nullptr, void *progress_callback_data = nullptr) const

Write the image to the named file, converted to the specified pixel data type dtype (TypeUnknown signifies to use the data type of the buffer), and file format (an empty fileformat means to infer the type from the filename extension).

By default, it will always try to write a scanline-oriented file, unless the set_write_tiles() method has been used to override this.

Return

true upon success, or false if the write failed (in which case, you should be able to retrieve an error message via geterror()).

Parameters
  • filename: The filename to write to.

  • dtype: Optional override of the pixel data format to use in the file being written. The default (UNKNOWN) means to try writing the same data format that as pixels are stored within the ImageBuf memory (or whatever type was specified by a prior call to set_write_format()). In either case, if the file format does not support that data type, another will be automatically chosen that is supported by the file type and loses as little precision as possible.

  • fileformat: Optional override of the file format to write. The default (empty string) means to infer the file format from the extension of the filename (for example, “foo.tif” will write a TIFF file).

  • progress_callback/progress_callback_data: If progress_callback is non-NULL, the underlying write, if expensive, may make several calls to progress_callback(progress_callback_data, portion_done) which allows you to implement some sort of progress meter.

bool OIIO::ImageBuf::write(ImageOutput *out, ProgressCallback progress_callback = nullptr, void *progress_callback_data = nullptr) const

Write the pixels of the ImageBuf to an open ImageOutput. The ImageOutput must have already been opened with a spec that indicates a resolution identical to that of this ImageBuf (but it may have specified a different pixel data type, in which case data conversions will happen automatically). This method does NOT close the file when it’s done (and so may be called in a loop to write a multi-image file).

Note that since this uses an already-opened ImageOutput, which is too late to change how it was opened, it does not honor any prior calls to set_write_format or set_write_tiles.

The main application of this method is to allow an ImageBuf (which by design may hold only a single image) to be used for the output of one image of a multi-subimage and/or MIP-mapped image file.

Return

true if all went ok, false if there were errors writing.

Parameters
  • out: A pointer to an already-opened ImageOutput to which the pixels of the ImageBuf will be written.

  • progress_callback/progress_callback_data: If progress_callback is non-NULL, the underlying write, if expensive, may make several calls to progress_callback(progress_callback_data, portion_done) which allows you to implement some sort of progress meter.

void OIIO::ImageBuf::set_write_format(TypeDesc format)

Set the pixel data format that will be used for subsequent write() calls that do not themselves request a specific data type request.

Note that this does not affect the variety of write() that takes an open ImageOutput* as a parameter.

Parameters
  • format: The data type to be used for all channels.

void OIIO::ImageBuf::set_write_format(cspan<TypeDesc> format)

Set the per-channel pixel data format that will be used for subsequent write() calls that do not themselves request a specific data type request.

Parameters
  • format: The type of each channel (in order). Any channel’s format specified as TypeUnknown will default to be whatever type is described in the ImageSpec of the buffer.

void OIIO::ImageBuf::set_write_tiles(int width = 0, int height = 0, int depth = 0)

Override the tile sizing for subsequent calls to the write() method (the variety that does not take an open ImageOutput*). Setting all three dimensions to 0 indicates that the output should be a scanline-oriented file.

This lets you write a tiled file from an ImageBuf that may have been read originally from a scanline file, or change the dimensions of a tiled file, or to force the file written to be scanline even if it was originally read from a tiled file.

In all cases, if the file format ultimately written does not support tiling, or the tile dimensions requested, a suitable supported tiling choice will be made automatically.

Getting and setting information about an ImageBuf

bool OIIO::ImageBuf::initialized() const

Returns true if the ImageBuf is initialized, false if not yet initialized.

IBStorage OIIO::ImageBuf::storage() const

Which type of storage is being used for the pixels? Returns an enumerated type describing the type of storage currently employed by the ImageBuf: UNINITIALIZED (no storage), LOCALBUFFER (the ImageBuf has allocated and owns the pixel memory), APPBUFFER (the ImageBuf “wraps” memory owned by the calling application), or IMAGECACHE (the image is backed by an ImageCache).

const ImageSpec &OIIO::ImageBuf::spec() const

Return a read-only (const) reference to the image spec that describes the buffer.

const ImageSpec &OIIO::ImageBuf::nativespec() const

Return a read-only (const) reference to the “native” image spec (that describes the file, which may be slightly different than the spec of the ImageBuf, particularly if the IB is backed by an ImageCache that is imposing some particular data format or tile size).

This may differ from spec() for example, if a data format conversion was requested, if the buffer is backed by an ImageCache which stores the pixels internally in a different data format than that of the file, or if the file had differing per-channel data formats (ImageBuf must contain a single data format for all channels).

ImageSpec &OIIO::ImageBuf::specmod()

Return a writable reference to the ImageSpec that describes the buffer. It’s ok to modify most of the metadata, but if you modify the spec’s format, width, height, or depth fields, you get the pain you deserve, as the ImageBuf will no longer have correct knowledge of its pixel memory layout. USE WITH EXTREME CAUTION.

string_view OIIO::ImageBuf::name(void) const

Returns the name of the buffer (name of the file, for an ImageBuf read from disk).

string_view OIIO::ImageBuf::file_format_name(void) const

Return the name of the image file format of the disk file we read into this image, for example "openexr". Returns an empty string if this image was not the result of a read().

int OIIO::ImageBuf::subimage() const

Return the index of the subimage the ImageBuf is currently holding.

int OIIO::ImageBuf::nsubimages() const

Return the number of subimages in the file.

int OIIO::ImageBuf::miplevel() const

Return the index of the miplevel the ImageBuf is currently holding.

int OIIO::ImageBuf::nmiplevels() const

Return the number of miplevels of the current subimage.

int OIIO::ImageBuf::nchannels() const

Return the number of color channels in the image. This is equivalent to spec().nchannels.

int xbegin() const
int xend() const
int ybegin() const
int yend() const
int zbegin() const
int zend() const

Returns the [begin,end) range of the pixel data window of the buffer. These are equivalent to spec().x, spec().x+spec().width, spec().y, spec().y+spec().height, spec().z, and spec().z+spec().depth, respectively.

int OIIO::ImageBuf::orientation() const

Return the current "Orientation" metadata for the image, per the table in sec-metadata-orientation_

void OIIO::ImageBuf::set_orientation(int orient)

Set the "Orientation" metadata for the image.

int oriented_width() const
int oriented_height() const
int oriented_x() const
int oriented_y() const
int oriented_full_width() const
int oriented_full_height() const
int oriented_full_x() const
int oriented_full_y() const

The oriented width, height, x, and y describe the pixel data window after taking the display orientation into consideration. The full versions the “full” (a.k.a. display) window after taking the display orientation into consideration.

ROI OIIO::ImageBuf::roi() const

Return pixel data window for this ImageBuf as a ROI.

ROI OIIO::ImageBuf::roi_full() const

Return full/display window for this ImageBuf as a ROI.

void OIIO::ImageBuf::set_origin(int x, int y, int z = 0)

Alters the metadata of the spec in the ImageBuf to reset the “origin” of the pixel data window to be the specified coordinates. This does not affect the size of the pixel data window, only its position.

void OIIO::ImageBuf::set_full(int xbegin, int xend, int ybegin, int yend, int zbegin, int zend)

Set the “full” (a.k.a. display) window to Alters the metadata of the spec in the ImageBuf to reset the “full” image size (a.k.a. “display window”) to

[xbegin,xend) x [ybegin,yend) x [zbegin,zend)`

This does not affect the size of the pixel data window.

void OIIO::ImageBuf::set_roi_full(const ROI &newroi)

Set full/display window for this ImageBuf to a ROI. Does NOT change the channels of the spec, regardless of newroi.

bool OIIO::ImageBuf::contains_roi(ROI roi) const

Is the specified roi completely contained in the data window of this ImageBuf?

TypeDesc OIIO::ImageBuf::pixeltype() const

The data type of the pixels stored in the buffer (equivalent to spec().format).

int OIIO::ImageBuf::threads() const

Retrieve the current thread-spawning policy of this ImageBuf.

void OIIO::ImageBuf::threads(int n) const

Set the threading policy for this ImageBuf, controlling the maximum amount of parallelizing thread “fan-out” that might occur during expensive operations. The default of 0 means that the global attribute("threads") value should be used (which itself defaults to using as many threads as cores).

The main reason to change this value is to set it to 1 to indicate that the calling thread should do all the work rather than spawning new threads. That is probably the desired behavior in situations where the calling application has already spawned multiple worker threads.

Copying ImageBuf’s and blocks of pixels

const ImageBuf &OIIO::ImageBuf::operator=(const ImageBuf &src)

Copy assignment.

const ImageBuf &OIIO::ImageBuf::operator=(ImageBuf &&src)

Move assignment.

bool OIIO::ImageBuf::copy(const ImageBuf &src, TypeDesc format = TypeUnknown)

Try to copy the pixels and metadata from src to *this (optionally with an explicit data format conversion).

If the previous state of *this was uninitialized, owning its own local pixel memory, or referring to a read-only image backed by ImageCache, then local pixel memory will be allocated to hold the new pixels and the call always succeeds unless the memory cannot be allocated. In this case, the format parameter may request a pixel data type that is different from that of the source buffer.

If *this previously referred to an app-owned memory buffer, the memory cannot be re-allocated, so the call will only succeed if the app-owned buffer is already the correct resolution and number of channels. The data type of the pixels will be converted automatically to the data type of the app buffer.

Return

true upon success or false upon error/failure.

Parameters
  • src: Another ImageBuf from which to copy the pixels and metadata.

  • format: Optionally request the pixel data type to be used. The default of TypeUnknown means to use whatever data type is used by the src. If *this is already initialized and has APPBUFFER storage (“wrapping” an application buffer), this parameter is ignored.

ImageBuf OIIO::ImageBuf::copy(TypeDesc format) const

Return a full copy of this ImageBuf (optionally with an explicit data format conversion).

void OIIO::ImageBuf::copy_metadata(const ImageBuf &src)

Copy all the metadata from src to *this (except for pixel data resolution, channel types and names, and data format).

bool OIIO::ImageBuf::copy_pixels(const ImageBuf &src)

Copy the pixel data from src to *this, automatically converting to the existing data format of *this. It only copies pixels in the overlap regions (and channels) of the two images; pixel data in *this that do exist in src will be set to 0, and pixel data in src that do not exist in *this will not be copied.

void OIIO::ImageBuf::swap(ImageBuf &other)

Swap the entire contents with another ImageBuf.

Getting and setting pixel values

Getting and setting individual pixels – slow

float OIIO::ImageBuf::getchannel(int x, int y, int z, int c, WrapMode wrap = WrapBlack) const

Retrieve a single channel of one pixel.

Return

The data value, converted to a float.

Parameters
  • x/y/z: The pixel coordinates.

  • c: The channel index to retrieve.

  • wrap: WrapMode that determines the behavior if the pixel coordinates are outside the data window: WrapBlack, WrapClamp, WrapPeriodic, WrapMirror.

void OIIO::ImageBuf::getpixel(int x, int y, int z, float *pixel, int maxchannels = 1000, WrapMode wrap = WrapBlack) const

Retrieve the pixel value by x, y, z pixel indices, placing its contents in pixel[0..n-1] where n is the smaller of maxchannels the actual number of channels stored in the buffer.

Parameters
  • x/y/z: The pixel coordinates.

  • pixel: The results are stored in pixel[0..nchannels-1]. It is up to the caller to ensure that pixel points to enough memory to hold the required number of channels.

  • maxchannels: Optional clamp to the number of channels retrieved.

  • wrap: WrapMode that determines the behavior if the pixel coordinates are outside the data window: WrapBlack, WrapClamp, WrapPeriodic, WrapMirror.

void OIIO::ImageBuf::interppixel(float x, float y, float *pixel, WrapMode wrap = WrapBlack) const

Sample the image plane at pixel coordinates (x,y), using linear interpolation between pixels, placing the result in pixel[].

Parameters
  • x/y: The pixel coordinates. Note that pixel data values themselves are at the pixel centers, so pixel (i,j) is at image plane coordinate (i+0.5, j+0.5).

  • pixel: The results are stored in pixel[0..nchannels-1]. It is up to the caller to ensure that pixel points to enough memory to hold the number of channels in the image.

  • wrap: WrapMode that determines the behavior if the pixel coordinates are outside the data window: WrapBlack, WrapClamp, WrapPeriodic, WrapMirror.

void OIIO::ImageBuf::interppixel_bicubic(float x, float y, float *pixel, WrapMode wrap = WrapBlack) const

Bicubic interpolation at pixel coordinates (x,y).

void OIIO::ImageBuf::interppixel_NDC(float s, float t, float *pixel, WrapMode wrap = WrapBlack) const

Linearly interpolate at NDC coordinates (s,t), where (0,0) is the upper left corner of the display window, (1,1) the lower right corner of the display window.

Note

interppixel() uses pixel coordinates (ranging 0..resolution) whereas interppixel_NDC() uses NDC coordinates (ranging 0..1).

void OIIO::ImageBuf::interppixel_bicubic_NDC(float s, float t, float *pixel, WrapMode wrap = WrapBlack) const

Bicubic interpolation at NDC space coordinates (s,t), where (0,0) is the upper left corner of the display (a.k.a. “full”) window, (1,1) the lower right corner of the display window.

void OIIO::ImageBuf::setpixel(int x, int y, int z, const float *pixel, int maxchannels = 1000)

Set the pixel with coordinates (x,y,z) to have the values in pixel[0..n-1]. The number of channels copied, n, is the minimum of maxchannels and the actual number of channels in the image.

void OIIO::ImageBuf::setpixel(int i, const float *pixel, int maxchannels = 1000)

Set the i-th pixel value of the image (out of width*height*depth), from floating-point values in pixel[]. Set at most maxchannels (will be clamped to the actual number of channels).


Getting and setting regions of pixels – fast

bool OIIO::ImageBuf::get_pixels(ROI roi, TypeDesc format, void *result, stride_t xstride = AutoStride, stride_t ystride = AutoStride, stride_t zstride = AutoStride) const

Retrieve the rectangle of pixels spanning the ROI (including channels) at the current subimage and MIP-map level, storing the pixel values beginning at the address specified by result and with the given strides (by default, AutoStride means the usual contiguous packing of pixels) and converting into the data type described by format. It is up to the caller to ensure that result points to an area of memory big enough to accommodate the requested rectangle. Return true if the operation could be completed, otherwise return false.

bool OIIO::ImageBuf::set_pixels(ROI roi, TypeDesc format, const void *data, stride_t xstride = AutoStride, stride_t ystride = AutoStride, stride_t zstride = AutoStride)

Copy the data into the given ROI of the ImageBuf. The data points to values specified by format, with layout detailed by the stride values (in bytes, with AutoStride indicating “contiguous” layout). It is up to the caller to ensure that data points to an area of memory big enough to account for the ROI. Return true if the operation could be completed, otherwise return false.

Deep data in an ImageBuf

bool OIIO::ImageBuf::deep() const

Does this ImageBuf store deep data? Returns true if the ImageBuf holds a “deep” image, false if the ImageBuf holds an ordinary pixel-based image.

int OIIO::ImageBuf::deep_samples(int x, int y, int z = 0) const

Retrieve the number of deep data samples corresponding to pixel (x,y,z). Return 0 if not a deep image, or if the pixel is outside of the data window, or if the designated pixel has no deep samples.

void OIIO::ImageBuf::set_deep_samples(int x, int y, int z, int nsamples)

Set the number of deep samples for pixel (x,y,z). If data has already been allocated, this is equivalent to inserting or erasing samples.

void OIIO::ImageBuf::deep_insert_samples(int x, int y, int z, int samplepos, int nsamples)

Insert nsamples new samples, starting at position samplepos of pixel (x,y,z).

void OIIO::ImageBuf::deep_erase_samples(int x, int y, int z, int samplepos, int nsamples)

Remove nsamples samples, starting at position samplepos of pixel (x,y,z).

float OIIO::ImageBuf::deep_value(int x, int y, int z, int c, int s) const

Return the value (as a float) of sample s of channel c of pixel (x,y,z). Return 0 if not a deep image or if the pixel coordinates or channel number are out of range or if that pixel has no deep samples.

uint32_t OIIO::ImageBuf::deep_value_uint(int x, int y, int z, int c, int s) const

Return the value (as a uint32_t) of sample s of channel c of pixel (x,y,z). Return 0 if not a deep image or if the pixel coordinates or channel number are out of range or if that pixel has no deep samples.

void OIIO::ImageBuf::set_deep_value(int x, int y, int z, int c, int s, float value)

Set the value of sample s of channel c of pixel (x,y,z) to a float value (it is expected that channel c is a floating point type).

void OIIO::ImageBuf::set_deep_value(int x, int y, int z, int c, int s, uint32_t value)

Set the value of sample s of channel c of pixel (x,y,z) to a uint32_t value (it is expected that channel c is an integer type).

const void *OIIO::ImageBuf::deep_pixel_ptr(int x, int y, int z, int c, int s = 0) const

Return a pointer to the raw data of pixel (x,y,z), channel c, sample s. Return nullptr if the pixel coordinates or channel number are out of range, if the pixel/channel has no deep samples, or if the image is not deep. Use with caution these pointers may be invalidated by calls that adjust the number of samples in any pixel.

DeepData &OIIO::ImageBuf::deepdata()
const DeepData &OIIO::ImageBuf::deepdata() const

Returns a reference to the underlying DeepData for a deep image.

Error Handling

template<typename ...Args>
void OIIO::ImageBuf::errorf(const char *fmt, const Args&... args) const

Error reporting for ImageBuf: call this with printf-like arguments to report an error.

bool OIIO::ImageBuf::has_error(void) const

Returns true if the ImageBuf has had an error and has an error message ready to retrieve via geterror().

std::string OIIO::ImageBuf::geterror(void) const

Return the text of all error messages issued since geterror() was called (or an empty string if no errors are pending). This also clears the error message for next time.

Miscellaneous

void *localpixels()
const void *localpixels() const

Return a raw pointer to the “local” pixel memory, if they are fully in RAM and not backed by an ImageCache, or nullptr otherwise. You can also test it like a bool to find out if pixels are local.

void *pixeladdr(int x, int y, int z = 0, int ch = 0)
const void *pixeladdr(int x, int y, int z = 0, int ch = 0) const

Return the address where pixel (x,y,z), channel ch, is stored in the image buffer. Use with extreme caution! Will return nullptr if the pixel values aren’t local in RAM.

int OIIO::ImageBuf::pixelindex(int x, int y, int z, bool check_range = false) const

Return the index of pixel (x,y,z). If check_range is true, return -1 for an invalid coordinate that is not within the data window.

static WrapMode OIIO::ImageBuf::WrapMode_from_string(string_view name)

Return the WrapMode corresponding to the name ("default", "black", "clamp", "periodic", "mirror"). For an unknown name, this will return WrapDefault.

Iterators – the fast way of accessing individual pixels

Sometimes you need to visit every pixel in an ImageBuf (or at least, every pixel in a large region). Using the getpixel() and setpixel() for this purpose is simple but very slow. But ImageBuf provides templated Iterator and ConstIterator types that are very inexpensive and hide all the details of local versus cached storage.

Note

ImageBuf::ConstIterator is identical to the Iterator, except that ConstIterator may be used on a const ImageBuf and may not be used to alter the contents of the ImageBuf. For simplicity, the remainder of this section will only discuss the Iterator.

An Iterator is associated with a particular ImageBuf. The Iterator has a current pixel coordinate that it is visiting, and an iteration range that describes a rectangular region of pixels that it will visits as it advances. It always starts at the upper left corner of the iteration region. We say that the iterator is done after it has visited every pixel in its iteration range. We say that a pixel coordinate exists if it is within the pixel data window of the ImageBuf. We say that a pixel coordinate is valid if it is within the iteration range of the iterator.

The Iterator<BUFT,USERT> is templated based on two types: BUFT the type of the data stored in the ImageBuf, and USERT type type of the data that you want to manipulate with your code. USERT defaults to float, since usually you will want to do all your pixel math with float. We will thus use Iterator<T> synonymously with Iterator<T,float>.

For the remainder of this section, we will assume that you have a float-based ImageBuf, for example, if it were set up like this:

ImageBuf buf ("myfile.exr");
buf.read (0, 0, true, TypeDesc::FLOAT);
template<>
Iterator<BUFT>(ImageBuf &buf, WrapMode wrap = WrapDefault)

Initialize an iterator that will visit every pixel in the data window of buf, and start it out pointing to the upper left corner of the data window. The wrap describes what values will be retrieved if the iterator is positioned outside the data window of the buffer.

template<>
Iterator<BUFT>(ImageBuf &buf, const ROI &roi, WrapMode wrap = WrapDefault)

Initialize an iterator that will visit every pixel of buf within the region described by roi, and start it out pointing to pixel (roi.xbegin, roi.ybegin, roi.zbegin). The wrap describes what values will be retrieved if the iterator is positioned outside the data window of the buffer.

template<>
Iterator<BUFT>(ImageBuf &buf, int x, int y, int z, WrapMode wrap = WrapDefault)

Initialize an iterator that will visit every pixel in the data window of buf, and start it out pointing to pixel (x, y, z). The wrap describes what values will be retrieved if the iterator is positioned outside the data window of the buffer.

Iterator::operator++()

The ++ operator advances the iterator to the next pixel in its iteration range. (Both prefix and postfix increment operator are supported.)

bool Iterator::done() const

Returns true if the iterator has completed its visit of all pixels in its iteration range.

ROI Iterator::range() const

Returns the iteration range of the iterator, expressed as an ROI.

int Iterator::x() const
int Iterator::y() const
int Iterator::z() const

Returns the x, y, and z pixel coordinates, respectively, of the pixel that the iterator is currently visiting.

bool Iterator::valid() const

Returns true if the iterator’s current pixel coordinates are within its iteration range.

bool Iterator::valid(int x, int y, int z = 0) const

Returns true if pixel coordinate (x, y, z) are within the iterator’s iteration range (regardless of where the iterator itself is currently pointing).

bool Iterator::exists() const

Returns true if the iterator’s current pixel coordinates are within the data window of the ImageBuf.

bool Iterator::exists(int x, int y, int z = 0) const

Returns true if pixel coordinate (x, y, z) are within the pixel data window of the ImageBuf (regardless of where the iterator itself is currently pointing).

USERT &Iterator::operator[](int i)

The value of channel i of the current pixel. (The wrap mode, set up when the iterator was constructed, determines what value is returned if the iterator points outside the pixel data window of its buffer.)

int Iterator::deep_samples() const

For deep images only, retrieves the number of deep samples for the current pixel.

void Iterator::set_deep_samples()

For deep images only (and non-const ImageBuf), set the number of deep samples for the current pixel. This only is useful if the ImageBuf has not yet had the deep_alloc() method called.

USERT Iterator::deep_value(int c, int s) const
uint32_t Iterator::deep_value_int(int c, int s) const

For deep images only, returns the value of channel c, sample number s, at the current pixel.

void Iterator::set_deep_value(int c, int s, float value)
void Iterator::set_deep_value(int c, int s, uint32_t value)

For deep images only (and non-cconst ImageBuf, sets the value of channel c, sample number s, at the current pixel. This only is useful if the ImageBuf has already had the deep_alloc() method called.

Example: Visiting all pixels to compute an average color

void print_channel_averages (const std::string &filename)
{
    // Set up the ImageBuf and read the file
    ImageBuf buf (filename);
    bool ok = buf.read (0, 0, true, TypeDesc::FLOAT);  // Force a float buffer
    if (! ok)
        return;

    // Initialize a vector to contain the running total
    int nc = buf.nchannels();
    std::vector<float> total (n, 0.0f);

    // Iterate over all pixels of the image, summing channels separately
    for (ImageBuf::ConstIterator<float> it (buf);  ! it.done();  ++it)
        for (int c = 0;  c < nc;  ++c)
            total[c] += it[c];

    // Print the averages
    imagesize_t npixels = buf.spec().image_pixels();
    for (int c = 0;  c < nc;  ++c)
        std::cout << "Channel " << c << " avg = " (total[c] / npixels) << "\n";
}

Example: Set all pixels in a region to black

bool make_black (ImageBuf &buf, ROI region)
{
    if (buf.spec().format != TypeDesc::FLOAT)
        return false;    // Assume it's a float buffer

    // Clamp the region's channel range to the channels in the image
    roi.chend = std::min (roi.chend, buf.nchannels);

    // Iterate over all pixels in the region...
    for (ImageBuf::Iterator<float> it (buf, region);  ! it.done();  ++it) {
        if (! it.exists())   // Make sure the iterator is pointing
            continue;        //   to a pixel in the data window
        for (int c = roi.chbegin;  c < roi.chend;  ++c)
            it[c] = 0.0f;  // clear the value
    }
    return true;
}

Dealing with buffer data types

The previous section on iterators presented examples and discussion based on the assumption that the ImageBuf was guaranteed to store float data and that you wanted all math to also be done as float computations. Here we will explain how to deal with buffers and files that contain different data types.

Strategy 1: Only have float data in your ImageBuf

When creating your own buffers, make sure they are float:

ImageSpec spec (640, 480, 3, TypeDesc::FLOAT); // <-- float buffer
ImageBuf buf ("mybuf", spec);

When using ImageCache-backed buffers, force the ImageCache to convert everything to float:

// Just do this once, to set up the cache:
ImageCache *cache = ImageCache::create (true /* shared cache */);
cache->attribute ("forcefloat", 1);
...
ImageBuf buf ("myfile.exr");   // Backed by the shared cache

Or force the read to convert to float in the buffer if it’s not a native type that would automatically stored as a float internally to the ImageCache:1

1

ImageCache only supports a limited set of types internally, currently float, half, uint8, uint16, and all other data types are converted to these automatically as they are read into the cache.

ImageBuf buf ("myfile.exr");   // Backed by the shared cache
buf.read (0, 0, false /* don't force read to local mem */,
          TypeDesc::FLOAT /* but do force conversion to float*/);

Or force a read into local memory unconditionally (rather than relying on the ImageCache), and convert to float:

ImageBuf buf ("myfile.exr");
buf.read (0, 0, true /*force read*/,
          TypeDesc::FLOAT /* force conversion */);

Strategy 2: Template your iterating functions based on buffer type

Consider the following alternate version of the make_black function from Section Example: Set all pixels in a region to black

template<type BUFT>
static bool make_black_impl (ImageBuf &buf, ROI region)
{
    // Clamp the region's channel range to the channels in the image
    roi.chend = std::min (roi.chend, buf.nchannels);

    // Iterate over all pixels in the region...
    for (ImageBuf::Iterator<BUFT> it (buf, region);  ! it.done();  ++it) {
        if (! it.exists())   // Make sure the iterator is pointing
            continue;        //   to a pixel in the data window
        for (int c = roi.chbegin;  c < roi.chend;  ++c)
            it[c] = 0.0f;  // clear the value
    }
    return true;
}

bool make_black (ImageBuf &buf, ROI region)
{
    if (buf.spec().format == TypeDesc::FLOAT)
        return make_black_impl<float> (buf, region);
    else if (buf.spec().format == TypeDesc::HALF)
        return make_black_impl<half> (buf, region);
    else if (buf.spec().format == TypeDesc::UINT8)
        return make_black_impl<unsigned char> (buf, region);
    else if (buf.spec().format == TypeDesc::UINT16)
        return make_black_impl<unsigned short> (buf, region);
    else {
        buf.error ("Unsupported pixel data format %s", buf.spec().format);
        retrn false;
    }
}

In this example, we make an implementation that is templated on the buffer type, and then a wrapper that calls the appropriate template specialization for each of 4 common types (and logs an error in the buffer for any other types it encounters).

In fact, imagebufalgo_util.h provides a macro to do this (and several variants, which will be discussed in more detail in the next chapter). You could rewrite the example even more simply:

#include <OpenImageIO/imagebufalgo_util.h>

template<type BUFT>
static bool make_black_impl (ImageBuf &buf, ROI region)
{
    ... same as before ...
}

bool make_black (ImageBuf &buf, ROI region)
{
    bool ok;
    OIIO_DISPATCH_COMMON_TYPES (ok, "make_black", make_black_impl,
                                 buf.spec().format, buf, region);
    return ok;
}

This other type-dispatching helper macros will be discussed in more detail in Chapter ImageBufAlgo: Image Processing.