HTML画布:平滑地着色黑白图像

时间:2014-12-05 17:32:40

标签: javascript html css html5 canvas

我试图在画布上为黑白图像的黑色像素着色。

我正在使用的天真代码是:

function color_text(canvas, r, g, b, w, h) {
    var ctx = canvas.getContext('2d');
    var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    var pixels = imageData.data;
    for (var x = 0; x < w; x++) {
        for (var y = 0; y < h; y++) {
            var redIndex = ((y - 1) * (canvas.width * 4)) + ((x - 1) * 4);
            var greenIndex = redIndex + 1;
            var blueIndex = redIndex + 2;
            var alphaIndex = redIndex + 3;
            if ((pixels[redIndex] < 240) && (pixels[greenIndex] < 240) && (pixels[blueIndex] < 240)) {
                pixels[redIndex] = r;
                pixels[greenIndex] = g;
                pixels[blueIndex] = b;
            }
        }
    }
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.putImageData(imageData, 0, 0);
}

我使用&lt; 240作为非白色像素的检测器而不是正好255,因为这些是用手绘书法扫描的,因此需要一个软糖因子。将此算法应用于由画布的原生drawImage()缩小的图像,可生成与黑白图像一样好的结果。

但是,canvas的原生drawImage()还有很多不足之处,因此我使用this answer中提供的稍微修改过的代码缩小了图像。这段代码生成的黑白图像非常漂亮,比canvas的原生方法要好得多。但是,当我使用上述功能为图像着色时,它看起来很糟糕。

这里有一个完整的jsfiddle:http://jsfiddle.net/q9sd9w1k/

关于如何有效地为高质量版本着色的任何想法?

感谢。

2 个答案:

答案 0 :(得分:2)

您应该使用HSL色彩空间来着色图像。这样您就可以处理边缘情况,例如这样,抗锯齿像素也可以根据亮度值正确着色。

所需的主要步骤是:

  • 创建图像的灰度版本
  • 决定您要使用哪种颜色(在HSL中这将是[0,360]度 - 您也可以将想要使用的颜色从RGB转换为HSL。)
  • 使用Hue更新第二个缓冲区,其中使用从HSL转换的RGB,相同的饱和度和第一个缓冲区中的灰度值作为亮度。

示例代码,包含执行这些步骤所需的一切 - 根据需要采用:

转换为灰度:

var lumas = new Float32Array(width * height),
    idata = ctx.getImageData(0, 0, width, height),
    data = idata.data,
    len = data.length,
    i = 0,
    cnt = 0;

for(; i < len; i += 4)
    lumas[cnt++] = (data[i] * 0.2126 + 
                    data[i+1] * 0.7152 + 
                    data[i+2] * 0.0722) / 255; //normalized value

您需要一个hsl2rgb函数:

function hsl2rgb(h, s, l) {

    var r, g, b, q, p;

    h /= 360;

    if (s === 0) {
        r = g = b = l;

    }
    else {
        function hue2rgb(p, q, t) {
            t %= 1;
            if (t < 0.1666667) return p + (q - p) * t * 6;
            if (t < 0.5) return q;
            if (t < 0.6666667) return p + (q - p) * (0.6666667 - t) * 6;
            return p;
        }

        q = l < 0.5 ? l * (1 + s) : l + s - l * s;
        p = 2 * l - q;

        r = hue2rgb(p, q, h + 0.3333333);
        g = hue2rgb(p, q, h);
        b = hue2rgb(p, q, h - 0.3333333);
    }

    return {
        r: (r * 255 + 0.5) | 0,
        g: (g * 255 + 0.5) | 0,
        b: (b * 255 + 0.5) | 0
    }
}

然后遍历亮度缓冲区,将值传递为l,将生成的alpha设置为255的rgb组件放入画布的缓冲区中:

var idata = ctx.createImageData(0, 0, width, height),
    buffer = idata.data,
    len = buffer.length,
    hue = 90
    sat = 0.5,
    i = 0,
    cnt = 0;

for(; i < len; i += 4) {

    var color = hsl2rgb(h, s, lumas[cnt++]); // HSL to RGB

    buffer[i  ] = color.r;
    buffer[i+1] = color.g;
    buffer[i+2] = color.b;
    buffer[i+3] = 255;
}

ctx.putImageData(idata, 0, 0);

答案 1 :(得分:0)

John-Paul Gignac提供以下答案,效果很好:

对于每个像素,请执行以下操作:

c1_r = f1_r + (b1_r-f1_r)*c0_r/255
c1_g = f1_g + (b1_g-f1_g)*c0_g/255
c1_b = f1_b + (b1_b-f1_b)*c0_b/255

其中c1_r是像素的新红色值,c0_r是其旧值,b1_r是新背景颜色的红色值,f1_r是新前景的红色值。这里的假设是原始背景和前景分别是白色和黑色。

为了与原版比较,新的着色功能是:

function color_text_gignac(canvas, r, g, b, w, h) {
    var ctx = canvas.getContext('2d');
    var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    var pixels = imageData.data;
    for (var x = 0; x < w; x++) {
        for (var y = 0; y < h; y++) {
            var redIndex = ((y - 1) * (canvas.width * 4)) + ((x - 1) * 4);
            var greenIndex = redIndex + 1;
            var blueIndex = redIndex + 2;
            var alphaIndex = redIndex + 3;
            pixels[redIndex] = r + (255 - r) * pixels[redIndex] / 255;
            pixels[greenIndex] = g + (255 - g) * pixels[greenIndex] / 255;
            pixels[blueIndex] = b + (255 - b) * pixels[blueIndex] / 255;
        }
    }
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.putImageData(imageData, 0, 0);
}

以下是一个完整的工作示例:http://jsfiddle.net/moq9pd7h/2/

向下滚动以查看清晰的彩色版本。