OpenImageIO icon indicating copy to clipboard operation
OpenImageIO copied to clipboard

API to read ImageInput into ImageBuf

Open lgritz opened this issue 7 months ago • 6 comments

In the comments of #4225, @zavinator suggested:

it would be great if the ImageInput class could integrate more seamlessly with ImageBuf, for instance by returning an ImageBuf instance for each subimage along with its associated metadata.

This is a great idea, and I can think of several possible forms for this to take:

  1. An ImageBuf constructor that takes an open ImageInput* where it reads from the "current" subimage of the input into the IB. (IB already has a write() method that writes to an open ImageOutput that you pass it.)
  2. An ImageInput::read_imagebuf() method a bit like read_image(), but returns (or reads into?) an ImageBuf rather than reading into a memory buffer.
  3. One of the above two, but as a fully standalone utility functions rather than being a method of either ImageBuf or ImageInput.
  4. Some kind of iterator that, given an ImageInput* and ImageBuf*, reads each successive subimage from the ImageInput into the IB. (Choice 1: it's just the iterator and you call next() to advance; choice 2: it's a total black box and the caller doesn't iterate, but rather, what you supply is a lambda that is called for each subimage?)

Or something else I haven't thought of?

Please feel free to continue the discussion in the comments to this issue.

lgritz avatar Jun 16 '25 19:06 lgritz

All of these directions sound promising :)

Starting with an ImageInput::read_imagebuf() method seems most practical. An iterator-based approach could also be great for batch processing of subimages.

zavinator avatar Jun 16 '25 19:06 zavinator

Some thoughts on what these choices might look like:

Choice 1: ImageBuf constructor from open ImageInput

auto inp = ImageInput::open("foo.exr");
ImageBuf buf(*inp, subimage, miplevel, datatype);

Choice 2: ImageInput method to read into an ImageBuf

auto inp = ImageInput::open("foo.exr");
ImageBuf buf = inp->read_imagebuf(subimage, miplevel, datatype);

or

auto inp = ImageInput::open("foo.exr");
ImageBuf buf;
bool ok = inp->read_imagebuf(buf, subimage, miplevel, datatype);
// A little more symmetric to ImageInput::read_image()

Choice 3: Standalone utility

auto inp = ImageInput::open("foo.exr");
ImageBuf buf;
bool ok = OIIO::read_into_imagebuf(buf, &inp);

Choice 4a: Iterator object

auto inp = ImageInput::open("foo.exr");
ImageBuf buf;
for (ReaderIterator it(inp, buf); !it.done; it.next()) {
    // use buf()
}

Choice 4b: Iterating function + lambda

auto inp = ImageInput::open("foo.exr");
OIIO::iterate_ImageBuf_over_ImageInput(
    [](const ImageBuf& buf, int subimage, int miplevel)
    {
        // use buf... subimage and miplevel say which subimage/mip it's pointing to
    }
);

lgritz avatar Jun 16 '25 19:06 lgritz

Another possibility:

ImageBuf buf = ...;
bool ok = buf.next_subimage();

To move the IB to load the next subimage of the same image file. Returns false if not possible or there are no remaining subimages. Only works for IB's that directly read from a file, not for an IB that is an altered copy or the returned result of an IBA function. Could do the same for MIP levels, so you could write a strictly IB-land loop like:

ImageBuf buf("foo.exr");
do {
    do {
        // buf is now at buf.subimage(), buf.miplevel()
    } while (buf.next_miplevel());
} while (buf.next_subimage());

lgritz avatar Jun 16 '25 20:06 lgritz

I'm interested in this in relation to reading raw files. Usually a raw file contains one raw image and a set of JPEG images of various sizes.

Typical use cases would be:

  • read the main sub-image (the raw image)
  • read the biggest available JPEG sub-image, to provide a quick preview.
  • read the thumbnail sub-image closest to the requested size, for example, to show in an image gallery.

I assume this would be impossible to do without iterating on the caller side. Unless we pass some hints to the reader on what kind of image is required, essentially forcing the ImageInput to discard all but one/few sub-images?

antond-weta avatar Jun 16 '25 22:06 antond-weta

Anton, we already have this method of ImageInput:

    virtual bool get_thumbnail(ImageBuf& thumb, int subimage);

it just doesn't yet have an implementation in the RawInput class.

lgritz avatar Jun 16 '25 23:06 lgritz

Looks solid to me:

bool ok = inp->read_imagebuf(buf, subimage, miplevel, datatype);

for (ReaderIterator it(inp, buf); !it.done; it.next()) {
    // use buf()
}
// The question is how to handle mipmap (subimage?) selection - e.g., if someone wants to skip all mipmap levels.

// This one is interesting too
do {
    do {
        // buf is now at buf.subimage(), buf.miplevel()
    } while (buf.next_miplevel());
} while (buf.next_subimage());

zavinator avatar Jun 17 '25 07:06 zavinator