Skip to content

[Bug]: Background of rotated png is rendered black #29300

Closed

Description

@maederan201

Bug summary

If I rotate an image, the background of its bounding box is rendered black when rendering as .pdf or .png

Code for reproduction

import matplotlib.pyplot as plt
from matplotlib.transforms import Affine2D
import numpy as np

img = np.random.random((100, 20, 3))

tr_rotate = Affine2D().rotate_deg_around(0 / 2, 0 / 2, 45)
tr_translate = Affine2D().translate(250, 250)

fig, ax = plt.subplots()

ax.imshow(
    img,
    transform=tr_rotate + ax.transData,
    clip_on=False,
)

ax.imshow(
    img,
    transform=tr_translate + ax.transData,
    clip_on=False,
)

ax.set_xlim(-250, 500)
ax.set_ylim(-250, 500)

fig.savefig("MWE.png")

Actual outcome

Image

Expected outcome

Transparent Bounding Box

Additional information

In the past this was possible, however I don't know which version of matplotlib was used...

Operating system

KDE Neon

Matplotlib Version

3.9.2

Matplotlib Backend

tkagg

Python version

3.12.3

Jupyter version

No response

Installation

pip

Activity

  1. maederan201 commented on Dec 13, 2024

    @maederan201
    Author

    I should have done some more due diligence, the problem is resolved if interpolation='none' is passed to imshow.

    So is this expected behavior of the interpolation or still a bug that should be fixed?

  2. tacaswell commented on Dec 13, 2024

    @tacaswell
    Member

    I think we should understand how why this is happening before we decide if it is a bug or a feature ;)

  3. Shivangbijalwan commented on Dec 17, 2024

    @Shivangbijalwan

    you can change the colour of the background convert the mirror icon to RGBA

    import matplotlib.pyplot as plt
    from matplotlib.transforms import Affine2D
    from PIL import Image
    
    # Load the image and convert to RGBA to handle transparency
    mirror_icon = Image.open("mirror.png").convert("RGBA")
    
    # Define transformations
    tr_rotate = Affine2D().rotate_deg_around(0, 0, 45)  # Rotate around (0,0)
    tr_translate = Affine2D().translate(250, 250)  # Translate by (250, 250)
    
    fig, ax = plt.subplots()
    
    # Display the rotated image with proper alpha handling
    ax.imshow(
        mirror_icon,
        transform=tr_rotate + ax.transData,
        clip_on=False,
        alpha=1.0  # Ensures transparency is respected
    )
    
    # Display the translated image
    ax.imshow(
        mirror_icon,
        transform=tr_translate + ax.transData,
        clip_on=False,
        alpha=1.0
    )
    
    # Set limits for clarity
    ax.set_xlim(-1000, 1000)
    ax.set_ylim(-1000, 1000)
    
    # Remove axis for a clean output
    ax.axis('off')
    
    # Save to file
    fig.savefig("MWE.pdf", transparent=True)  # Save with transparency
    plt.show()
    

    (Edit: code formatted)

  4. maederan201 commented on Dec 22, 2024

    @maederan201
    Author

    @Shivangbijalwan This works as well. I also tested it with a different .png image, which has some transparent sections already, and there this happens. I think this is also dependent on the image type, however this is sort of beyond my capabilities.
    Too me it still seems like unwanted behaviour, but the workaround(s) here are easy enough to solve the issue that it might be ok...

  5. tacaswell commented on Dec 23, 2024

    @tacaswell
    Member

    What version of pillow do you have installed?

  6. tacaswell commented on Dec 23, 2024

    @tacaswell
    Member

    @Shivangbijalwan Can you explain a bit why changing the loading image to RGBA works?

  7. Shivangbijalwan commented on Dec 23, 2024

    @Shivangbijalwan

    @tacaswell Changing the loding image to RGBA works .....................
    When you load an image using Pillow (Image.open()), its format depends on the file type, such as P (palette-based) or LA (luminance-alpha) for PNGs with transparency, or RGB for JPEGs without transparency. These formats might not explicitly include an alpha channel, which represents transparency, leading to issues when matplotlib tries to render or transform the image. If the alpha channel isn’t properly recognized, transparent regions can appear as black, especially during transformations like rotation. By converting the image to RGBA using convert("RGBA"), you ensure each pixel includes an explicit transparency value, which matplotlib can then interpret correctly. This standardization prevents the loss or misinterpretation of transparency, allowing matplotlib to render the image properly, even after transformations or during output saving, such as exporting to a PDF.

  8. Shivangbijalwan commented on Dec 23, 2024

    @Shivangbijalwan

    @maederan201 You're absolutely correct that the behavior seems tied to the image type and how matplotlib handles transparency for certain formats. When working with PNG images that already include transparency, Pillow and matplotlib can sometimes misinterpret the transparency information due to differences in how formats like P (palette-based) or LA (luminance-alpha) store data. This can lead to unexpected results, such as transparent regions being displayed as black.

    While it does feel like unwanted behavior—likely a quirk in how matplotlib processes image transformations—the convert("RGBA") workaround provides a reliable solution. By explicitly standardizing the image format to RGBA, you remove ambiguities and ensure consistent handling of transparency, even with complex images. It's not ideal that this step is needed, but as you've pointed out, the fix is simple and effective enough to mitigate the issue without significant overhead.

  9. tacaswell commented on Dec 23, 2024

    @tacaswell
    Member

    @Shivangbijalwan Once we have a numpy array the details of loading from the file should not matter. Presumably we should be able to up-convert to add the alpha channel when we do the rotation. Do you want to try opening a PR to do that?


    I also want to remind people to please not just copy-paste output from LLMs into github.

    https://matplotlib.org/devdocs/devel/contribute.html#restrictions-on-generative-ai-usage

  10. Shivangbijalwan commented on Dec 23, 2024

    @Shivangbijalwan

    @tacaswell "Thanks for the suggestion, but I’m not able to open a PR for this at the moment. However, I’ll keep it in mind for future improvements."

  11. dstansby commented on Mar 21, 2025

    @dstansby
    Member

    What does your mirror_icon numpy array look like? I've just tried your example, but replacing mirror_icon with an RGBA array, and I can't reproduce the black background.

  12. added 5 commits that reference this issue on Mar 27, 2025
    9f85279
    5c157c2
    cafd084
    cea719b
    7bf4731
  13. rcomer commented on Apr 9, 2025

    @rcomer
    Member

    I updated the OP with a self-contained example.

  14. rcomer commented on Apr 9, 2025

    @rcomer
    Member

    Actually this was fixed by #29776. But perhaps it is worth adding a test for this case?

    Image

  15. added a commit that references this issue on Apr 11, 2025
    b0fc12c
  16. socram-somel commented on Apr 12, 2025

    @socram-somel

    Actually this was fixed by #29776. But perhaps it is worth adding a test for this case?

    Image

    @rcomer added a test for this case in PR #29887

  17. anntzer commented on Jul 24, 2025

    @anntzer
    Contributor

    I think this is already tested (somewhat subtly) by the downsampling_specle test change in #29776 (see note at the end of #29776 (comment)).

  18. added this to the v3.11.0 milestone on Jul 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions