Hmm, good point, forgot about this. uint8 would be weird default on the python side.
I like the oiio.FLOAT default idea better than the others as it will probably be more obvious what happened in their code should they happen to be using the read without supplying the format. It may break things but it breaks them the "least" in my opinion. Requiring the argument for every call is probably better long term but we're already changing behavior with supplying oiio.UNKNOWN so we should probably confine the real changes to those folks as best we can. Just my two cents. TL;DR: I'd go with oiio.FLOAT as a default ~Andrew On Fri, Feb 19, 2016 at 4:52 PM, Larry Gritz <[email protected]> wrote: > OK, just one more wrinkle. > > For a script that passes no format at all, the default is/was UNKNOWN, but > that changes behavior. Before, it would return an array based on > spec.format (the "widest" format of the channels). But now, it means to > return a uint8 blob of packed native data, which is "advanced usage" for > sure, and possibly not a good default. > > I propose changing the parameter default to oiio.FLOAT, which gives a > sensible behavior for apps as well as making it very likely that old apps > will continue to work in some kind of sensible way. > > Another way to go is to require the argument to be passed. That will break > old Python scripts, but will force everything to be explicit moving forward. > > > > On Feb 19, 2016, at 11:49 AM, Haarm-Pieter Duiker <[email protected]> > wrote: > > Two thumbs up! (packed into one float thumbs up) > > HP > > > > > On Fri, Feb 19, 2016 at 10:21 AM, Andrew Gartner <[email protected]> > wrote: > >> Yep, I'm good with that. >> >> Thanks again for teasing this apart Larry/HP >> >> ~Andrew >> >> On Fri, Feb 19, 2016 at 10:09 AM, Larry Gritz <[email protected]> wrote: >> >>> OK, that sounds reasonable. So we have: >>> >>> * If you ask for a specific type, convert and return an array of that >>> type. If you ask for HALF, the half bit pattern gets returned in a uint16 >>> array, since there is no true half type. >>> >>> * If you ask for UNKNOWN (explicitly "give me raw data"), it returns an >>> array of unsigned chars containing the raw data. >>> >>> Everybody can live with that? >>> >>> >>> On Feb 19, 2016, at 8:08 AM, Haarm-Pieter Duiker < >>> [email protected]> wrote: >>> >>> Of the options "pass half values disguised as an unsigned short array" >>> feels the cleanest to me. You keep the right number of components in the >>> array, if you have any checks for that, and the data to be convert to halfs >>> is already grouped appropriately. >>> >>> Converting to halfs is also a one line call to numpy: >>> np.frombuffer(np.getbuffer(np.uint16(uint16Value)), dtype=np.float16) >>> Ex. https://github.com/hpd/CLF/blob/master/python/aces/clf/Common.py#L92 >>> >>> HP >>> >>> >>> >>> >>> >>> On Thu, Feb 18, 2016 at 4:06 PM, Larry Gritz <[email protected]> wrote: >>> >>>> In C++, asking for UNKNOWN just copies the native format data and >>>> leaves it for you to sort out. But to C++, a buffer is a buffer, you're >>>> passing it a void* in any case. >>>> >>>> In Python, it's dynamic typing, so read_image RETURNS an array, and it >>>> has to be an array of some type. Which type? >>>> >>>> I think we all are coming to agree that if you ask for UNKNOWN, >>>> probably the most analogous thing (to C++) is to return an unsigned char >>>> array, filled with the raw data, and leave you to sort it out. That's as >>>> close to "untyped raw buffer" as we can get. >>>> >>>> If you *ask* for HALF, it's nonsensical, because you can't make an >>>> actual half array in Python. You could promote and convert it to float. Or >>>> you could return raw values in unsigned char array (like if you'd passed >>>> UNKNOWN). Or, yeah, another possibility is to pass half values disguised as >>>> an unsigned short array? >>>> >>>> I'm not super fond of the last choice. >>>> >>>> Right now, we do something stupider than any of those -- which is to >>>> pack raw half values into a buffer, but the buffer advertises itself as >>>> being a float array. That clearly needs to change. It was never >>>> intentional; I just never thought carefully about that case because I never >>>> imagined anybody asking for a type that didn't exist in Python. >>>> >>>> So, current proposal on the table: >>>> >>>> * If you ask for a type that can be a valid Python array type, convert >>>> and return an array of that type. >>>> >>>> * If you ask for UNKNOWN (explicitly "give me raw data") or HALF >>>> (implicitly so, because it doesn't exist in Python), it returns an array of >>>> unsigned chars containing the raw data. >>>> >>>> >>>> >>>> On Feb 18, 2016, at 3:36 PM, Haarm-Pieter Duiker < >>>> [email protected]> wrote: >>>> >>>> Returning a series of unsigned 16 bit ints for a call with the type >>>> half feels like a nice middle ground. The consumer will have to know that >>>> halfs aren't natively supported in Python, and how to convert from unsigned >>>> short to half, but that doesn't feel like a large burden. >>>> >>>> I can't speak to the expected behavior of the UNKNOWN in Python. I >>>> haven't used that path in Python or C++. >>>> >>>> HP >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> On Thu, Feb 18, 2016 at 1:23 PM, Larry Gritz <[email protected]> wrote: >>>> >>>>> I don't have especially strong feelings about this one way or the >>>>> other. >>>>> >>>>> Just returning a raw data byte array matches the C++ behavior more >>>>> closely, no argument there. >>>>> >>>>> On the "con" side, perhaps I was thinking of compatibility? We're >>>>> really talking about changing the meaning of oiio.UNKNOWN from "use >>>>> spec.format" to "return raw data", which differ in the case of mixed >>>>> channel types. >>>>> >>>>> Are there Python programs out there that pass UNKNOWN (or pass >>>>> nothing, defaulting to UNKNOWN) and rely on getting the right kind of >>>>> array >>>>> back that matches spec.format? >>>>> >>>>> >>>>> >>>>> >>>>> On Feb 18, 2016, at 12:58 PM, Andrew Gartner <[email protected]> >>>>> wrote: >>>>> >>>>> "Second, I could collapse 2a and 2b, and just say that if you ask for >>>>> UNKNOWN, you get an array of uint8 back with the native raw data" >>>>> >>>>> Just out of curiosity, what are the drawbacks to doing this? I admit I >>>>> like having some way of getting at the raw data at any time (hence my >>>>> original method of exposing the native calls). That allowed me to check my >>>>> imagespec and regardless of whether I had a mixed format image or all half >>>>> data I could get everything in one read call. Granted I'm used to keeping >>>>> track of and manipulating the strides of those arrays in bytes just out of >>>>> old habit (and C++ usage) so maybe I'm the minority opinion. >>>>> >>>>> Even so, your current thinking still works if that's where the >>>>> consensus is I'm happy to use it as such. >>>>> >>>>> Thanks again >>>>> >>>>> ~Andrew >>>>> >>>>> On Thu, Feb 18, 2016 at 12:21 PM, Larry Gritz <[email protected]> >>>>> wrote: >>>>> >>>>>> I think that the only format that we can encounter as pixel data, >>>>>> which does not exist in Python arrays, is 'half'. >>>>>> >>>>>> So let me rephrase my current thinking: >>>>>> >>>>>> 1. If you ask for a specific type (except HALF), you'll get a Python >>>>>> array of that type holding the converted values. >>>>>> >>>>>> 2. Otherwise (i.e., you ask for UNKNOWN or HALF), you will get the >>>>>> native (raw) data. >>>>>> (a) If all channels are the same data type and it's anything but >>>>>> half, you'll get the data as a Python array of that type. >>>>>> (b) Otherwise (half, or mixed channel types), you'll get the data as >>>>>> a Python array of unsigned bytes. >>>>>> >>>>>> Note that (1) is the easy case to deal with: ask for the type you >>>>>> want, let it do the conversion. If you go for option (2) by asking for >>>>>> native data, you get a blob and it's up to you to figure out what to do >>>>>> with it. >>>>>> >>>>>> There are two other choices we could make. I'm not inclined to at the >>>>>> moment, but would be happy to do so if people think it's helpful. First, >>>>>> if >>>>>> you ask for HALF, I could have it return float. Second, I could collapse >>>>>> 2a >>>>>> and 2b, and just say that if you ask for UNKNOWN, you get an array of >>>>>> uint8 >>>>>> back with the native raw data, even if it happened to be all channels of >>>>>> the same type, a type that you could have made into a Python array of the >>>>>> right type. >>>>>> >>>>>> >>>>>> On Feb 17, 2016, at 11:16 PM, Haarm-Pieter Duiker < >>>>>> [email protected]> wrote: >>>>>> >>>>>> Picking this up a little later in the day. Sorry about that. Adding >>>>>> quotes from earlier in the thread just so it's clear what I'm responding >>>>>> to. >>>>>> >>>>>> The current status: >>>>>> " >>>>>> If you read_image(oiio.FLOAT) of a half image (on disk), you get >>>>>> floats back? >>>>>> " >>>>>> Yes. >>>>>> >>>>>> " >>>>>> But if you read_image(oiio.HALF) of a half image, you get what >>>>>> appears to be an array of floats, but they are actually packed half >>>>>> values? >>>>>> " >>>>>> Yes. >>>>>> >>>>>> The proposal: >>>>>> " >>>>>> 1. If you ask for a (non-UNKNOWN) format that exists in Python, it >>>>>> converts to and returns an array of that format. >>>>>> " >>>>>> This is the current behavior, no? >>>>>> >>>>>> " >>>>>> 2. If you ask for UNKNOWN, or a format that doesn't exist, it returns >>>>>> the raw data in an unsigned char array. >>>>>> " >>>>>> It feels like this is two proposals (Trying not to clash with your >>>>>> earlier 2a and 2b): >>>>>> 2c. If you ask for UNKNOWN, return raw data in an unsigned char array >>>>>> 2d. If you ask for a format that doesn't exist, return raw data in an >>>>>> unsigned char array >>>>>> >>>>>> 2c. feels right. It should work for the case of typical RGB or RGBA >>>>>> images but also for multi-layer EXRs. The consumer can convert the >>>>>> channels >>>>>> to their intended types using methods from the ImageSpec. I'd suggest >>>>>> that >>>>>> asking for UNKNOWN lead unequivocally to a raw unsigned char array. >>>>>> Supporting the special cases described in the 2a and 2b listed earlier >>>>>> would require additional logic on the consuming code side to account for >>>>>> those cases. Feels like a recipe for lots of brittle special case logic. >>>>>> >>>>>> 2d. is less clear. How is the change in behavior from returning real >>>>>> values for known types to returning raw char array data for unknown types >>>>>> signaled to the consumer? Is this still something that programmers have >>>>>> to >>>>>> just know a priori? How is this different from the current behavior? >>>>>> >>>>>> I suppose the list of types known to OIIO but not Python is finite >>>>>> and likely to shrink over time. Having special cases like we have in that >>>>>> example code, isn't such a big deal in the mean time, but then that's >>>>>> just >>>>>> saying the the current behavior is fine. >>>>>> >>>>>> Hope that's helpful in some way. Aside from agreeing that adding an >>>>>> UNKNOWN option is a good idea, we're still left without a good way to >>>>>> consume half data without accounting for it explicitly. >>>>>> >>>>>> HP >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> On Wed, Feb 17, 2016 at 4:35 PM, Andrew Gartner < >>>>>> [email protected]> wrote: >>>>>> >>>>>>> That would certainly take care of things for me. Hopefully not too >>>>>>> much of an impact on others as well. >>>>>>> >>>>>>> ~Andrew >>>>>>> >>>>>>> >>>>>>> >>>>>>> On Wed, Feb 17, 2016 at 4:20 PM, Larry Gritz <[email protected]> >>>>>>> wrote: >>>>>>> >>>>>>>> So I'm proposing: >>>>>>>> >>>>>>>> 1. If you ask for a (non-UNKNOWN) format that exists in Python, it >>>>>>>> converts to and returns an array of that format. >>>>>>>> >>>>>>>> 2. If you ask for UNKNOWN, or a format that doesn't exist, it >>>>>>>> returns the raw data in an unsigned char array. >>>>>>>> >>>>>>>> >>>>>>>> There is a variation: >>>>>>>> >>>>>>>> 2a. If you ask for UNKNOWN, and all channels are the same format >>>>>>>> and it's a type that exists in Python, return that type. >>>>>>>> 2b. If you ask for UNKNOWN and it's a "mixed type" file, or a >>>>>>>> single type but one that doesn't exist in Python, or the type you ask >>>>>>>> for >>>>>>>> doesn't exist in Python, return raw data packed into an unsigned char >>>>>>>> array. >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> On Feb 17, 2016, at 4:10 PM, Andrew Gartner < >>>>>>>> [email protected]> wrote: >>>>>>>> >>>>>>>> Yea the C++ implementation works well with oiio.UNKNOWN, I kinda >>>>>>>> miss that in the python side to be honest. Right now it looks like >>>>>>>> things >>>>>>>> revert back to spec.format if oiio.UNKNOWN is supplied to >>>>>>>> read_scanlines, >>>>>>>> that can be problematic if you have multiple formats in a single image >>>>>>>> so >>>>>>>> I've avoided it. >>>>>>>> >>>>>>>> @Larry, to you question about returning an unsigned char array, I >>>>>>>> like the idea on principle in that it preserves the decoupling as you >>>>>>>> said. >>>>>>>> I'm wondering if there would be any weirdness if you had to grab >>>>>>>> multiple >>>>>>>> channels of an image that had different data types one of which isn't >>>>>>>> representable in python? Would it default to just unsigned char yet >>>>>>>> again >>>>>>>> in that case? >>>>>>>> >>>>>>>> @Haarm: interesting, I didn't realize they were concatenated/packed >>>>>>>> like that! I just saw the 'f' in the python array and assumed I was >>>>>>>> seeing >>>>>>>> promoted values :) I'm still scratching my head over the multiple >>>>>>>> format >>>>>>>> reads though, same as for Larry's idea. >>>>>>>> >>>>>>>> Thanks for the replies, Cheers, >>>>>>>> >>>>>>>> ~Andrew >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> On Wed, Feb 17, 2016 at 3:49 PM, Haarm-Pieter Duiker < >>>>>>>> [email protected]> wrote: >>>>>>>> >>>>>>>>> If you're up for using numpy, this will get you the half float >>>>>>>>> values without too much extra work: >>>>>>>>> oiioFloats = inputImage.read_image(oiio.HALF) >>>>>>>>> oiioHalfs = np.frombuffer(np.getbuffer(np.float32(oiioFloats)), >>>>>>>>> dtype=np.float16) >>>>>>>>> >>>>>>>>> One note, the current OIIO Python implementation doesn't promote >>>>>>>>> the halfs to float on read. The 'float' values in the returned buffer >>>>>>>>> are >>>>>>>>> actually each two concatenated half values, and the float buffer will >>>>>>>>> have >>>>>>>>> half as many entries as you would expect. >>>>>>>>> >>>>>>>>> Example usage for reading here: >>>>>>>>> >>>>>>>>> https://github.com/hpd/CLF/blob/master/python/aces/filterImageWithCLF.py#L126 >>>>>>>>> and the reverse for writing: >>>>>>>>> >>>>>>>>> https://github.com/hpd/CLF/blob/master/python/aces/filterImageWithCLF.py#L193 >>>>>>>>> >>>>>>>>> HP >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> On Wed, Feb 17, 2016 at 3:25 PM, Larry Gritz <[email protected]> >>>>>>>>> wrote: >>>>>>>>> >>>>>>>>>> In C++, you can just call read_scanlines and pass format=UNKNOWN >>>>>>>>>> to get back the raw data in its original format. >>>>>>>>>> >>>>>>>>>> The problem is that in Python, there is no 'half' so it's not >>>>>>>>>> quite sure what to return. >>>>>>>>>> >>>>>>>>>> I kinda like the decoupling of the raw reads (read_native_*) >>>>>>>>>> which are the part overloaded by the individual format readers, from >>>>>>>>>> the >>>>>>>>>> app-callable read_*. So perhaps rather than exposing read_native_*, >>>>>>>>>> we >>>>>>>>>> should just modify the Python bindings for read_* to notice that if >>>>>>>>>> the >>>>>>>>>> native raw data is not a type representable in Python, to return it >>>>>>>>>> as an >>>>>>>>>> unsigned character array? >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> > On Feb 17, 2016, at 2:55 PM, Andrew Gartner < >>>>>>>>>> [email protected]> wrote: >>>>>>>>>> > >>>>>>>>>> > Hey all, >>>>>>>>>> > >>>>>>>>>> > Apologies if this has come up before, but I'm curious if anyone >>>>>>>>>> had considered exposing ImageInput.read_native_scanlines() on the >>>>>>>>>> python >>>>>>>>>> side before. The reason I ask is mainly because the half datatype >>>>>>>>>> doesn't >>>>>>>>>> exist in the native python array class which OIIO uses for python >>>>>>>>>> reads. >>>>>>>>>> Currently the python array will punt and for anything to float >>>>>>>>>> (which I'd >>>>>>>>>> rather avoid). >>>>>>>>>> > >>>>>>>>>> > I had put together an implementation in OIIO 1.5 that simply >>>>>>>>>> took the pixel size as a parameter and exposed read_native_scanlines >>>>>>>>>> that >>>>>>>>>> way and that allowed me to get the right data properly into either >>>>>>>>>> numpy or >>>>>>>>>> a raw char python array. However, I'd rather not be forked off like >>>>>>>>>> that as >>>>>>>>>> it's a headache trying to remain current with the mainline, plus >>>>>>>>>> others may >>>>>>>>>> find it useful. >>>>>>>>>> > >>>>>>>>>> > Does anyone think exposing the function in general makes sense? >>>>>>>>>> I'm happy to send the implementation if anyone cares to see it as >>>>>>>>>> well. >>>>>>>>>> > >>>>>>>>>> > Cheers, >>>>>>>>>> > >>>>>>>>>> > ~Andrew >>>>>>>>>> > >>>>>>>>>> >>>>>>>>>> -- >>>>>>>>>> Larry Gritz >>>>>>>>>> [email protected] >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> _______________________________________________ >>>>>>>>>> Oiio-dev mailing list >>>>>>>>>> [email protected] >>>>>>>>>> http://lists.openimageio.org/listinfo.cgi/oiio-dev-openimageio.org >>>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> _______________________________________________ >>>>>>>>> Oiio-dev mailing list >>>>>>>>> [email protected] >>>>>>>>> http://lists.openimageio.org/listinfo.cgi/oiio-dev-openimageio.org >>>>>>>>> >>>>>>>>> >>>>>>>> _______________________________________________ >>>>>>>> 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 >>>>>>>> >>>>>>>> >>>>>>> >>>>>>> _______________________________________________ >>>>>>> Oiio-dev mailing list >>>>>>> [email protected] >>>>>>> http://lists.openimageio.org/listinfo.cgi/oiio-dev-openimageio.org >>>>>>> >>>>>>> >>>>>> _______________________________________________ >>>>>> 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 >>>>>> >>>>>> >>>>> _______________________________________________ >>>>> 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 >>>>> >>>>> >>>> _______________________________________________ >>>> 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 >>>> >>>> >>> _______________________________________________ >>> 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 >>> >>> >> >> _______________________________________________ >> Oiio-dev mailing list >> [email protected] >> http://lists.openimageio.org/listinfo.cgi/oiio-dev-openimageio.org >> >> > _______________________________________________ > 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 > >
_______________________________________________ Oiio-dev mailing list [email protected] http://lists.openimageio.org/listinfo.cgi/oiio-dev-openimageio.org
