How to Use a Region of Interest (ROI) to Restrict Operations to a Specific Image Portion in OpenImageIO

In OpenImageIO, you restrict operations to a specific image portion by passing a Region of Interest (ROI) struct to ImageBufAlgo functions, which limits pixel processing to the defined half-open bounds while leaving the rest of the buffer untouched.

The OpenImageIO library provides a lightweight, value-based mechanism for selective image processing through the Region of Interest (ROI). This struct defines rectangular or volumetric sub-regions using half-open intervals, allowing algorithms to target specific pixels without altering the entire image. Mastering how to use a Region of Interest (ROI) to restrict operations is fundamental for efficient batch processing, cropping, and localized edits in both C++ and Python pipelines.

Understanding the ROI Data Structure

The ROI implementation resides in src/include/OpenImageIO/imageio.h, where it serves as the primary abstraction for spatial and channel bounds.

Core Definition and Bounds

An ROI represents a sub-region through half-open intervals [xbegin, xend) × [ybegin, yend) × [zbegin, zend) with an optional channel range [chbegin, chend). The default constructor creates an undefined ROI that the library interprets as "All", meaning no restriction applies.

According to the source code at lines 88-108, the struct provides direct access to begin/end coordinates for each dimension. The library also supplies utility functions roi_union() and roi_intersection() (lines 95-102) for combining multiple regions algebraically.

ROI and ImageSpec Integration

Every ImageSpec object exposes its data and display windows as ROI objects through specific accessor methods defined around lines 428-456:

  • ROI roi() const noexcept – Returns the data window as an ROI
  • ROI roi_full() const noexcept – Returns the display window as an ROI
  • void set_roi(const ROI &r) noexcept – Replaces the data window
  • void set_roi_full(const ROI &r) noexcept – Replaces the display window

These methods allow seamless conversion between image metadata and processable regions without modifying the underlying channel count.

Practical Implementation in Python and C++

ROI objects are passed by copy to algorithms, making them safe to construct on-the-fly with zero performance penalty.

Python Example: Selective Processing with ROI

The Python bindings in src/python/py_roi.cpp expose the ROI class as oiio.ROI. Below is a complete workflow targeting the test implementation found in testsuite/python-roi/src/test_roi.py:

import OpenImageIO as oiio

# Load source image

buf = oiio.ImageBuf("flower.exr")

# Define ROI: x=[100,300), y=[50,200), all channels

roi = oiio.ROI(100, 300, 50, 200)

# Fill only the ROI region with red (RGB: 1.0, 0.0, 0.0)

oiio.ImageBufAlgo.fill(buf, (1.0, 0.0, 0.0), roi)

# Extract the ROI into a new buffer

sub = oiio.ImageBufAlgo.crop(buf, roi)

# Save the cropped result

sub.write("flower_cropped.exr")

C++ Example: Buffer Manipulation by Region

In C++, ROI usage follows identical semantics through the OIIO namespace, with algorithms implemented in src/libOpenImageIO/imagebufalgo_*.cpp:

#include <OpenImageIO/imageio.h>
#include <OpenImageIO/imagebuf.h>
#include <OpenImageIO/imagebufalgo.h>

using namespace OIIO;

int main() {
    ImageBuf src("flower.exr");
    
    // Define ROI bounds explicitly
    ROI roi(100, 300, 50, 200);
    
    // Process only the specified region
    ImageBufAlgo::fill(src, {0.0f, 1.0f, 0.0f}, roi);
    
    // Crop to new buffer containing only the ROI
    ImageBuf cropped = ImageBufAlgo::crop(src, roi);
    cropped.write("flower_cropped.exr");
    
    return 0;
}

How ImageBufAlgo Algorithms Consume ROI

Most high-level functions in ImageBufAlgo accept an optional ROI parameter that restricts reads and writes to the specified bounds. Key algorithms implementing this pattern include:

  • ImageBufAlgo::fill – Modifies only pixels within the ROI (implementation in imagebufalgo_fill.cpp)
  • ImageBufAlgo::crop and ImageBufAlgo::cut – Extract regions based on ROI boundaries (implementation in imagebufalgo_crop.cpp)
  • ImageBufAlgo::resize – Resamples content restricted to the input ROI
  • ImageBufAlgo::parallel_image – Distributes work across threads limited to the specified region

Internally, these algorithms call contains() or contains_roi() to validate that requested coordinates fall within both the image's data window and the supplied ROI. The command-line utility oiiotool leverages this same backend, forwarding user-specified regions to these algorithms (see src/oiiotool/oiiotool.cpp around lines 3285-3324).

Summary

  • ROI Definition: A lightweight struct using half-open intervals [begin, end) for x, y, z coordinates and channels, defined in src/include/OpenImageIO/imageio.h
  • Default Behavior: An undefined ROI indicates "All" pixels should be processed
  • ImageSpec Integration: Access data/display windows via roi() and roi_full(), or modify them using set_roi() and set_roi_full()
  • Algorithm Restriction: Pass ROI to ImageBufAlgo functions like fill, crop, and resize to limit processing scope
  • Utility Functions: Combine regions using roi_union() and roi_intersection() for complex selection logic

Frequently Asked Questions

What happens if I don't specify an ROI when calling ImageBufAlgo functions?

When you omit the ROI parameter or pass a default-constructed ROI, OpenImageIO treats this as an "All" specification. The algorithm processes the entire data window of the source image without spatial restriction, maintaining backward compatibility with code that predates ROI support.

How do channel ranges work within an ROI?

The ROI struct includes chbegin and chend parameters that specify which image channels to process. For most 2D images, you can omit these (defaulting to 0 and the buffer's channel count), but they become essential when processing specific layers in multi-channel EXR files or volumetric data where z-dimension slicing is required.

Can I combine multiple ROIs to create complex selection shapes?

Yes. The header src/include/OpenImageIO/imageio.h provides roi_union() to create a bounding box encompassing two regions and roi_intersection() to find overlapping areas. These utility functions allow you to construct compound logic—such as masking operations—before passing the final ROI to processing algorithms.

Does restricting operations with ROI improve processing performance?

Using an ROI restricts the pixel iteration domain, which reduces the number of memory accesses and computations performed. For large images and computationally expensive filters (like resize or convolution operations), limiting the active region provides linear speedup proportional to the ratio of ROI area to total image area.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →