首页 > 解决方案 > Mapping 16 bit grayscale to 8 bit in PNG

问题描述

I'm trying to decode a PNG in C# with 16 bits per channel (bpc) for a grayscale image. I believe I'm decoding the correct raw values however when I convert to 8bpc for returning values I'm not calculating the same values as System.Drawing, Gimp or SixLabors.ImageSharp.

Given the following byte values of a single grayscale pixel:

byte b0 = data[index];
byte b1 = data[index + 1];

I've tried the following mappings:

byte result = b0;
byte result = (byte)(255 * (((b0 << 8) + b1)/ushort.MaxValue));
byte result = (byte)(((b0 << 8) + b1)/256.0);

But none of these approaches seem to match the values from other software predicted for the following values:

b0   b1   expected
55   186  55
67   135  67
35   241  36

I'm sure I'm misunderstanding the correct method for normalizing back to an 8 bpc value.

The image in question is this:

A 16 by 16 pixel Grayscale image with 16 bpc

标签: c#png

解决方案


这是转换位深度的官方方法:

https://www.w3.org/TR/2003/REC-PNG-20031110/#13Sample-depth-rescaling

output = floor((input * MAXOUTSAMPLE / MAXINSAMPLE) + 0.5)

where

MAXINSAMPLE = (2^sampledepth)-1
MAXOUTSAMPLE = (2^desired_sampledepth)-1

因此,如果我以您的最后一个示例为例:

35 << 8 | 241 == 8960 | 241 == 9201
9201 * 255 / 65535 == 2346255 / 65535 == 35
floor(35 + 0.5) == 35

为什么不36呢?

可能涉及伽玛

结论:

首先检查您的内部实施是否正确遵循规范,然后进行相应调整。

https://www.w3.org/TR/2003/REC-PNG-20031110/


推荐阅读