如何在不改变其模式的情况下更改HTML5 Canvas中图像的颜色

问题描述 投票:4回答:3

我想改变这个图像的背景颜色,同时保持the image的形式,效果和轮廓。

<canvas id="canvas01" width="1200" height="800"></canvas>
<script>
    function drawImage(imageObj,x, y, width, height){
        var canvas = document.getElementById('canvas01');
        var context = canvas.getContext('2d');
        context.drawImage(imageObj, x, y, width, height);
    }
    var image = new Image();
    image.onload = function(){
        drawImage(this, 400, 100, 320, 450);
    };
    image.src ="images/658FFBC6.png";
</script>
javascript jquery html5 canvas html5-canvas
3个回答
6
投票

Luma保存

冒着类似于现有答案的风险,我想指出一个小而重要的差异,使用略有不同的方法。

关键是保留图像中的亮度分量(即阴影细节,皱纹等),因此需要两个步骤来通过globalCompositeOperation使用混合模式控制外观(或者,使用RGB和RGB之间转换的手动方法)如果必须支持旧浏览器,则为HSL颜色空间):

  • saturation”:将改变下一个绘制元素的色度(强度,饱和度)并将其应用于画布上的现有内容,但保留亮度和色调。
  • hue”:将从光源获取色度和亮度,但如果愿意,可根据下一个绘制的元素改变色调或颜色。

由于这些是混合模式(忽略alpha通道),我们还需要使用合成作为最后一步来剪切结果。

color混合模式也可以使用,但它会改变亮度,这可能是也可能不是。在许多情况下,差异可能很微妙,但也很明显,这取决于失去亮度/阴影定义的目标色度和色调。

因此,为了获得保持亮度和色度的高质量结果,这些或多或少是主要步骤(假设空白画布):

// step 1: draw in original image
ctx.globalCompositeOperation = "source-over";
ctx.drawImage(img, 0, 0);

// step 2: adjust saturation (chroma, intensity)
ctx.globalCompositeOperation = "saturation";
ctx.fillStyle = "hsl(0," + sat + "%, 50%)";  // hue doesn't matter here
ctx.fillRect(0, 0);

// step 3: adjust hue, preserve luma and chroma
ctx.globalCompositeOperation = "hue";
ctx.fillStyle = "hsl(" + hue + ",1%, 50%)";  // sat must be > 0, otherwise won't matter
ctx.fillRect(0, 0, c.width, c.height);

// step 4: in our case, we need to clip as we filled the entire area
ctx.globalCompositeOperation = "destination-in";
ctx.drawImage(img, 0, 0);

// step 5: reset comp mode to default
ctx.globalCompositeOperation = "source-over";

50%亮度(L)将保持原始亮度值。

实例

单击复选框以查看对结果的影响。然后使用不同的色度和色调设置进行测试。

var ctx = c.getContext("2d");
var img = new Image(); img.onload = demo; img.src = "//i.stack.imgur.com/Kk1qd.png";
function demo() {c.width = this.width>>1; c.height = this.height>>1; render()}

function render() {
  var hue = +rHue.value, sat = +rSat.value, l = +rL.value;
  
  ctx.clearRect(0, 0, c.width, c.height);
  ctx.globalCompositeOperation = "source-over";
  ctx.drawImage(img, 0, 0, c.width, c.height);

  if (!!cColor.checked) {
    // use color blending mode
    ctx.globalCompositeOperation = "color";
    ctx.fillStyle = "hsl(" + hue + "," + sat + "%, 50%)";
    ctx.fillRect(0, 0, c.width, c.height);
  }
  else {
    // adjust "lightness"
    ctx.globalCompositeOperation = l < 100 ? "color-burn" : "color-dodge";
    // for common slider, to produce a valid value for both directions
    l = l >= 100 ? l - 100 : 100 - (100 - l);
    ctx.fillStyle = "hsl(0, 50%, " + l + "%)";
    ctx.fillRect(0, 0, c.width, c.height);
    
    // adjust saturation
    ctx.globalCompositeOperation = "saturation";
    ctx.fillStyle = "hsl(0," + sat + "%, 50%)";
    ctx.fillRect(0, 0, c.width, c.height);

    // adjust hue
    ctx.globalCompositeOperation = "hue";
    ctx.fillStyle = "hsl(" + hue + ",1%, 50%)";
    ctx.fillRect(0, 0, c.width, c.height);
  }
  
  // clip
  ctx.globalCompositeOperation = "destination-in";
  ctx.drawImage(img, 0, 0, c.width, c.height);

  // reset comp. mode to default
  ctx.globalCompositeOperation = "source-over";
}

rHue.oninput = rSat.oninput = rL.oninput = cColor.onchange = render;
body {font:16px sans-serif}
<div>
  <label>Hue: <input type=range id=rHue max=359 value=0></label>
  <label>Saturation: <input type=range id=rSat value=100></label>
  <label>Lightness: <input type=range id=rL max=200 value=100></label>
  <label>Use "color" instead: <input type=checkbox id=cColor></label>
</div>
<canvas id=c></canvas>

5
投票

全球综合业务

2D上下文属性ctx.globalCompositeOperation对于各种图像处理任务非常有用。有关globalCompositeOperation at MDN的更多信息

您可以将图像转换为画布,这样您就可以对其进行编辑。

function imageToCanvas(image){
    const c = document.createElement("canvas");
    c.width = image.width;
    c.height = image.height;
    c.ctx = c.getContext("2d"); // attach context to the canvas for eaasy reference
    c.ctx.drawImage(image,0,0);
    return c;
}

您可以使用globalCompositeOperation = "color"为图像着色

function colorImage(image,color){ // image is a canvas image
     image.ctx.fillStyle = color;
     image.ctx.globalCompositeOperation = "color";
     image.ctx.fillRect(0,0,image.width,image.height);
     image.ctx.globalCompositeOperation = "source-over";
     return image;
}

不幸的是,这也会覆盖alpha像素,因此您需要使用原始图像作为遮罩来恢复alpha像素。

function maskImage(dest,source){
     dest.ctx.globalCompositeOperation = "destination-in";
     dest.ctx.drawImage(source,0,0);
     dest.ctx.globalCompositeOperation = "source-over";
     return dest;
}

然后你有一个彩色图像

Example.

在他的例子中,我用一系列颜色为图像着色,并添加了一个功能,将图像的画布副本恢复为原始图像。如果从页面获取图像作为元素,则使用naturalWidthnaturalHeight作为widthheight属性可能与图像分辨率不匹配。

const ctx = canvas.getContext("2d");
const image = new Image;
var colCopy;
image.src = "https://i.stack.imgur.com/Kk1qd.png";
image.onload = () => {
  colCopy = imageToCanvas(image);
  const scale = canvas.height / image.naturalHeight; 
  ctx.scale(scale, scale);
  ctx.drawImage(colCopy, 0, 0);
  for (var i = 32; i < 360; i += 32) {
    restoreImage(colCopy, image);
    colorImage(colCopy, "hsl(" + i + ",100%,50%)");
    maskImage(colCopy, image);
    ctx.drawImage(colCopy, 150 * i / 16, 0);
  }
}



function imageToCanvas(image) {
  const c = document.createElement("canvas");
  c.width = image.naturalWidth;
  c.height = image.naturalHeight;
  c.ctx = c.getContext("2d"); // attach context to the canvas for easy reference
  c.ctx.drawImage(image, 0, 0);
  return c;
}

function restoreImage(dest, source) {
  dest.ctx.clearRect(0, 0, dest.width, dest.height);
  dest.ctx.drawImage(source, 0, 0);
  return dest;
}

function colorImage(dest, color) { // image is a canvas image
  dest.ctx.fillStyle = color;
  dest.ctx.globalCompositeOperation = "color";
  dest.ctx.fillRect(0, 0, dest.width, dest.height);
  dest.ctx.globalCompositeOperation = "source-over";
  return dest;
}

function maskImage(dest, source) {
  dest.ctx.globalCompositeOperation = "destination-in";
  dest.ctx.drawImage(source, 0, 0);
  dest.ctx.globalCompositeOperation = "source-over";
  return dest;
}
canvas {
  border: 2px solid black;
}
<canvas id="canvas" width=600></canvas>

在某些情况下,图像可能会稍微褪色,您可以使用类似于上面所示的复合操作将图像转换为更高对比度的黑白图像,并使用高对比度图像作为模板进行着色。

使用过滤器

大多数常见的浏览器现在支持具有色调偏移滤镜的画布滤镜。您可以使用它将色调移动到您想要的值,但首先您需要知道图像原始色调是什么。 (参见下面有关如何查找HUE的示例)

有关兼容性以及如何使用画布筛选器,请参阅Canvas filters at MDN

以下函数将保持饱和度并仅移动色调。

// dest canvas to hold the resulting image
// source the original image
// hue The hue to set the dest image to
// sourceHue the hue reference point of the original image.
function colorImage(dest,source, hue , sourceHue) { // image is a canvas image
  dest.ctx.clearRect(0,0,dest.width, dest.height);
  dest.ctx.filter="hue-rotate("+((hue - sourceHue) | 0)+"deg)";
  dest.ctx.drawImage(source,0, 0, dest.width, dest.height);
  return dest;
}

Filters example.

以下使用ctx.filter = "hue-rotate(30deg)"旋转色调。我没有包含任何代码来查找图像原始色调,所以手动将其设置为120。

const ctx = canvas.getContext("2d");
const image = new Image;
var colCopy;
const sourceHue = 120;
image.src = "https://i.stack.imgur.com/Kk1qd.png";
image.onload = () => {
  colCopy = imageToCanvas(image);
  
  const scale = canvas.height / image.naturalHeight; 
  ctx.scale(scale, scale);
  ctx.drawImage(colCopy, 0, 0);
  for (var i = 32; i < 360; i += 32) {
    colorImage(colCopy,image,i,sourceHue);
    ctx.drawImage(colCopy, 150 * i / 16, 0);
  }
}



function imageToCanvas(image) {
  const c = document.createElement("canvas");
  c.width = image.naturalWidth;
  c.height = image.naturalHeight;
  c.ctx = c.getContext("2d"); // attach context to the canvas for easy reference
  c.ctx.drawImage(image, 0, 0);
  return c;
}

function colorImage(dest,source, hueRotate , sourceHue) { // image is a canvas image
  dest.ctx.clearRect(0,0,dest.width, dest.height);
  dest.ctx.filter="hue-rotate("+((hueRotate - sourceHue) | 0)+"deg)";
  dest.ctx.drawImage(source,0, 0, dest.width, dest.height);
  return dest;
}
canvas {
  border: 2px solid black;
}
<canvas id="canvas" width=600></canvas>

RGB to Hue

有很多答案可以帮助在SO上找到像素的色调。这是一个特别详细的RGB to HSL conversion如果你发现它有用,不要忘记提出答案。

Filters example White.

以下使用ctx.filter = "grayscale(100%)"去除饱和度,然后使用ctx.filter = "brightness(amount%)"来改变亮度。这提供了从黑色到白色的一系列灰色。您也可以通过减少灰度量来对颜色执行相同操作。

const ctx = canvas.getContext("2d");
const image = new Image;
var colCopy;
const sourceHue = 120;
image.src = "https://i.stack.imgur.com/Kk1qd.png";
image.onload = () => {
  colCopy = imageToCanvas(image);
  
  const scale = canvas.height / image.naturalHeight; 
  ctx.scale(scale, scale);
  ctx.drawImage(colCopy, 0, 0);
  for (var i = 40; i < 240; i += 20) {
    grayImage(colCopy,image,i);
    ctx.drawImage(colCopy, 150 * ((i-40) / 12), 0);
  }
}



function imageToCanvas(image) {
  const c = document.createElement("canvas");
  c.width = image.naturalWidth;
  c.height = image.naturalHeight;
  c.ctx = c.getContext("2d"); // attach context to the canvas for easy reference
  c.ctx.drawImage(image, 0, 0);
  return c;
}

function grayImage(dest,source, brightness) { // image is a canvas image
  dest.ctx.clearRect(0,0,dest.width, dest.height);
  dest.ctx.filter = "grayscale(100%)";
  dest.ctx.drawImage(source,0, 0, dest.width, dest.height);
  dest.ctx.filter = "brightness(" + brightness +"%)";
  dest.ctx.drawImage(dest,0, 0, dest.width, dest.height);

  return dest;
}
canvas {
  border: 2px solid black;
}
<canvas id="canvas" width=600></canvas>

-1
投票

在执行绘制操作之前,您可以在一行代码上组合过滤器,如下所示:

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const image = document.getElementById('source');

ctx.filter = 'hue-rotate(120deg) grayscale(10%) brightness(150%)';
ctx.drawImage(image, 10, 10, 180, 120);
<canvas id="canvas"></canvas>
<div style="display:none;">
  <img id="source"
       src="https://interactive-examples.mdn.mozilla.net/media/examples/gecko-320-213.jpg">
</div>
© www.soinside.com 2019 - 2024. All rights reserved.