gpt4 book ai didi

html - source-over alpha 混合效果不佳(HTML5 Canvas )

转载 作者:太空狗 更新时间:2023-10-29 14:16:50 26 4
gpt4 key购买 nike

编辑:我不一定需要这个问题的解决方案——相反,我想了解为什么它会发生。我不明白为什么我应该在下面得到奇怪的结果......

虽然这个问题针对的是我在使用 HTML5 canvas 应用程序时遇到的问题,但我认为这个问题不太具体。

我有一个 HTML5 Canvas 应用程序,可让您在屏幕上标记图像。这些图像是 32 位 PNG,所以我使用透明度。如果我在同一位置多次标记高度透明的图像(大约 100 次),我最终会得到一个非常糟糕的结果:

http://i.imgur.com/qYY5n.png

我用作图章的图像颜色是 RGB(167, 22, 22) 而我要盖章的背景是 RGB(255, 255 , 255)。这是源图像,如果有人感兴趣的话:

http://i.stack.imgur.com/Wm9Dx.png

如您所知,图像的 alpha 水平极低。可能大约 2/255 到 5/255 左右。我期望会发生的是,如果您将图像标记重复应用到 Canvas 上的次数足够多,您将获得颜色为 RGBA(167, 22, 22, 255) 。不幸的是,我得到了混合颜色,包括一些非常奇怪的灰色区域,其值为 RGB(155, 155, 155)

我刚刚加载了 Excel 并插入了 source-over alpha 混合的等式 (Wikipedia reference),经过足够的迭代后我似乎收敛到 RGB(167, 22, 22)。我可能遗漏了一些关于 alpha 混合操作以及 HTML5 canvas 如何实现源代码合成的基本知识……谁能帮我理清思路?

谢谢!

注意:this question与我的问题类似,但我不太明白为什么我会得到我在此处发布的结果。

最佳答案

canvas 数学内部的精度和舍入规则大多是未定义的,因此很难确切地说出这里发生了什么。我们真正知道的是像素是无符号字节,并且 alpha 是预乘的。

但是,我们可以通过使用 getImageData 在绘制图章时检查像素来获取一些信息,如下所示:

var px = 75;
var py = 100;
var stamp = new Image;
stamp.onload = function() {
for (var i = 0; i < 100; ++i) {
imageData = context.getImageData(px, py, 1, 1);
console.log(Array.prototype.slice.call(imageData.data, 0, 4));
context.drawImage(stamp, 0, 0);
}
};
stamp.src = 'stamp.png';

px = 75py = 100 处的样本正好位于灰色 Blob 的中间。在白色 Canvas 上绘制一次图章后,日志显示为:

[254, 254, 254, 255]

px = 120py = 150 处,样本位于红色区域的中间。绘制一次图章后,日志显示为:

[254, 253, 253, 255]

因此,对于灰色像素,看起来 Canvas 被修改为 (-1, -1, -1),对于红色像素,修改为 (-1, -2, -2)。

使用 RMagick 对图章图像中的这些相同像素进行采样给出:

[167, 22, 22, 1]  // x = 75, y = 100
[167, 22, 22, 2] // x = 120, y = 150

通过数学计算,使用标准的 alpha 混合方程,您可以测试每个颜色值:

function blend(dst, src) {
var a = src[3] / 255.0
return [
(1.0 - a) * dst[0] + a * src[0],
(1.0 - a) * dst[1] + a * src[1],
(1.0 - a) * dst[2] + a * src[2]
];
}

console.log(blend([255, 255, 255], [167, 22, 22, 1]));
// output: [254.6549..., 254.0862..., 254.0862...]

console.log(blend([255, 255, 255], [167, 22, 22, 2]));
// output: [254.3098..., 253.1725..., 253.1725...]

据此,我们可以猜测 Canvas 混合代码实际上是在对结果进行取整,而不是四舍五入。这将为您提供 [254, 254, 254][254, 253, 253] 的结果,就像我们在 Canvas 上看到的那样。他们可能根本没有进行任何舍入,并且在转换回无符号字节时隐式地被舍入。

这就是为什么另一篇文章建议将图像数据存储为 float 组,自己进行数学计算,然后用结果更新 Canvas 。这样您可以获得更高的精度,并且可以控制诸如舍入之类的事情。

编辑:事实上,这个 blend() 函数并不完全正确,即使结果是 floored,因为 120 的 Canvas 像素值, 150稳定在[127, 0, 0],这个函数稳定在[167, 22, 22]。同样,当我只将图像绘制到透明 Canvas 中时,120, 150 处像素上的 getImageData[127, 0, 0, 2]。什么?!

原来这是由预乘引起的,预乘似乎应用于加载的Image元素。参见 this jsFiddle举个例子。

预乘像素存储为:

// r, g, b are 0 to 255
// a is 0 to 1
// dst is all 0 to 255
dst.r = Math.floor(r * a);
dst.g = Math.floor(g * a);
dst.b = Math.floor(b * a);
dst.a = a * 255;

它们稍后解压为:

inv = 1.0 / (a / 255);
r = Math.floor(dst.r * inv);
g = Math.floor(dst.g * inv);
b = Math.floor(dst.b * inv);

针对 [167, 22, 22, 2] 运行此打包/解包显示:

a = 2 / 255;                                // 0.00784
inv = 1.0 / (2 / 255); // 127.5
r = Math.floor(Math.floor(167 * a) * inv); // 127
g = Math.floor(Math.floor(22 * a) * inv); // 0
b = Math.floor(Math.floor(22 * a) * inv); // 0

关于html - source-over alpha 混合效果不佳(HTML5 Canvas ),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5360699/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com