如何使用JavaScript将画布图像转为灰度?

10

我有一张28x28像素大小的图片在HTML5画布中。我使用以下代码将画布的imageData作为RGBA(红色,绿色,蓝色,透明度)值的数组获取:

canvas = document.getElementById('canvas');
ctx = canvas.getContext("2d");
imgData = ctx.getImageData(0, 0, 28, 28);

现在我想将图像灰度化,以便获得一个由784个值(28x28像素)组成的数组,其中每个像素只有一个值(而不是四个)。
我找到了很多不同的灰度化公式,有些是乘以RGB值,有些只是计算平均值 - 我真的不知道该使用哪个...
我也卡在获取784个值上了 - 它总是3136(因为有4个通道)...
提前谢谢!

是的,有多个公式可供选择。没有一个单一的真正公式。 - Bergi
好的,谢谢。但是在将每个像素的四个值转换为灰度后,我如何获得一个包含784个值的数组? - Tom
在你的for循环中,只需要有两个计数器 - 一个是i += 1,另一个是每次迭代都加4的j += 4 - Bergi
太好了!使用两个计数器的for循环完美地解决了问题(我不知道为什么我自己没有想到)- 谢谢! - Tom
这并不是 OP 所追求的,因此不是对这个问题主体的回答,但是对于标题来说,请注意 CSS 滤镜已经添加到 Canvas2D API 中,在支持的浏览器中,您可以通过在绘制带有“copy”全局组合操作模式的画布之前将上下文的滤镜设置为“grayscale(100%)”来实现灰度效果。 - Kaiido
2个回答

16

主要思想是让颜色的红绿蓝三个组成部分具有相同的值。为此,您需要计算每个像素的亮度。有几种计算亮度的方法,这是其中之一。

window.onload = function() {
  let canvas = document.getElementById("c");

  let ctx = canvas.getContext("2d");
  canvas.width = 50;
  canvas.height = 50;

  let srcImg = document.getElementById("sof");
  ctx.drawImage(srcImg, 0, 0, ctx.canvas.width, ctx.canvas.height);
  let imgData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
  let pixels = imgData.data;
  for (var i = 0; i < pixels.length; i += 4) {

    let lightness = parseInt((pixels[i] + pixels[i + 1] + pixels[i + 2]) / 3);

    pixels[i] = lightness;
    pixels[i + 1] = lightness;
    pixels[i + 2] = lightness;
  }
  ctx.putImageData(imgData, 0, 0);
}
<canvas id="c"></canvas>
<img src="data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAAZAAD/4QMtaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjMtYzAxMSA2Ni4xNDU2NjEsIDIwMTIvMDIvMDYtMTQ6NTY6MjcgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTJDRTJFNzlFMzkzMTFFODlEQ0FEQ0NCN0JCMjUxRTEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTJDRTJFN0FFMzkzMTFFODlEQ0FEQ0NCN0JCMjUxRTEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDoxMkNFMkU3N0UzOTMxMUU4OURDQURDQ0I3QkIyNTFFMSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDoxMkNFMkU3OEUzOTMxMUU4OURDQURDQ0I3QkIyNTFFMSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pv/uAA5BZG9iZQBkwAAAAAH/2wCEABENDQ0ODRIODhIaEQ8RGh8XEhIXHyIXFxcXFyIjGx4dHR4bIyMpKi0qKSM2Njs7NjZBQUFBQUFBQUFBQUFBQUEBEhERFBYUGBUVGBcTFxMXHRcZGRcdLB0dIB0dLDgoIyMjIyg4MjUtLS01Mj09ODg9PUFBQUFBQUFBQUFBQUFBQf/AABEIADQAMgMBIgACEQEDEQH/xACCAAEAAgMBAAAAAAAAAAAAAAAAAwQBAgUGAQEAAwEAAAAAAAAAAAAAAAAAAgMEARAAAQMCAgYHBwUAAAAAAAAAAQACAxEEIRIxQYHREwXwUWGRoSIVcbHBMkJSY2JyIxQ1EQACAgEEAgMBAAAAAAAAAAAAARECITFRYQMSQkGBE3H/2gAMAwEAAhEDEQA/APcIiIAijnnit4zLK7Kwd9TqAVC/fMwxcwtpOJCweZgPkynXt8FC91VN6xlpaxuTp1uzS0nCb0nY6aKOGaOeJssZq1w2g6wfYpFJNNSvki004eGgiIunAiLUvBa4xkPc0HAGvm6kBRn5haiZ9pdRkR6MzhVrvjtVUsksKzWxFzYSfOyuagPXv71vJfyFmS/sSW66YjZXeqrDbcSthcPglcQOFICWuJ1VFfFYr3lzKb9fS64h6m6lIUQ0vZT50fMrNS3y0PZOf638ljKMxJwMbh9J/V7wuso4WRRNEMeUFuJa2gxOk0Ui1ddPCsffC/hl7b+dp+uXy+QiIplZkaVzrQS8G64FBLmOSujN2roa1S9OFSRM8VNcMNKPKOpw5IcvPfuj8NyR23MzPG+dsLmtcC4hozAdhopvT/zv6bU9P/O/ptVX4rE27HG9i39nmKdanapmL/Rm/buVxVoLQQSGTiF5IpirKtKQiIgCIiAIiIAiIgCIiA//2Q=="
  id="sof" />

更新:
替代的lightness计算方法:

  • 维基百科 (Luma):
    let lightness = parseInt(pixels[i]*.299 + pixels[i + 1]*.587 + pixels[i + 2]*.114);

  • 其他来源(未知):
    let lightness = parseInt(3*pixels[i] + 4*pixels[i + 1] + pixels[i + 2] >>> 3);

  • 维基百科 (线性亮度):
    let lightness = 0.2126 * pixels[i] + 0.715 * pixels[i+1] + 0.0722 * pixels[i+2];


11
你可以直接为渲染上下文设置滤镜。这比计算每个像素的颜色更简单。
为此,您应该使用 ctx.filter = 'grayscale(1)';

const img = document.querySelector('img');
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');

img.onload = function() {
  canvas.width = img.width;
  canvas.height = img.height;
  ctx.filter = 'grayscale(1)';
  ctx.drawImage(img, 0, 0, img.width, img.height);
}
<canvas></canvas>
<img src="data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAAZAAD/4QMtaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjMtYzAxMSA2Ni4xNDU2NjEsIDIwMTIvMDIvMDYtMTQ6NTY6MjcgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTJDRTJFNzlFMzkzMTFFODlEQ0FEQ0NCN0JCMjUxRTEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTJDRTJFN0FFMzkzMTFFODlEQ0FEQ0NCN0JCMjUxRTEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDoxMkNFMkU3N0UzOTMxMUU4OURDQURDQ0I3QkIyNTFFMSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDoxMkNFMkU3OEUzOTMxMUU4OURDQURDQ0I3QkIyNTFFMSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pv/uAA5BZG9iZQBkwAAAAAH/2wCEABENDQ0ODRIODhIaEQ8RGh8XEhIXHyIXFxcXFyIjGx4dHR4bIyMpKi0qKSM2Njs7NjZBQUFBQUFBQUFBQUFBQUEBEhERFBYUGBUVGBcTFxMXHRcZGRcdLB0dIB0dLDgoIyMjIyg4MjUtLS01Mj09ODg9PUFBQUFBQUFBQUFBQUFBQf/AABEIADQAMgMBIgACEQEDEQH/xACCAAEAAgMBAAAAAAAAAAAAAAAAAwQBAgUGAQEAAwEAAAAAAAAAAAAAAAAAAgMEARAAAQMCAgYHBwUAAAAAAAAAAQACAxEEIRIxQYHREwXwUWGRoSIVcbHBMkJSY2JyIxQ1EQACAgEEAgMBAAAAAAAAAAAAARECITFRYQMSQkGBE3H/2gAMAwEAAhEDEQA/APcIiIAijnnit4zLK7Kwd9TqAVC/fMwxcwtpOJCweZgPkynXt8FC91VN6xlpaxuTp1uzS0nCb0nY6aKOGaOeJssZq1w2g6wfYpFJNNSvki004eGgiIunAiLUvBa4xkPc0HAGvm6kBRn5haiZ9pdRkR6MzhVrvjtVUsksKzWxFzYSfOyuagPXv71vJfyFmS/sSW66YjZXeqrDbcSthcPglcQOFICWuJ1VFfFYr3lzKb9fS64h6m6lIUQ0vZT50fMrNS3y0PZOf638ljKMxJwMbh9J/V7wuso4WRRNEMeUFuJa2gxOk0Ui1ddPCsffC/hl7b+dp+uXy+QiIplZkaVzrQS8G64FBLmOSujN2roa1S9OFSRM8VNcMNKPKOpw5IcvPfuj8NyR23MzPG+dsLmtcC4hozAdhopvT/zv6bU9P/O/ptVX4rE27HG9i39nmKdanapmL/Rm/buVxVoLQQSGTiF5IpirKtKQiIgCIiAIiIAiIgCIiA//2Q=="
/>


1
不是Safari :( https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/filter - murkle
!#$%& Safari!!! - Bron Davies
已经有一份关于此事的Pull Request https://github.com/WebKit/WebKit/pull/3793,很快可能就会在那里了。 - tzi

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