Yeah, this is a bit tricky. The APIs very much want to assume that although
channels may be stored in a planar or separate fashion in the file
(RRRRR...GGGGG.....BBBBB), that you will want to read them into memory buffers
with "contiguous" pixels (RGBRGBRGB...). So there is no API call that directly
reads an image (or scanline or tile) into separate channel buffers, nor any
per-channel strides that would let you force that to happen with a single call.
Perhaps this is an oversight. On the other hand, given that it's hard for me to
recall anybody asking for this in the last 12 years, it may have been the right
assumption in general.
The easy solution is to read it into a temporary buffer and then rescramble it
to separate out the channels. We have some helper functions to make that easy,
look in imageio.h for the convert_image() function (and also
parallel_convert_image). You would basically call this once for each channel,
constructing the strides for src and dst so that they coalesce the R's, G's,
and B's together into their separate buffers.
There is an obvious performance penalty (of the extra copy step). If that's
really critical for anybody, then we could consider adding "separate" versions
of the API calls that instead of one data pointer would take a data[], one per
channel. That would probably be a lot of work, though not exactly rocket
science. Like I said, the lack of requests for this over the years is strong
circumstantial evidence that it's not an important enough case to enough people
to justify the work or the extra API complexity, though if lots of people chime
in that they have wanted this, I could be convinced.
-- lg
> On Oct 15, 2020, at 4:09 AM, Jan Hettenkofer <[email protected]> wrote:
>
> Hello all,
>
> I am struggling to read images into a buffer that organises channels
> in a planar memory layout (i.e. all red scanlines before all blue
> before all green etc...). It appeared to me like specifying the
> strides for `ImageInput::read_image` would be the thing to do here.
> However, upon playing with it for a while I realised that it seems
> to be impossible to specify the stride for the channel values within
> a pixel.
>
> For example the following results in the red channel correctly set
> but all the other channel values set to 0 (untouched from the
> default initialisation I suppose):
>
> ```
> std::vector<T> img_data(spec.width * spec.height * spec.nchannels);
>
> auto column_stride = sizeof(img_data[0]);
> auto row_stride = column_stride * spec.width;
> // initially I assumed that the z-stride would be the one to use
> // to rearrange the channels - now I realise that this is only
> // relevant for volumetric images
> auto slice_stride = row_stride * spec.height;
>
> // helpers::type_to_typedesc<T> returns the OIIO typedesc
> // corresponding to T
> file->read_image(helpers::type_to_typedesc<T>(),
> img_data.data(),
> column_stride,
> row_stride,
> slice_stride
> );
> ```
>
> I suspect that in this case green and blue values are written but
> then immediately overwritten by the red channel in the next two
> pixels.
>
> I could of course just accept the interleaved memory layout and then
> copy the pixels into the right place afterwards.
>
> Another way might be writing a clever adapter that "looks" like an
> array to OIIO but actually just re-routes the writes to a real buffer
> with modified positions. However, this would strongly couple my code
> to implementation details of `read_image`.
>
> Is there a method to specify a planar memory layout for the target
> buffer in order to avoid such workarounds?
>
> Thanks a lot in advance for your help!
>
> With kind regards,
> Jan Hettenkofer
>
> _______________________________________________
> Oiio-dev mailing list
> [email protected]
> http://lists.openimageio.org/listinfo.cgi/oiio-dev-openimageio.org
>
--
Larry Gritz
[email protected]
_______________________________________________
Oiio-dev mailing list
[email protected]
http://lists.openimageio.org/listinfo.cgi/oiio-dev-openimageio.org