我试图在画布上为黑白图像的黑色像素着色。
我正在使用的天真代码是:
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/
关于如何有效地为高质量版本着色的任何想法?
感谢。
答案 0 :(得分:2)
您应该使用HSL色彩空间来着色图像。这样您就可以处理边缘情况,例如这样,抗锯齿像素也可以根据亮度值正确着色。
所需的主要步骤是:
示例代码,包含执行这些步骤所需的一切 - 根据需要采用:
转换为灰度:
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/
向下滚动以查看清晰的彩色版本。