Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Though no pixel has transparency after -flatten, an alpha channel is added and saved in PNG #6985

Open
BlackXStar42 opened this issue Dec 29, 2023 · 21 comments

Comments

@BlackXStar42
Copy link

ImageMagick version

7.1.1-24

Operating system

Windows

Operating system, version and so on

7, 6.1.7601

Description

I am using -flatten to composite a partially transparent image with the background color. I expect the image to contain no transparency anymore afterwards. Though there actually is no pixel left anymore which has any transparency, the operation still adds an alpha channel, which is even stored in a PNG afterwards. I am confused as this is a counter-intuitive behavior. I am also confused as I have indeed observed IM to optimize the colors when saving an image as a PNG, potentially removing the alpha channel if no transparency is used. Sometimes it doesn't seem to do that and I don't know why. So how exactly does the optimization work, when does it remove an alpha channel and when does it not? Also, I am wondering if -flatten adding an unused alpha channel is the intended behavior. If so, I would like to know why so and which option I could use to ask IM to remove an unused alpha channel. I know I could use -alpha Remove but this will always remove the alpha channel and I would rather like to ask IM if it can be removed.

Another strange behavior I observed is that even when I use -alpha Remove after -flatten, I first have to set the background color to a solid color though -flatten just used a solid background color. Yet another strange behavior I observed is that setting the background color just before -alpha Remove only works when I use a color name or rgb(...) but not if I use rgba(..., 1.0).

Steps to Reproduce

magick convert Cross.png -flatten HasAlphaChannelButNoTransparency.png
magick convert Cross.png -flatten -alpha Remove StillHasAlphaChannelButNoTransparency.png
magick convert Cross.png -flatten -background rgba(0,0,0,1) -alpha Remove AndEvenStillHasAlphaChannelButNoTransparency.png
magick convert Cross.png -flatten -background black -alpha Remove FinallyNoAlphaChannel.png

Images

Cross

@fmw42
Copy link

fmw42 commented Dec 29, 2023

Add -alpha off after -flatten.

magick cross.png -background white -flatten -alpha off x.png

No alpha channel

Channel statistics:
    Pixels: 12000000
    Red:
      min: 0  (0)
      max: 255 (1)
      mean: 253.944 (0.995859)
      standard deviation: 16.2331 (0.0636592)
      kurtosis: 237.206
      skewness: -15.441
      entropy: 0.00633329
    Green:
      min: 0  (0)
      max: 255 (1)
      mean: 252.538 (0.990345)
      standard deviation: 24.8857 (0.0975908)
      kurtosis: 98.6572
      skewness: -10.029
      entropy: 0.0113726
    Blue:
      min: 0  (0)
      max: 255 (1)
      mean: 252.538 (0.990345)
      standard deviation: 24.8857 (0.0975908)
      kurtosis: 98.6572
      skewness: -10.029
      entropy: 0.0113726
  Image statistics:
    Overall:
      min: 0  (0)
      max: 255 (1)
      mean: 253.007 (0.992183)
      standard deviation: 22.0015 (0.0862803)
      kurtosis: 123.079
      skewness: -11.1771
      entropy: 0.00969282

On Imagemagick 7, use magick, not magick convert.

@BlackXStar42
Copy link
Author

Thank you for your quick response. I still wonder:

  1. Why do I even have to do that? -flattening with a solid color should be smart enough not only to turn off but even remove the alpha channel on its own, especially if the bottom-most layer is already solid and does not contain any alpha channel to begin with.
  2. Why does -alpha Remove not work after -flattening unless I provide it with a new background color?
  3. Why does providing a new background color for -alpha Remove not work if I use rgba(...,1)?
  4. Why does IM seemingly only sometimes optimize the colors and remove an unused alpha channel before saving?
  5. How can I ask IM to remove an alpha channel only if it is unused? You see, I don't like the thought that I will always have to remember why exactly I added an explicit and unconditional removal of the alpha channel in my scripts, and I also don't want to have to annotate each and every spot neither 🫤

@fmw42
Copy link

fmw42 commented Dec 29, 2023

1) Needs response from IM developers
2) In my opinion, no background specified should work like -background white, since the default background color is white.
3) In my opinion -alpha remove should work like -alpha off.  But specifying rgba(...,1) means to keep an opaque alpha channel and is not the same as rgb(...)
4) In what functions?
5)What do you mean by "unused". Who is to say whether it might be needed or desired by others.

@fmw42
Copy link

fmw42 commented Dec 29, 2023

From the documentation for -alpha remove at https://imagemagick.org/Usage/masking/#alpha_remove

Note that while transparency is 'removed' the alpha channel will remain turned on, but will now be fully-opaque. If you no longer need the alpha channel you can then use Alpha Off to disable it.

@snibgo
Copy link

snibgo commented Dec 29, 2023

  1. Why do I even have to do that? -flattening with a solid color should be smart enough not only to turn off but even remove the alpha channel on its own, especially if the bottom-most layer is already solid and does not contain any alpha channel to begin with.

This is true when that -compose is Over, but not for all other compose methods. If the current -background setting is opaque, and -compose is Over, then the result of -layers flatten should be fully opaque. Perhaps some other things also need to be true. But if you know the result cannot logically have transparency, you can include -alpha off.

IM does have an annoying habit of adding a redundant opaque alpha channel, in some circumstances. And many operations by default include the alpha channel, which can give unexpected results.

When IM processes images, I would like it to not add an alpha channel if it is fully opaque. However, the rules for when this might occur are complex. A simple solution would be to add a check for opacity after every operation. But that would be expensive; it might need all pixels to be examined.

When IM writes images, I would like it to remove the alpha channel if it is fully opaque, by default. But this would be a new feature, and might break scripts.

Another possibility: add a new option to IM: `-alpha off-if-opaque'. If there is no alpha channel, or it has any transparency, this does nothing. If it has a fully opaque alpha channel, it is turned off.

@urban-warrior
Copy link
Member

Ensure that certain algorithms, in accordance with the SVG standard, necessitate the presence of an alpha channel for tasks like compositing. If the alpha channel is absent, we automatically incorporate it. The decision of whether to retain it or not is left to the user as part of their workflow. If opting to exclude it, append -alpha off to your command line. Our design philosophy operates under the premise that users are best equipped to make decisions about their preferences, rather than ImageMagick making decisions on their behalf.

@BlackXStar42
Copy link
Author

I am sorry for my quite lengthy reply but I do like to think about each detail I am given.

@fmw42 (how can I quote on GutHub keeping the reference to the original author 🤔)

[...]
2) In my opinion, no background specified should work like -background white, since the default background color is white.

Exactly, but no background specified does yield a different result than specifying white.

3) In my opinion -alpha remove should work like -alpha off.  But specifying rgba(...,1) means to keep an opaque alpha channel and is not the same as rgb(...)

I wasn't aware of that. So, in addition to potentially offing the alpha channel, the main difference between opaque and remove is that the latter will use the background color instead of the color information of each pixel? Shouldn't it be named background then? Oh no, wait, we already have an option with that name, which does something completely different, again. 😵‍💫 (I'm sorry, I know this is polemic but I couldn't get a grip on myself.)
Also, the documentation implies that RGB(...) has the same meaning as RGBA(...,1). "These examples all specify the same color [...]" is the exact wording. No mentioning that adding an explicit alpha value of 1 will change the behavior of other operations.

4) In what functions?

Whenever the number of colors is small enough so a palette can be used when saving, for example. I can see that this is probably supposed to be a pure storage optimization but nevertheless it is removing the alpha channel. It may be quite unexpected that a composed image sometimes has no alpha channel after saving and sometimes it will, because the number of colors of a composed image may not be easily predictable. An image that happens to have 250 colors will not have an alpha channel stored, an image that happens to have 260 colors will have.

5)What do you mean by "unused". Who is to say whether it might be needed or desired by others.

By "unused", I mean an alpha channel that was added for technical reasons within an operation but has all its values set to 100% after the operation.

@snibgo

[...]
This is true when that -compose is Over, but not for all other compose methods. If the current -background setting is opaque, and -compose is Over, then the result of -layers flatten should be fully opaque. Perhaps some other things also need to be true. But if you know the result cannot logically have transparency, you can include -alpha off.

As you pointed out, offing the alpha channel will only be correct as long as the background color is solid. In a script, it may not always be apparent that when changing the background color to something else, I will have to remove -alpha off somewhere else in the command line to avoid a wrong output.

[...]
When IM processes images, I would like it to not add an alpha channel if it is fully opaque. However, the rules for when this might occur are complex.

I agree and in my opinion it is very bothersome for a human having to remember all the complexity and nasty details around a functional behavior. I always thought the paramount purpose of a software was to make life easier, not the opposite. 😔

A simple solution would be to add a check for opacity after every operation. But that would be expensive; it might need all pixels to be examined.

I tend to disagree for two reasons. 1) You don't need to add a check after every operation, because transparency does not come out of no where. Each operation already knows (or at least should know) if it can introduce or eliminate transparency. For instance, resizing will not affect the presence of transparency, extending with a transparent background will always introduce transparency, and in my case, flattening with a solid color will always eliminate transparency. 2) It would only be necessary when saving and, as it seems, IM already does such a check when storing an image (only it is incomplete so it isn't effective in my case).

When IM writes images, I would like it to remove the alpha channel if it is fully opaque, by default. But this would be a new feature, and might break scripts.

I tend to disagree here, too. Categorical adherence to a once implemented behavior facilitates programming-by-exception and functional singularities. It would be better to enhance existing behavior with an option to go back to the previous behavior, for instance by specifying a compatibility level in the command line of IM or an environment variable. If a user of IM insists on a behavior to not change at all, he or she could always add this switch already the moment he or she creates the script. This is for instance the way it is implemented in some DBMS. Also, I feel like this breaking-change rule is treated arbitrarily. For instance, some time in the past, IM added palette optimization to ICO files, turning truecolor images into 256-color images, which is a total different thing in terms of ICO files, absolutely introducing a breaking change. Additionally, the whole argument about breaking changes ignores the fixing of actual bugs, which may inevitably change the behavior of an operation.

Another possibility: add a new option to IM: `-alpha off-if-opaque'. If there is no alpha channel, or it has any transparency, this does nothing. If it has a fully opaque alpha channel, it is turned off.

This is what I had in mind, too.

@urban-warrior

Ensure that certain algorithms, in accordance with the SVG standard, necessitate the presence of an alpha channel for tasks like compositing. If the alpha channel is absent, we automatically incorporate it. The decision of whether to retain it or not is left to the user as part of their workflow. If opting to exclude it, append -alpha off to your command line.

In for a penny, in for a pound. If a car switches on the headlights automatically for some reason or another (may it be for legal ones or just because the ambient light turned low, I don't care), I would expect it to switch them off again as soon as the necessity is gone.

Our design philosophy operates under the premise that users are best equipped to make decisions about their preferences, rather than ImageMagick making decisions on their behalf.

IM makes a decision on the user's behalf when it is asked to rotate an image only if it is landscape. And IM makes a decision on the user's behalf to off the alpha channel when it is asked to blend the image transparency with a background color through -alpha remove if it is a solid color. And IM even makes a decision on the user's behalf to not off the alpha channel in this case if the user explicitly stated an alpha value of 1 in the color specification.

So, in essence I would still like to understand:

  1. Why is -alpha remove not working after -flatten unless I explicitly repeat the same -background color right in front of it?
  2. As I can't find anything about it in the docs, is it an intentional behavior that RGB(x,y,z) makes a difference over RGBA(x,y,z,1) in some operations and, if so, in which ones?
  3. When exactly does IM do palette optimization and remove the alpha channel when saving an image to a PNG.

I would be deeply grateful if a developer could elaborate on that.

Also, is there any chance we could get an off-if-opaque option for -alpha or, even better, a new setting for all alpha-compositing operations, including -flatten, -layers flatten, and -alpha remove (assuming RGBA(...,1) is indeed supposed to have a different meaning on it than RGB(...)), which will off the alpha channel automatically after its operation in case there is no transparency left? Although I believe this should not even be necessary because every single operation should know if it introduces, eliminates or doesn't affect the presence of transparency, I could live with this solution for the sake of avoiding a breaking change.

@snibgo
Copy link

snibgo commented Dec 30, 2023

On sRGBA(10%,20%,1), see https://imagemagick.org/script/command-line-options.php#alpha:

Remove: Composite the image over the background color.

When the background colour has an alpha channel, such as sRGBA(10%,20%,1) the result will have an alpha channel. To me, that seems like reasonable behaviour.

Each operation already knows (or at least should know) if it can introduce or eliminate transparency.

Most operations can introduce or eliminate transparency. It would be cool if each operation knew whether it had actually introduced or eliminated transparency. But that would be complex and bug-prone and (worse) create a maintenance headache.

For instance, resizing will not affect the presence of transparency, ...

Not strictly true. If the input has transparency, resizing it may remove all the transparency. I don't think resizing can ever add transparency to an opaque image, but I could be wrong. Yes, I am being picky here, because the internal code of IM needs to be picky.

In a script, it may not always be apparent that when changing the background color to something else, I will have to remove -alpha off somewhere else in the command line to avoid a wrong output.

I agree. In principle, the script could save the image, then examine whether the result has an alpha channel that is entirely opaque. If it does, it reads the file, -alpha off, and saves it. Then it continues as required. But that would be horrible. In my opinion, -alpha off-if-opaque is a simple solution that does not add complexity to IM, and is easily understandable to users, and can't break existing scripts.

EDIT: I realise this isn't @ BlackXStar42's ideal solution, which I summarise as: "If IM automatically adds an alpha channel which turns out to be fully opaque, IM should automatically remove the alpha channel." That would be easier for users of IM, but greatly more difficult to implement.

@urban-warrior
Copy link
Member

We added support for the -alpha remove-opaque option to remove the alpha channel if and only if the entire alpha canvas is opaque.

@snibgo
Copy link

snibgo commented Dec 31, 2023

Thank you.

@fmw42
Copy link

fmw42 commented Dec 31, 2023

I do not see this new define listed in the change log. It would be nice to have it there so one knows when it was released. Please add any new features to the change log in the future.

Or is this only in the beta for the next release?

@snibgo
Copy link

snibgo commented Dec 31, 2023

From the name remove-opaque we might expect it to act like -alpha remove if and only if the alpha channel is opaque. But it doesn't. Looking at the source code, it acts like -alpha off if and only if the alpha channel is opaque.

This is fine with me. I prefer it to act like -alpha off. But the name is confusing.

@urban-warrior
Copy link
Member

The change log is only updated during the release process. For beta releases, you need to check the git main commits for any changes, see this commit for example.

@snibgo , we had an AI pick the name for us. We said we need an option name that removes the alpha channel only if its opaque. It suggested remove-opaque-alpha. This is Beta source and the option argument can readily be changed. If you and @fmw42 agree on a better option name, let us know and we will update the patch.

@fmw42
Copy link

fmw42 commented Dec 31, 2023

Does it not add computations to check if every pixel is opaque? If so, I don't think this is a good idea and would leave it to the user to use -alpha off. What is the difference in using the -alpha remove-opaque vs -alpha off? The user needs to type extra in either case. Please correct me if I am wrong.

@snibgo
Copy link

snibgo commented Dec 31, 2023

What is the difference in using the -alpha remove-opaque vs -alpha off?

-alpha off will unconditionally turn the alpha off, so no pixels will have any transparency. The new operation will turn the alpha off only if the image is fully opaque. If any pixels have transparency, nothing will change, and pixels will retain transparency.

@fmw42
Copy link

fmw42 commented Dec 31, 2023

Thanks for the correction. But still does not not take extra computations to check if every pixel is opaque or does that add negligibly to the processing?

@snibgo
Copy link

snibgo commented Dec 31, 2023

Yes, it has to loop through pixels, to check for any transparency.

@snibgo
Copy link

snibgo commented Dec 31, 2023

... we had an AI pick the name for us.

The AI doesn't know the difference between -alpha off and -alpha remove. The new operation is a conditional off, not a conditional remove.

@urban-warrior
Copy link
Member

We're confused, aren't we conditionally removing the alpha channel if its opaque. Give it some thought and provide the final option name and we'll push a patch.

@fmw42
Copy link

fmw42 commented Dec 31, 2023

In my opinion -alpha remove-opaque is not a good name. One is not removing opacity. One is removing alpha. -alpha off-if-opaque sounds more correct. Or -alpha remove-if-opaque if that is more proper.

Other possibilities would be to use a short equivalent name to remove such as

-alpha discard or -alpha detach or -alpha eject -alpha eliminate or -alpha purge

though these do not actually say what is happening.

So prehaps -alpha off-if-opaque or -alpha remove-if-opaque is best depending upon whether the operation is off or remove.

@urban-warrior
Copy link
Member

-alpha off-if-opaque it is then. We added the patch to the Github main branch.

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

No branches or pull requests

4 participants