Saving 16 bit PNG with alpha premultiplies the alpha while PNG alpha should be unpremultiplied #99186

Open
opened 2022-06-26 16:54:23 +02:00 by Christian F · 28 comments

The PNG specs seems to indicate that PNG files always store unpremultiplied alpha, yet saving PNG images as demonstrated below saves them in a premultiplied form. Is this expected?

Original Report

System Information
Operating system: Windows 10
Graphics card: GTX 980

Blender Version
Blender 3.2 (in 2.92 LTS too, other versions not tested)

I have a very bright red object with motion blur (edit: motion blur is not the cause, see answer below).
The values of the Red channel reach up to 500. Green and Blue are completely 0.
If I save the image as PNG 16 bit with alpha the image gets completely desaturated and broken. Inspecting the image data shows that the Green and Blue channels now have the values of the red channel, which doesn't make any sense...

Saving the image without alpha gives the correct result.

alphabug.blend

Here is also a simpler test file that just uses a gradient with emission and transparency.

alphabug2.blend

Also when importing the saved images I noticed that: 8bit RGBA PNG looks more saturated than 16 bit RGBA PNG. HOWEVER, 16 bit RGBA looks like 8 bit RGBA when I choose channel packed as alpha mode on the 16 bit image in the compositor. So 8 bit straight alpha looks like 16 bit channel packed alpha image...

The PNG specs seems to indicate that PNG files always store unpremultiplied alpha, yet saving PNG images as demonstrated below saves them in a premultiplied form. Is this expected? ### Original Report **System Information** Operating system: Windows 10 Graphics card: GTX 980 **Blender Version** Blender 3.2 (in 2.92 LTS too, other versions not tested) I have a very bright red object with motion blur (edit: motion blur is not the cause, see answer below). The values of the Red channel reach up to 500. Green and Blue are completely 0. If I save the image as PNG 16 bit with alpha the image gets completely desaturated and broken. Inspecting the image data shows that the Green and Blue channels now have the values of the red channel, which doesn't make any sense... Saving the image without alpha gives the correct result. [alphabug.blend](https://archive.blender.org/developer/F13230991/alphabug.blend) Here is also a simpler test file that just uses a gradient with emission and transparency. [alphabug2.blend](https://archive.blender.org/developer/F13233281/alphabug2.blend) Also when importing the saved images I noticed that: 8bit RGBA PNG looks more saturated than 16 bit RGBA PNG. HOWEVER, 16 bit RGBA looks like 8 bit RGBA when I choose channel packed as alpha mode on the 16 bit image in the compositor. So 8 bit straight alpha looks like 16 bit channel packed alpha image...
Author

Added subscriber: @Christian-Fritz

Added subscriber: @Christian-Fritz
Member

Added subscriber: @lichtwerk

Added subscriber: @lichtwerk
Member

Changed status from 'Needs Triage' to: 'Needs User Info'

Changed status from 'Needs Triage' to: 'Needs User Info'
Member

For completeness: can you share an example scene that produces such a render?

