【问题标题】:Mapping 16 bit grayscale to 8 bit in PNG在 PNG 中将 16 位灰度映射到 8 位
【发布时间】:2019-08-27 19:39:03
【问题描述】:

我正在尝试在 C# 中使用每通道 16 位 (bpc) 对灰度图像解码 PNG。我相信我正在解码正确的原始值,但是当我转换为 8bpc 以返回值时,我计算的值与 System.Drawing、Gimp 或 SixLabors.ImageSharp 不同。

给定单个灰度像素的以下字节值:

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

我尝试了以下映射:

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

但这些方法似乎都没有与其他软件预测的以下值相匹配:

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

我确定我误解了正常化回 8 bpc 值的正确方法。

有问题的图片是这样的:

【问题讨论】:

  • 这是一个很好的问题,但您应该添加是否使用 Forms 或 WPF 以及如何解码 PNG,可能有一些基础设施您可以利用而不是重新发明轮子。
  • 据我所见,您在乘以 255 之前先除以 ushort.MaxValue,这会丢弃您稍后需要的低位,因为除法将是整数。在第一个映射中首先乘以255
  • @Aybe 这实际上只是普通的 C#,我正在尝试编写一个 PNG 解码器,只是为了看看它是如何完成的,所以我想看看轮子是如何构建的。我目前拥有的解码逻辑在这里:github.com/EliotJones/BigGustave/blob/master/src/BigGustave/…
  • @NetMage 谢谢,我也试过这种方式,它适用于大多数情况,但看起来 (55, 186) 情况仍然失败,但我在 Gimp 和 ImageSharp 中检查了这一点其中将值视为 56,因此看起来 System.Drawing 中的 GDI+ 在映射回时对值的舍入做了一些奇怪的事情。
  • 您的帖子没有说明在这种情况下出了什么问题 - 您显示的是预期值,但不是您从 result 获得的错误值?还是result2?

标签: c# png


【解决方案1】:

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

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

There might gamma being involved.

结论:

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

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

【讨论】:

    猜你喜欢
    • 2012-02-07
    • 1970-01-01
    • 2017-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-19
    • 1970-01-01
    相关资源
    最近更新 更多