以灰度保存 canvas

Save canvas in grayscale

我想知道是否可以在 HTML5 中添加彩色 canvas,然后在单击保存按钮时将其另存为黑白(灰度)PNG。我已经设法将 canvas 保存为 PNG,太棒了!但我想添加灰度功能,而不必自己用 Illustrator 等程序更改它。 canvas 有移动的粒子顺便说一下。 (虽然我不确定这是否有任何影响)

谢谢~!

function download(){
    var dt = c.toDataURL('image/png');
    this.href = dt; 
}; 
downloadlink.addEventListener('click', download, false); 

可以通过从 canvas 中提取 RGB 数据并计算它们的亮度值来实现。

不过有几个先决条件需要满足,例如 CORS(在这种情况下这似乎不是问题,因为您已经可以保存图像)以及是否希望保留原始数据保存后,您可以将 canvas 上的当前图像复制到临时图像,或者保留需要提取的像素数据的备份。

RGB 转亮度

根据REC 709(或BT.709)公式将RGB数据转换成亮度值(灰度)的公式如下:

luma = Red x 0.2126 + Green x 0.7152 + Blue x 0.0722

或者您可以使用 REC 601 (BT.601) 公式(这在标清视频素材中更常见):

luma = Red x 0.299 + Green x 0.587 + Blue x 0.114

要使用它,只需遍历所有像素,使用这些公式之一获取亮度值,并将目标中的所有通道替换为生成的亮度值。

临时数据

对于临时数据,我们可以执行以下任一操作(单击保存按钮时):

临时canvas

  • 创建临时 canvas
  • 设置大小等于来源canvas
  • 使用 drawImage()
  • 在源 canvas 中绘制
  • 使用getImageData()
  • 提取目标canvas'像素数据
  • 迭代像素,使用公式转换,放回数据
  • 保存图像然后丢弃临时图像canvas

图像数据备份

  • 使用 getImageData() 提取图像数据 - 这将作为我们的备份
  • 使用 createImageData()
  • 创建相同大小的新 ImageData
  • 迭代备份中的像素,但将结果以亮度形式放入创建的 ImageData
  • 放回创建的ImageData,保存图片
  • 放回备份数据

是否需要后一步实际上取决于您如何更新 canvas。如果您正在制作循环动画,则可能不需要放回备份(或保留一个),如果一切都得到更新,但如果是这样并且您看到灰色闪光或某些区域留下灰色,则需要此步骤。

使用 ImageData 方法的示例

var ctx = c.getContext("2d"), img = new Image;
img.onload = setup;  img.crossOrigin = "";
img.src = "//i.imgur.com/OrYVGI8.jpg";

function setup() {
  c.width = this.naturalWidth;  c.height = this.naturalHeight;
  ctx.drawImage(this, 0, 0);    btn.disabled = false
}

// Main code for demo
btn.onclick = function() {
  
  var idataSrc = ctx.getImageData(0, 0, c.width, c.height), // original
      idataTrg = ctx.createImageData(c.width, c.height),    // empty data
      dataSrc = idataSrc.data,                              // reference the data itself
      dataTrg = idataTrg.data,
      len = dataSrc.length, i = 0, luma;
  
  // convert by iterating over each pixel each representing RGBA
  for(; i < len; i += 4) {
    // calculate luma, here using Rec 709
    luma = dataSrc[i] * 0.2126 + dataSrc[i+1] * 0.7152 + dataSrc[i+2] * 0.0722;

    // update target's RGB using the same luma value for all channels
    dataTrg[i] = dataTrg[i+1] = dataTrg[i+2] = luma;
    dataTrg[i+3] = dataSrc[i+3];                            // copy alpha
  }
  
  // put back luma data so we can save it as image
  ctx.putImageData(idataTrg, 0, 0);
  demo.src = c.toDataURL();                                 // set demo result's src url
  
  // restore backup data
  ctx.putImageData(idataSrc, 0, 0);
};
<button disabled id=btn>SAVE TO GREY</button> (click then scroll down to see result)<br>
<canvas id=c></canvas><br><img id=demo>

我建议这样计算亮度:

var luma = (11 * obj.data[i] + 16 * obj.data[i + 1] +  5 * obj.data[i + 2]) >> 5;

或者您可以使用 ctx.globalCompositeOperation = 'luminosity' 在白色填充 canvas 上绘制图像之前,虽然不那么有趣但结果相同 (: