Skip to content

Incorrect alpha compositing using "matplotlib.pyplot.imshow". #9906

Closed

Description

@thomasmansencal

Bug report

Bug summary

matplotlib.pyplot.imshow definition alpha compositing is incorrect, references #3343.

Code for reproduction

from matplotlib import pyplot as plt

plt.figure(figsize=(8, 8))
RGBA = plt.imread('RGB_Droplet.png')
plt.imshow(RGBA)
plt.show()

Actual outcome

image

Expected outcome

Composited in The Foundry Nuke:

image

The images used are available here:

Matplotlib version

  • Operating system: macOS
  • Matplotlib version: '2.1.0'
  • Matplotlib backend (print(matplotlib.get_backend())): 'module://ipykernel.pylab.backend_inline'
  • Python version: '2.7.13 |Continuum Analytics, Inc.| (default, Dec 20 2016, 23:05:08) \n[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)]'
  • Jupyter version (if applicable): notebook 5.0.0 py27_0

Activity

  1. anntzer commented on Dec 1, 2017

    @anntzer
    Contributor

    Possibly related to #8847?

  2. added this to the milestone on Dec 1, 2017
  3. QuLogic commented on Dec 5, 2017

    @QuLogic
    Member

    Here's how it looks in the Drive preview (albeit with checkerboard background):
    screenshot-2017-12-5 rgb_droplet png
    and here's how it looks in eog with white background:
    screenshot from 2017-12-05 01-04-22
    or in gimp with a white layer below it:
    screenshot from 2017-12-05 01-09-33

    So I'm not sure this is a bug per se. It is possible that one is using premultiplied alpha and the other is not (when the incoming image is the opposite.)

  4. thomasmansencal commented on Dec 5, 2017

    @thomasmansencal
    ContributorAuthor

    My image has alpha premultiplied, so I guess what Matplotlib doing is multiplying again RGB by alpha, this should probably be an optional behavior.

  5. thomasmansencal commented on Dec 5, 2017

    @thomasmansencal
    ContributorAuthor

    Seems to be it:

    image

    Using:

    RGB_Droplet_UnPremultiplied.png

  6. efiring commented on Dec 7, 2017

    @efiring
    Member

    It's not clear to me whether there is a proposed change for mpl here. If not, this should be closed. If there is something that needs to be done, perhaps it should be stated at the top of a new issue, with a reference to this one, and then close this one.

  7. thomasmansencal commented on Dec 7, 2017

    @thomasmansencal
    ContributorAuthor

    I don't know if it is at image read time or display time, but MPL should offer an option for different behaviour whether the RGB channel has alpha pre-multiplied or not. Currently the RGB channel is getting multiplied potentially two times, so this is an issue.

    • If the RGB channels are already pre-multiplied by alpha, you don't multiply.
    • If the RGB channels are not pre-multiplied by alpha, you multiple by alpha.
  8. efiring commented on Dec 7, 2017

    @efiring
    Member

    I think the option would have to be at image read time; internally, mpl has to consistently use one form or the other, and presently it is using straight RGBA. It seems like the underlying problem here is that RGBA is not well-defined. Therefore imread could gain a kwarg, defaulting to premultiplied=False; when set to True it would cause the input RGB values to be divided by the alpha value. Is this correct? If so, would you like to submit a PR for this?

  9. jklymak commented on Dec 7, 2017

    @jklymak
    Member

    PNG spec says not to premultiply alpha, so I don't think we are doing anything incorrect by reading it that way: https://www.w3.org/TR/PNG-Rationale.html If the user wants to divide by alpha after reading, they are welcome to do so.

  10. thomasmansencal commented on Dec 7, 2017

    @thomasmansencal
    ContributorAuthor

    Just to be clear, I have the same issue if I pass an image generated directly by code to imshow.

    I could not find anywhere in MPL imshow documentation a statement that image must be either premultiplied or un-premultiplied, this should be clarified.

  11. jklymak commented on Dec 7, 2017

    @jklymak
    Member

    I'm not following your first statement. Do you have an example?

    I think the documentation is pretty clear. If you want to specify RGB, you pass RGB, if you want to specify RGBA, you pass RGBA. I'm not clear why you would you pass RGBA to imshow if you've already applied the alpha to the RGB data. What would imshow do with the A channel?

  12. thomasmansencal commented on Dec 7, 2017

    @thomasmansencal
    ContributorAuthor

    The question is not whether you pass alpha or not, but whether your RGB has been premultiplied with Alpha or not. The capability of doing A over B through an alpha is totally independent of the state of RGB.

  13. thomasmansencal commented on Dec 7, 2017

    @thomasmansencal
    ContributorAuthor

    Defining the following variables:

    • Mask: the black and white droplet alpha / mask
    • A: the RGB droplet, already premultiplied by Mask
    • B: the grid background

    On the left, A, is composited over B, using Mask as an alpha in the Merge1 (Over) node.
    On the right, A is divided by Mask in the Merge3 (Divide) node and is composited over B, using Mask as an alpha in the Merge2 (Over) node.

    As you can see A over B is possible in both cases, the problem is that depending whether your RGB values have been premultiplied, you have to account for it, and thus at the very least, indicate clearly in the document what MPL expect, which seems to be un-premultiplied RGB data to be sent to imshow.

    image

  14. jklymak commented on Dec 7, 2017

    @jklymak
    Member

    Matplotlib imshow has no concept of premultiplied. It takes an MxNx3 RGB array of floats and displays those values. Matplotlib doesn't care how those values were produced. If you pass it an MxNx4 RGBA array it displays those RGB values with an alpha given by A. The image is displayed with the alpha mixed with whatever background exists on the figure behind the image.

    The image you supplied has already had A applied against black background. Passing A again, as you are doing above, masks the black background, but of course still allows some of the side data to leak in and look greyish.

    I really don't think this is a bug, and I really think the documentation is clear.

    That there is a community out there that pre-multiplies alpha into their images and then still supplies that alpha in the PNG isn't really our business, but in my opinion, they are going against the spec and missing the point of having an alpha channel.

    fig

    from matplotlib import pyplot as plt
    import numpy as np
    
    fig, axs = plt.subplots(1, 2,)
    RGBA = plt.imread('RGB_Droplet.png')
    axs[0].imshow(RGBA)
    axs[1].imshow(RGBA[:,:,:-1])
    plt.show()
  15. thomasmansencal commented on Dec 7, 2017

    @thomasmansencal
    ContributorAuthor

    PNG is probably not the good file format to discuss about since the spec seems to advocate straight / un-premultiplied alpha but a lot of other formats don't specify it.

    I really think the documentation is clear.

    I would argue the opposite considering:

    1. The fact people have asked on Stackoverflow: https://stackoverflow.com/questions/25089068/how-does-imshow-handle-the-alpha-channel-with-an-m-x-n-x-4-input
    2. There is a related issue opened here: Issues with imshow and RGBA values #3343
    3. The very issue we are discussing on: Incorrect alpha compositing using "matplotlib.pyplot.imshow". #9906

    I don't think a mention to straight un-premultiplied and premultiplied alpha compositing, matplotlib behavior in that regard along a possible reference to https://en.wikipedia.org/wiki/Alpha_compositing would be bad in the imshow documentation.

    I have scratched my head a few hours trying to understand what is happening and I'm not alone.

  16. jklymak commented on Dec 7, 2017

    @jklymak
    Member

    #3343 and the S/O question are the same thing and both were fixed by #5718. If you run the code in #5718 you get the expected result (all white). The state of affairs before #5718 was indeed incorrect and a bug.

    Please provide a minimal self-contained example that does not require opening an image that displays the problem you think still exists.

  17. thomasmansencal commented on Dec 7, 2017

    @thomasmansencal
    ContributorAuthor

    As of this writing I'm leaning on the side that the documentation is scarce in term of describing what is the alpha compositing process in MPL not so much on there is an issue. With the myriad of image manipulation packages, image file formats and way of writing those images, I think MPL should describe its alpha compositing process. As a matter of fact, imshow documentation does not describe how it performs its over operation, it is a black box and one would have to dive into the code to understand it.

  18. jklymak commented on Dec 7, 2017

    @jklymak
    Member

    If alpha is not documented anywhere, then that is indeed an oversight.

    99% sure that its just: RGB_back is the background and RGB_fore is the foreground, its RGB = RBG_back * (1 - alpha) + RGB_fore * alpha. The zorder sets what is the foreground.

  19. thomasmansencal commented on Dec 7, 2017

    @thomasmansencal
    ContributorAuthor

    Thanks @jklymak!

  20. jklymak commented on Dec 7, 2017

    @jklymak
    Member

    No problem - I know next to nothing about "formal" image processing, so I only have the "basic" intuition about what alpha could mean. Fortunately for me, I think thats the same as how Matplotlib thinks about it. You've obviously used it in different contexts, so are approaching it from a different point of view. Its definitely helpful to get different points of view so we can make the docs as universal as possible.

    I'd suggest https://matplotlib.org/tutorials/colors/colors.html#sphx-glr-tutorials-colors-colors-py as a decent place to put the appropriate definitions, though your suggestion as to where else it should go would be appreciated. However, bear in mind that Matplotlib uses alpha for most objects, so we aren't going to explain it for every function call!

    Thanks!

  21. anntzer commented on Jan 11, 2018

    @anntzer
    Contributor

    Closed by #9959 and #9981, afaict.

  22. modified the milestones: , v2.2.0 on Feb 12, 2018
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

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions