从 JavaScript 中的图像中读取像素数据会返回半透明像素的意外结果

Reading pixeldata from images in JavaScript returns unexpected results for semi transparent pixels

本文关键字:像素 返回 半透明 意外 结果 数据 图像 读取 像素数 JavaScript      更新时间:2023-09-26

我正在从JavaScript中的png文件中读取RGBA数据。为此,我在画布上绘制图像并使用.getImageData。 数据与我的预期不同:

测试图像:https://raw.githubusercontent.com/FlorianLudwig/imghash/master/test/transparent.png

JS中第三个像素的RGBA是:[9, 0, 0, 54](在chrome和Firefox中测试过),但它应该是[12, 0, 0, 54]的(至少它是gimp和python枕头声称的)。

我的猜测可能出了什么问题

  1. drawImage进行一些合成(将ctx.globalCompositeOperation设置为 copy 没有任何区别)
  2. 加载图像时正在进行一些颜色管理

示例代码

  1. http://codepen.io/anon/pen/EaemZw
  2. 选择测试图像
  3. 检查控制台输出

1) 合成

应用的绘制操作越多,随着时间的推移,舍入误差就越多。但是,对于初始抽奖,这并不重要。

copy模式不会影响您发现的它。这只是忽略现有的后台数据。源结束确实使用背景的 alpha 值,但由于最初没有,因此将具有相同的效果。

不过,这样做的主要问题是,由于需要在内部转换的预乘 alpha 值,存在 alpha 和舍入误差的普遍问题。

2) 色彩管理

浏览器确实支持各种级别的颜色管理。这包括 PNG 文件中的以下数据:

  • sRGB 块,说明 PNG 如何符合 sRGB 标准(相对、绝对等)
  • iCCP 块,其中包含包含 gamma 的 ICC 配置文件,如果浏览器支持 ICC 以及此版本的 ICC(典型版本是版本 2 和 4)。
  • 如果未找到 sRGB 或 iCCP 配置文件,它将查找包含表示 gamma 的数字的 gAMA 块。

应用这些数据时,它将更改原始位图。Gamma 不会影响最低值或最高值,但中间值会有明显的变化。由于颜色管理和转换错误 (1.),仍更改的值将更改。

例如,即使文件没有 gamma 块(或"文件 gamma"),测试文件就是这种情况,或者其值为 1,仍将应用显示 gamma(IE 除外)。对于Windows,这将是2.2,对于Mac通常是1.8。

这发生在图像返回以在带有画布的代码中使用之前。

或者,我建议看看我为此类场景创建的 pngtoy (它是免费的/MIT),以使开发人员能够从 PNG 获取原始的未更改位图(只需查看注释/状态,因为它目前仍处于 alpha 阶段)。我在下面提供了一个代码示例,该示例还读取了第三个像素,如预期的那样为红色通道提供 12。

使用 pngtoy 进行测试

这将显示原始未更改的位图值。

var out = document.querySelector("output"),
    png = new PngToy();
png.fetch("https://i.imgur.com/oX1kom7.png").then(
  function() {png.decode().then(show)}
);
function show(bmp) {
  var data = bmp.bitmap;
  out.innerHTML += data[8] + "," + data[9] + "," + data[10] + "," + data[11];
}
<script src="https://rawgit.com/epistemex/pngtoy/master/tests/Promise.js"></script>
<script src="https://rawgit.com/epistemex/pngtoy/master/pngtoy.min.js"></script>
<output>Loading external scripts (pngtoy.min.js + Promise polyfill for IE...)<br></output>

你的猜测#2是正确的。

规范赋予浏览器自由,可以对传入的图像进行 Alpha 和 Gamma 调整。