了解如何在不使用jQuery的情况下实现JavaScript灰度算法

3
今天我正在寻找一种将彩色图像转换为灰度图像的算法,结果并不需要长时间搜索,我找到了以下文章。 3种将Web图像转换为灰度的方法 本文包含以下代码片段。
var imgObj = document.getElementById('js-image');

function gray(imgObj) {
    var canvas = document.createElement('canvas');
    var canvasContext = canvas.getContext('2d');

    var imgW = imgObj.width;
    var imgH = imgObj.height;
    canvas.width = imgW;
    canvas.height = imgH;

    canvasContext.drawImage(imgObj, 0, 0);
    var imgPixels = canvasContext.getImageData(0, 0, imgW, imgH);

    for (var y = 0; y < imgPixels.height; y++) {
        for (var x = 0; x < imgPixels.width; x++) {
            var i = (y * 4) * imgPixels.width + x * 4;
            var avg = (imgPixels.data[i] + imgPixels.data[i + 1] + imgPixels.data[i + 2]) / 3;
            imgPixels.data[i] = avg;
            imgPixels.data[i + 1] = avg;
            imgPixels.data[i + 2] = avg;
        }
    }
    canvasContext.putImageData(imgPixels, 0, 0, 0, 0, imgPixels.width, imgPixels.height);
    return canvas.toDataURL();
}

imgObj.src = gray(imgObj);

在本文中,http://www.ajaxblender.com/howto-convert-image-to-grayscale-using-javascript.html 的评论中建议替换以下内容:
for (var y = 0; y < imgPixels.height; y++) {
    for (var x = 0; x < imgPixels.width; x++) {
        var i = (y * 4) * imgPixels.width + x * 4;
        var avg = (imgPixels.data[i] + imgPixels.data[i + 1] + imgPixels.data[i + 2]) / 3;
        imgPixels.data[i] = avg;
        imgPixels.data[i + 1] = avg;
        imgPixels.data[i + 2] = avg;
    }
}

使用:

for (var i = 0; i < imgPixels.data.length; i = i + 4) {
    var avg = (imgPixels.data[i] + imgPixels.data[i + 1] + imgPixels.data[i + 2]) / 3;
    imgPixels.data[i] = avg;
    imgPixels.data[i + 1] = avg;
    imgPixels.data[i + 2] = avg;
}

JSFiddle演示 https://jsfiddle.net/n6q9a2c9/

我对这个算法的理解有一些问题。

在这段代码片段中:

for (var i = 0; i < imgPixels.data.length; i = i + 4) {
    var avg = (imgPixels.data[i] + imgPixels.data[i + 1] + imgPixels.data[i + 2]) / 3;
    imgPixels.data[i] = avg;
    imgPixels.data[i + 1] = avg;
    imgPixels.data[i + 2] = avg;
}

我知道var avg = (imgPixels.data[i] + imgPixels.data[i + 1] + imgPixels.data[i + 2]) / 3是公式(R + G + B) / 3,下面是让我感到困惑的部分

for (var i = 0; i < imgPixels.data.length; i = i + 4) {

为什么这里我们要增加4?

如果能解释一下这个算法的工作原理,我将不胜感激。

1个回答

2
Canvas数据以RGBA(红色,绿色,蓝色和称为“alpha”的透明度值)交错格式提供给您。每个通道需要一个字节的像素,您有四个通道,因此每个像素有四个值。由于通道是交错的,因此每隔4个增加1个以遍历像素。
参考文献:

1
哦,我想我明白了。 500 * 333 = 166500,由于每个像素有4个通道(RGBA),所以乘以166500 * 4 = 666000 - Mikhail
@Mikhail 像素数据以一维数组的形式提供。他们不是使用单个for循环来迭代该数组,而是在xy上进行迭代,然后将其转换为像素数据数组的索引i。你所问的那行代码执行了从x,y到索引的转换。如果示例使用公式var i = (y * imgPixels.width + x) * 4;,可能会更易理解。请记住,索引通常从零开始,直到数组大小之前结束。根据原始公式:332 * 4 * 500 + 499 * 4 = 665996 - Ouroborus
谢谢。但是我注意到,在单个for循环的示例中,我们跳过了alpha通道(因为我们每次增加4),就像这样http://pastebin.com/3wz1nX2A,在两个`for`循环的示例中,算法不会这样做(不会增加4)。 - Mikhail
也许我的计算有误...我得到了以下结果http://pastebin.com/ba670aqX 算法跳过了2000个索引? - Mikhail
@Mikhail 看起来很接近了。这种困惑是我之前说过的原因之一,即使用公式 i = (y * imgPixels.width + x) * 4 比例子中使用的 i = (y * 4) * imgPixels.width + x * 4 更容易理解。它们做的事情是相同的,只是我的版本在数学上更清楚地说明了正在发生的事情。 - Ouroborus
显示剩余13条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接