Skip to content

Parameters for Image.open() #569

Description

@al45tair

There are a few file formats that would benefit from the ability to pass parameters through the Image.open() method. This needs to be thought about carefully because there are clearly some parameters that are generic (e.g. mode, size and dpi arguments, which could cause conversion or resizing, all of which can in some instances be supported directly by the image file plugin), while others are very format specific (e.g. the number of quality layers to load in a JPEG 2000 image).

As of today, some image file plugins are essentially relying on the laziness of PIL’s loading machinery so that the user can call open and subsequently set some properties on the returned object before load gets called. This is, IMO, pretty nasty.

(I might be up for fixing this myself, but I want to file a bug report before doing any coding because I want the opinions of others on how this should work.)

Activity

  1. aclark4life commented on Mar 25, 2014

    @aclark4life
    Member

    I am +1, I think, but @wiredfool will need to comment. Any precedent in any other API for doing this?

  2. added this to the Future milestone on Mar 25, 2014
  3. wiredfool commented on Mar 25, 2014

    @wiredfool
    Member

    I want to look carefully at the code before endorsing an api change. I'm at a tentative +1.

  4. al45tair commented on Mar 26, 2014

    @al45tair
    ContributorAuthor

    @aclark4life The Image.save() API already has arguments; of course, in that case, you specify the format, so it’s obvious what they mean. Contrast with Image.open(), where you don't necessarily know the type of the file you’re loading; in that case, you may still wish to pass arguments that matter to you if the file turns out to be of a particular type, but we don’t want type-specific arguments to clash.

    I was going to propose something like the following:

    im = Image.open(fp, mode='r', size=None, format_args={})
    

    We already have the mode argument; size would be a 2-tuple of pixel dimensions, while format_args is a dictionary mapping format names to format-specific-argument dictionaries. A JPEG 2000 themed example might be:

    im = Image.open(fp, mode='r', format_args={ 'JPEG2000': { 'layers': 4 } })
    

    the idea being that if fp turns out to be a JPEG 2000 file, the arguments in the relevant sub dictionary will apply. If it isn’t a JPEG 2000, it will be loaded without regard to those arguments. An alternative might be to use a flat dictionary with dot notation, e.g.

    im = Image.open(fp, mode='r', format_args={ 'JPEG2000.layers': 4 })
    

    Perhaps slightly easier to read and write?

    Additionally, the size argument should always be passed to the plugin, because JPEG and JPEG 2000 are — in some cases — capable of loading and processing just enough data to obtain an image of the requested size.

  5. modified the milestones: 2.5.0, Future on Apr 1, 2014
  6. aclark4life commented on Apr 1, 2014

    @aclark4life
    Member

    Maybe this can go in 2.5.0 due in 3 months

  7. modified the milestones: Future, 2.5.0 on Jun 1, 2014
  8. homm commented on Sep 15, 2015

    @homm
    Member

    We already have the mode argument

    This is not truth. Mode is not "image mode", this is "file read mode" and it is always "r". First lines of open():

    if mode != "r":
        raise ValueError("bad mode %r" % mode)

    I strongly disagree about passing mode and size in constructor because:

    There should be one-- and preferably only one --obvious way to do it.
    Although that way may not be obvious at first unless you're Dutch.
    

    Currently that way is opening image and calling draft(). This have many advantages: you can open image, check it dimensions, mode, some options and even verify sometimes before loading an image into memory.

    I agree what there should be some way to configure decoder, but format_args={'JPEG2000': {'layers': 4}} looks too ugly. Maybe something like this?

    i = Image.open(image)
    i.draft(mode, size)
    i.configure_decoder(jpeg2000={'layers': 4})
  9. al45tair commented on Sep 15, 2015

    @al45tair
    ContributorAuthor

    I'm not sure I understand your point about the mode argument. Maybe you’re confused and think I was proposing using it for the image mode, but indeed I was not.

    As far as the draft method goes, it looks like you’re right that that’s the way to go here — however, this is not documented particularly well and the documentation could do with an overhaul in this regard. Right now there is code out there that is not using draft but instead pokes about directly in the Image object to set the desired mode and size.

    I like the configure_decoder method also; it has less visual noise than using an explicit dictionary.

  10. radarhere commented on Aug 28, 2018

    @radarhere
    Member

    I created #1349 as a suggestion, but did not progress due to lack of a specific use for the API.

  11. Shivahk027 commented on Jun 9, 2020

    @Shivahk027
    im = Image.open(fp, mode='r', format_args={ 'JPEG2000': { 'layers': 4 } })

    in this what is fp? what is the value for fp? is that mandatory?

  12. radarhere commented on Jun 9, 2020

    @radarhere
    Member

    @Shivahk027 fp is short for 'file pointer'. It is one way of referencing the image that is being opened.

    from PIL import Image
    with open("Tests/images/hopper.png", "rb") as fp:
        im = Image.open(fp, mode='r', format_args={ 'JPEG2000': { 'layers': 4 } })

    Another way is just to use the path of the image.

    from PIL import Image
    im = Image.open("Tests/images/hopper.png", mode='r', format_args={ 'JPEG2000': { 'layers': 4 } })

    For more information, see https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.open

    Note however that format_args is still under discussion here, and has not been implemented.

  13. hugovk commented on Jun 10, 2020

    @hugovk
    Member

    Let's close this issue as it's not really progressed in many years. We can reopen if necessary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions