【问题标题】:HTML5 Canvas | Replace Color | Remove ColorHTML5 画布 |替换颜色 |去除颜色
【发布时间】:2019-03-13 18:50:33
【问题描述】:

创建此图像的最快方法是什么

在这种情况下,我必须检查每个像素值吗? 这个过程太慢了,例如:对于 100X100px,检查所有 rgba 值需要 40000 次循环

【问题讨论】:

  • 不知道为什么要检查 rgba 的 Alpha 通道。有没有办法通过 hsl 获取像素颜色,特别是它的色调值?
  • 这只是一个例子,即使我们只检查 rgb 值也需要 30000 个循环
  • 请详细说明您想要达到的效果,并展示您尝试过的代码。目前很难想象完成操作后您希望图像是什么样子。
  • 如果您检查每个像素值并删除绿色,您将留下一个带有孔洞的丑陋图像。一种更简单且效果更好的方法是提前计划。这是用帆布制作的。绘制图像时,您可以将每个圆圈保存在一个数组中,然后在排除绿色圆圈后重新绘制所有内容。

标签: javascript html image canvas process


【解决方案1】:

在支持它的浏览器中,您可以使用 svg 过滤器来做到这一点:

这里是an other Q/A,它展示了一种有趣的固定颜色的方法。

在这里,我创建了一个简单的辅助函数,它将为我们设置所需的 tableValues,并带有一些容差,我删除了 <feFill>,因此所选颜色变得透明(<feFill> 会污染 Chrome 中的画布)。 如果你想替换颜色,你仍然可以通过画布的合成选项来实现它(sn-p下面的注释代码)。

const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = e => {
  canvas.width = img.width;
  canvas.height = img.height;
  // update our filter
  updateChroma([76, 237, 0], 8);
// if you wish to replace the color, uncomment followings
//  ctx.fillStyle = "your_replaceColor";
//  ctx.fillRect(0,0,img.width,img.height);
  ctx.filter = 'url(#chroma)';
  ctx.drawImage(img, 0, 0);
  ctx.filter = 'none';
//  ctx.globalCompositeOperation = 'destination-in';
//  ctx.drawImage(img, 0,0);
};
img.src = "https://i.stack.imgur.com/hZm8o.png";

function updateChroma(rgb, tolerance) {
  const sels = ['R', 'G', 'B'];
  rgb.forEach((value, ind) => {
    const fe = document.querySelector('#chroma feFunc' + sels[ind]);
    let vals = '';
    if (!value) {
      vals = '0'
    } else {
      for (let i = 0; i < 256; i++) {
        vals += (Math.abs(value - i) <= tolerance) ? '1 ' : '0 ';
      }
    }
    fe.setAttribute('tableValues', vals);
  });
}
canvas {
  background: ivory
}
<svg width="0" height="0" style="position:absolute;visibility:hidden">
<filter id="chroma" color-interpolation-filters="sRGB"x="0" y="0" height="100%" width="100%">
  <feComponentTransfer>
    <feFuncR type="discrete"/>
    <feFuncG type="discrete"/>
    <feFuncB type="discrete"/>
  </feComponentTransfer>
  <feColorMatrix type="matrix" values="1 0 0 0 0 
                                       0 1 0 0 0
                                       0 0 1 0 0 
                                       1 1 1 1 -1" result="selected"/>
  <feComposite in="SourceGraphic" in2="selected" operator="out"/>
  </filter>
</svg>

<canvas id="canvas"></canvas>

我没有在很多设备上进行广泛的测试,但是在启用硬件加速的情况下,这可能比任何像素循环表现得更好,因为它应该全部在 GPU 上完成。


但浏览器支持仍然不是那么好...
所以无论如何你可能需要回退到像素操作。

在这里,根据您正在处理的色度,您可能需要牺牲一些质量来换取速度。

例如,在视频上,您可以在缩小的画布上执行色度,然后在主要画布上合成,然后将其绘制回来,每帧赢得几次迭代。有关示例,请参阅此 previous Q/A

【讨论】:

  • 似乎通道(rgb)是独立过滤的,所以例如如果我想删除黑色[1,1,1],那么红色[255,1,1]也会被删除,因为绿色和蓝色值是匹配的。纯黑色 [0,0,0] 也不起作用
【解决方案2】:

如果您检查每个像素值并移除绿色,您将留下一个带有孔洞的丑陋图像。一种更简单且效果更好的方法是提前计划。这是用帆布制作的。当您绘制图像时,您可以将每个圆圈保存在一个数组中,然后在排除绿色圆圈后重新绘制所有内容。

在下一个示例中,单击颜色以选择它,或单击 D 删除绿色圆圈。

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var cw = canvas.width = 300,
  cx = cw / 2;
var ch = canvas.height = 180,
  cy = ch / 2;

var color = "blue";


var drawing = false;


var points= [];

class Point{
  constructor(color,x,y){
    this.color = color;
    this.x = x;
    this.y = y
  }
  draw(){
    ctx.fillStyle = this.color;
    ctx.beginPath();
    ctx.arc(this.x,this.y,5,0,2*Math.PI);
    
    ctx.fill()
  }
  
}

canvas.addEventListener('mousedown', function(evt) {
  drawing = true;
  

}, false);

canvas.addEventListener('mouseup', function(evt) {
  drawing = false;

}, false);

canvas.addEventListener("mouseout", function(evt) {
  drawing = false;
}, false);

canvas.addEventListener("mousemove", function(evt) {
  if (drawing) {
    ctx.clearRect(0, 0, cw, ch);
    m = oMousePos(canvas, evt);
    var point = new Point(color,m.x,m.y);
    //point.draw();
    points.push(point);
    
    points.forEach((p) =>{
      p.draw()
    })
  }
}, false);

function oMousePos(canvas, evt) {
  var ClientRect = canvas.getBoundingClientRect();
  return { 
    x: Math.round(evt.clientX - ClientRect.left),
    y: Math.round(evt.clientY - ClientRect.top)
  }
}

colors.addEventListener("click", (e)=>{
  if(e.target.tagName == "SPAN"){color = e.target.id;
  }else if(e.target.id == "deleteGreen"){
    ctx.clearRect(0,0,cw,ch);
    points.forEach( p => {
      if(p.color !== "green"){p.draw()}
    })
  }
})
body {
  background-color: #eee;
}

#app {
  display: block;
  margin: auto;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  width: 300px;
  height: 300px;
}

canvas {
  background: #fff;
  border-radius: 3px;
  box-shadow: 0px 0px 15px 3px #ccc;
  cursor: pointer;
}

#colors {
  display: flex;
  margin-top: 1em;
  justify-content: space-between;
}

#colors span, #deleteGreen {
  display: block;
  width: 50px;
  height: 50px;
  border: 1px solid #d9d9d9;
}

#green {
  background-color: green;
}

#gold {
  background-color: gold;
}

#tomato {
  background-color: tomato;
}

#blue {
  background-color: blue;
}

#deleteGreen {
  text-align: center;
  line-height: 50px;
}
<div id="app">
<canvas id="canvas">:( </canvas>
  <div id="colors" >
    <span id="green"></span>
    <span id="gold"></span>
    <span id="tomato"></span>
    <span id="blue"></span>
    <div id="deleteGreen">D</div>
  </div> 

</div>

【讨论】:

    猜你喜欢
    • 2012-06-19
    • 1970-01-01
    • 2019-01-25
    • 1970-01-01
    • 1970-01-01
    • 2011-09-22
    • 1970-01-01
    • 2014-08-15
    • 1970-01-01
    相关资源
    最近更新 更多