For completeness: can you share an example scene that produces such a render?
Author
[alphabug.blend](https://archive.blender.org/developer/F13230991/alphabug.blend)
Member

Changed status from 'Needs User Info' to: 'Needs Triage'

Changed status from 'Needs User Info' to: 'Needs Triage'
Author

Update: testing (importing the saved images in compositor) 8bit RGBA PNG looks more saturated than 16 bit RGBA PNG. Although 8 bit gradient doesnt look exactly like the original render but I suppose it could be due to 8 bit clamping.

HOWEVER: 16 bit RGBA looks like 8 bit RGBA when I choose channel packed as alpha on the 16 bit image in the compositor. So 8 bit straight alpha looks like 16 bit channel packed alpha....
here another testing file that just uses a plane with a gradient.

alphabug2.blend

Update: testing (importing the saved images in compositor) 8bit RGBA PNG looks more saturated than 16 bit RGBA PNG. Although 8 bit gradient doesnt look exactly like the original render but I suppose it could be due to 8 bit clamping. HOWEVER: 16 bit RGBA looks like 8 bit RGBA when I choose channel packed as alpha on the 16 bit image in the compositor. So 8 bit straight alpha looks like 16 bit channel packed alpha.... here another testing file that just uses a plane with a gradient. [alphabug2.blend](https://archive.blender.org/developer/F13233281/alphabug2.blend)
Christian F changed title from PNG 16 bit saving with alpha causes incorrect clamping to PNG 16 bit with alpha has incorrect colorspace conversions with bright values 2022-07-01 18:35:40 +02:00
Member

Added subscriber: @OmarEmaraDev

Added subscriber: @OmarEmaraDev
Member

Changed status from 'Needs Triage' to: 'Needs User Info'

Changed status from 'Needs Triage' to: 'Needs User Info'
Member

Isn't this desaturation just the effect of filmic view transform coupled with the clamping and imprecision of PNG files?
Can you update your original report with the updates you posed in the comments to clarify the report?

Isn't this desaturation just the effect of filmic view transform coupled with the clamping and imprecision of PNG files? Can you update your original report with the updates you posed in the comments to clarify the report?
Author

As you can read in my message, it does not happen when saving without alpha. Furthermore, I have provided simple files that can easily replicate it. Without alpha it is fine. With alpha it is unacceptable. I have provided 2 files, what more information do you need...
I can later update my original post to include the contents of the comment though.

Oh and also, the image data itself is broken, I can see the desaturation in After Effects too and any other programme. It seems to be unrelated to view transform.

As you can read in my message, it does not happen when saving without alpha. Furthermore, I have provided simple files that can easily replicate it. Without alpha it is fine. With alpha it is unacceptable. I have provided 2 files, what more information do you need... I can later update my original post to include the contents of the comment though. Oh and also, the image data itself is broken, I can see the desaturation in After Effects too and any other programme. It seems to be unrelated to view transform.
Member

I don't need more information, you seemed to imply that the second file is more straightforward and demonstrates the problem better, so I was just suggesting that you update the report with only that file to make it clearer.

I am talking about the view transform when saving, not when viewing. If you want to ignore the view transform when saving, you can disable Save As Render in the save dialogue, this should fix the desaturation problem you are talking about. Does that work?

I don't need more information, you seemed to imply that the second file is more straightforward and demonstrates the problem better, so I was just suggesting that you update the report with only that file to make it clearer. I am talking about the view transform when saving, not when viewing. If you want to ignore the view transform when saving, you can disable Save As Render in the save dialogue, this should fix the desaturation problem you are talking about. Does that work?
Author

When deactivating "save as render" it does not desaturate but now the bright red parts which were white in the render result are red again since it doesn't apply the filmic transform.

When deactivating "save as render" it does not desaturate but now the bright red parts which were white in the render result are red again since it doesn't apply the filmic transform.
Author

Changed status from 'Needs User Info' to: 'Needs Triage'

Changed status from 'Needs User Info' to: 'Needs Triage'
Member

Right, but that's because of the PNG clamping, so we can't have it both ways. A better option would be to use a half float EXR image for this.

Right, but that's because of the PNG clamping, so we can't have it both ways. A better option would be to use a half float EXR image for this.
Author

Again, the issue only appears with alpha. Saving the 16-bit image without alpha has no issues. So how come this has to do anything with PNG clamping...
Also, isn't Blender transforming the 32 bit linear render into something I can view on my 8 or 16 bit sRGB monitor? What is the problem to save it then just like it is shown in the image viewer?

Again, the issue only appears with alpha. Saving the 16-bit image without alpha has no issues. So how come this has to do anything with PNG clamping... Also, isn't Blender transforming the 32 bit linear render into something I can view on my 8 or 16 bit sRGB monitor? What is the problem to save it then just like it is shown in the image viewer?
Member

If I save the image as PNG 16 bit with alpha the image gets completely desaturated and broken. Inspecting the image data shows that the Green and Blue channels now have the values of the red channel, which doesn't make any sense...

I though the issue is the fact that the image gets desaturated, which we established is due to the view transform. Otherwise, what exactly is the issue?

The difference between saving RGB or RGBA is probably due to alpha premultiplication, which seems to happen upon saving. Is this what you have issue with?

> If I save the image as PNG 16 bit with alpha the image gets completely desaturated and broken. Inspecting the image data shows that the Green and Blue channels now have the values of the red channel, which doesn't make any sense... I though the issue is the fact that the image gets desaturated, which we established is due to the view transform. Otherwise, what exactly is the issue? The difference between saving RGB or RGBA is probably due to alpha premultiplication, which seems to happen upon saving. Is this what you have issue with?
Member

Changed status from 'Needs Triage' to: 'Needs User Info'

Changed status from 'Needs Triage' to: 'Needs User Info'
Author

I clearly wrote in the post, that it happens when exporting the PNG 16 bit with alpha channel. It does not happen when exporting without alpha.
I don't know if the issue is because of premultiplication...

I clearly wrote in the post, that it happens when exporting the PNG 16 bit with alpha channel. It does not happen when exporting without alpha. I don't know if the issue is because of premultiplication...
Author

okay now let us get this straight.

Color management settings are as follows: Display Device: sRGB, View Transform: Filmic, Look: None, Exposure: 0, Gamma: 1, Sequencer: sRGB.

In the image viewer I can also inspect the colour values after colour management has been applied (the CM: .... part). Now let us inspect:
x: 950 y: 500 has CM: R: 1, G: 0.261, B: 0.261 and Alpha: 0.1495. If I remember the PNG file specs correctly, PNG expects unpremultiplied alpha. Now I export the image as PNG 16 bit RGBA (save as Render activated and using the Scene color management).
Now importing the exported image into Blender compositor. The Node has colorspace sRGB and alpha straight. The RGB (not the CM colour) at x: 950 y: 500 is R: 0.1494, G: 0.13346, B: 0.13346 and Alpha: 0.1495

Maybe this is a premultiplication issue since Red went from 1 to 0.1494 which is almost the alpha value at that point.

okay now let us get this straight. Color management settings are as follows: Display Device: sRGB, View Transform: Filmic, Look: None, Exposure: 0, Gamma: 1, Sequencer: sRGB. In the image viewer I can also inspect the colour values after colour management has been applied (the CM: .... part). Now let us inspect: x: 950 y: 500 has CM: R: 1, G: 0.261, B: 0.261 and Alpha: 0.1495. If I remember the PNG file specs correctly, PNG expects unpremultiplied alpha. Now I export the image as PNG 16 bit RGBA (save as Render activated and using the Scene color management). Now importing the exported image into Blender compositor. The Node has colorspace sRGB and alpha straight. The RGB (not the CM colour) at x: 950 y: 500 is R: 0.1494, G: 0.13346, B: 0.13346 and Alpha: 0.1495 Maybe this is a premultiplication issue since Red went from 1 to 0.1494 which is almost the alpha value at that point.
Member

Changed status from 'Needs User Info' to: 'Needs Developer To Reproduce'

Changed status from 'Needs User Info' to: 'Needs Developer To Reproduce'
Member

You are right in the fact that PNG seems to expect unpremultiplied alpha, yet blender does it anyways. This looks similar to #99337. I will tag the module for more information.

You are right in the fact that PNG seems to expect unpremultiplied alpha, yet blender does it anyways. This looks similar to #99337. I will tag the module for more information.
Omar Emara changed title from PNG 16 bit with alpha has incorrect colorspace conversions with bright values to Saving 16 bit PNG with alpha premultiplies the alpha while PNG alpha should be unpremultiplied 2022-07-06 08:12:05 +02:00
Author

At this point, can't they just give us the option to choose what alpha mode is being saved? Seriously, PNG, EXR etc. do not care what bits I put in them. Those formats are just there to let the parser know how to interpret the data and since most good compositors let you choose how you interpret the alpha, there is literally no point in putting these restrictions on us causing so many issues...

At this point, can't they just give us the option to choose what alpha mode is being saved? Seriously, PNG, EXR etc. do not care what bits I put in them. Those formats are just there to let the parser know how to interpret the data and since most good compositors let you choose how you interpret the alpha, there is literally no point in putting these restrictions on us causing so many issues...

Added subscriber: @NahuelBelich

Added subscriber: @NahuelBelich
Author

Any update on this? I am now in another project and this is completely unusable and unbearable. This is just ridiculous. Images get completely desaturated.

Any update on this? I am now in another project and this is completely unusable and unbearable. This is just ridiculous. Images get completely desaturated.

Apparently I just ran into the same issue during addon development. I'm trying to bake and save 16 bit non-color RGBA images in which each channel contains an individual mask.
TIF and EXR (or just packing the image internally) seem to work perfectly fine, but saving as PNG (no matter if saved via python or saved manually from the image editor) causes alpha to mess up the color channels.

I've attached an example image. Open it in Blender's image editor, set Color Space to Non-Color, Alpha to Channel Packed.
The blank image background will be 0.5 Red and 0.5 Alpha.

Safe it as 16 bit TIF and hit Reload Image. Everything will stay the same.

But if you safe it as 16 bit PNG and hit Reload, you will notice that Red has been bumped up to 1.

If this is some sort of mistake on my part, I'd be glad to hear it. Otherwise I'd be forced to either forbid saving as PNG or allowing only RGB images without alpha.

Edit: tried 3.6.4 and 4.1 Alpha, same issue.

Apparently I just ran into the same issue during addon development. I'm trying to bake and save 16 bit non-color RGBA images in which each channel contains an individual mask. TIF and EXR (or just packing the image internally) seem to work perfectly fine, but saving as PNG (no matter if saved via python or saved manually from the image editor) causes alpha to mess up the color channels. I've attached an example image. Open it in Blender's image editor, set Color Space to Non-Color, Alpha to Channel Packed. The blank image background will be 0.5 Red and 0.5 Alpha. Safe it as 16 bit TIF and hit Reload Image. Everything will stay the same. But if you safe it as 16 bit PNG and hit Reload, you will notice that Red has been bumped up to 1. If this is some sort of mistake on my part, I'd be glad to hear it. Otherwise I'd be forced to either forbid saving as PNG or allowing only RGB images without alpha. Edit: tried 3.6.4 and 4.1 Alpha, same issue.

Hi. I stumbled upon this Blender thread while trying to get libpng to save 16-bpc without premultiplying alpha on an unrelated hobby-project ... thought it might be helpful to post here. The code I was using: https://github.com/bluescan/tacent/blob/master/Modules/Image/Src/tImagePNG.cpp

It exhibits the same issue being described here. If I save the png with bitDepth = 8, all is well. If I save with 16 it multiplies the colours by the alpha. It is my understanding that the only difference between 8 and 16-bpc pngs, other than better precision of course, is supposed to be that 16-bpc has RGB in linear space and 8bpc is sRGB (linear alpha for both) -- none of this premultiplying business. I confirmed this (IMO incorrect) libpng behaviour with a simple image of all-white and half alpha.

Maybe Blender is using libpng as well? I also tried with GIMP 2.1.0 and the png export setting 'save colour values from transparent pixels' didn't seem to work when setting the pixelformat to 16bpc RGBA.

IDK... either there's a problem with libpng or I don't know how to use it properly.

Hi. I stumbled upon this Blender thread while trying to get libpng to save 16-bpc without premultiplying alpha on an unrelated hobby-project ... thought it might be helpful to post here. The code I was using: https://github.com/bluescan/tacent/blob/master/Modules/Image/Src/tImagePNG.cpp It exhibits the same issue being described here. If I save the png with bitDepth = 8, all is well. If I save with 16 it multiplies the colours by the alpha. It is my understanding that the only difference between 8 and 16-bpc pngs, other than better precision of course, is _supposed_ to be that 16-bpc has RGB in linear space and 8bpc is sRGB (linear alpha for both) -- none of this premultiplying business. I confirmed this (IMO incorrect) libpng behaviour with a simple image of all-white and half alpha. Maybe Blender is using libpng as well? I also tried with GIMP 2.1.0 and the png export setting 'save colour values from transparent pixels' didn't seem to work when setting the pixelformat to 16bpc RGBA. IDK... either there's a problem with libpng or I don't know how to use it properly.

FWIW I had it the wrong way around. The 16-bpc saving code with libpng was fine. The loading code when decoding to PNG_FORMAT_LINEAR_RGB_ALPHA (to keep the extra precision) was causing the pre-multiply. When I switched to using a different library ( https://github.com/randy408/libspng ) and loading 16-bpc to SPNG_FMT_RGBA16 it worked as expected.

No idea if this is the issue for you here...but might be helpful.

FWIW I had it the wrong way around. The 16-bpc saving code with libpng was fine. The loading code when decoding to PNG_FORMAT_LINEAR_RGB_ALPHA (to keep the extra precision) was causing the pre-multiply. When I switched to using a different library ( https://github.com/randy408/libspng ) and loading 16-bpc to SPNG_FMT_RGBA16 it worked as expected. No idea if this is the issue for you here...but might be helpful.
Sign in to join this conversation.
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset Browser
Interest
Asset Browser Project Overview
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Collada
Interest
Compatibility
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
EEVEE
Interest
EEVEE & Viewport
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest
Import Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overlay
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Virtual Reality
Interest
Vulkan
Interest
Wayland
Interest
Workbench
Interest: X11
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
EEVEE & Viewport
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Priority
High
Priority
Low
Priority
Normal
Priority
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Milestone
No project
No Assignees
6 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: blender/blender#99186
No description provided.