转换图像颜色而不更改其透明背景
Posted
技术标签:
【中文标题】转换图像颜色而不更改其透明背景【英文标题】:Convert Image Color without changing its transparent background 【发布时间】:2014-07-12 21:16:50 【问题描述】:在 html5 或 Canvas(或任何其他 HTML5 技术)的帮助下,有没有人有一个很好的解决方案来改变非透明区域的颜色。
我用谷歌搜索了很多,找到了一些可以改变图像颜色的解决方案(使用 Canvas),例如,
How do i tint an image with HTML5 Canvas?
Canvas - Change colors of an image using HTML5/CSS/JS?
但这些解决方案也会将透明背景更改为给定颜色(我不想要)。
它是具有透明背景的 *** png 图像。现在,我想在不影响其透明像素(背景)的情况下更改图像颜色,这样我就可以在 canvas toDataURL
方法的帮助下使用新颜色再次保存图像。
我不确定是否可以使用 HTML5。非常感谢任何帮助。
提前致谢。
*********************已编辑*********************
添加了药丸(胶囊)的新图像。
【问题讨论】:
这可能正是您不想要的,但我通常更改图像颜色的方法只是让它变得有点透明,并将背景图像颜色设置为所需的颜色。跨度> @dcclassics:我认为你误解了我的意思。我有一个带有一些透明像素的 png 图像,比如说,一个 mario(卡通人物)png 图像。因为马里奥是一个字符并且有多项式的一面。我们的图像必须包含一些透明的像素。所以现在,我想在不改变透明像素的情况下改变字符(马里奥)的颜色。 您可能需要canvas.getImageData
和putImageData
,它们允许您使用RGBA 值逐个像素地编辑画布。请参阅this tutorial 了解一下。
【参考方案1】:
一个演示(请参阅下面有关此演示的说明):http://jsfiddle.net/m1erickson/2d7ZN/
您可以使用context.getImageData
获取包含图像RGBA像素数据的数组
var imageData=context.getImageData(0,0,canvas.width,canvas.height);
var data=imageData.data;
您可以从那里读取(并可选择更改)每个像素的颜色。
rgba 数组的布局如下:
// top-left pixel (x:0,y:0)
data[0] red of pixel#0 (0-255)
data[1] green of pixel#0 (0-255)
data[2] blue of pixel#0 (0-255)
data[3] alpha of pixel#0 (opacity: 0-255)
// next pixel (x:1,y:0)
data[4] red of pixel#1
data[5] green of pixel#1
data[6] blue of pixel#1
data[7] alpha of pixel#1
你可以通过改变数据数组来改变任意像素的R、G、B或A分量
例如,这会将左上角的像素更改为纯红色
data[0]=255; // red
data[1]=0; // green
data[2]=0; // blue
data[3]=255; // alpha (opacity)
关于透明度:
每个像素的 alpha 分量决定了该像素的透明度。如果您只想修改大部分不透明像素,那么您可以轻松地忽略所有半透明像素,如下所示:
// alpha is 0-255
// ignore pixels with alpha < 200 (ignore all pixels that are mostly semi-transparent)
if( data[3]<200 ) // don't process this pixel
最后,您可以使用.putImageData
将修改后的像素推回画布
context.putImageData(imageData,0,0);
关于演示的注意事项
使用画布的默认 RGBA 配色方案很难“重新着色”图像。
所以这个演示将 RGB 数据转换为 HSL 颜色方案。这允许更改“颜色”(色调)而不影响像素的其他组件(S=颜色饱和度,L=颜色亮度)。
像素重新着色后,修改后的 HSL 数据将转换回 RGBA 并保存到数据数组中。
【讨论】:
谢谢马克E。它是一个很好的解决方案。虽然,我仍然在努力解决这个问题。我不知道要在图像中更改的颜色。我只想添加用户传递给图像的颜色(这是原始图像颜色+用户传递的颜色的混合)。 我添加了一个新的药丸(胶囊)图片。我想更改用户以十六进制形式传递的颜色。任何关于这方面的指导都会很棒。感谢Adv. 嗯,透明像素的 alpha 值接近于零(可能是 0-10),所以不要更改 alpha 值较低的像素。您的药丸图像具有较浅和较深的颜色。如果要更改药丸的较暗部分,则以较低 rgb 值(较低 rgb 值 == 较暗颜色)的像素为目标。如果要更改药丸的较亮部分,则以具有较高 rgb 值的像素为目标。正如我的回答一样,您应该将所选像素转换为 HSL,并将所选像素的颜色移向用户传递的颜色。这样,图像的饱和度和亮度就会保持不变。 谢谢。我不知道这是否会解决我的特定问题,但我肯定会尝试一下。这也是一个有价值的建议,这个答案也为解决此类问题提供了一个很好的解决方案。所以,接受你的回答。 是否可以在图像本身绘制在画布上之前更改图像数据?【参考方案2】:我今天遇到了类似的问题,所以我创建了 Node.js 脚本,它完全符合预期。
const chunk = require('lodash');
const createCanvas, loadImage, createImageData = require('canvas');
const hexToRgb = (hex) =>
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, (m, r, g, b) =>
return r + r + g + g + b + b;
);
const result = /^#?([a-f\d]2)([a-f\d]2)([a-f\d]2)$/i.exec(hex);
return result ?
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
: null;
;
const convert = async(url, hex) =>
const r, g, b = hexToRgb(hex);
const baseImage = await loadImage(url);
const canvas = createCanvas(baseImage.width, baseImage.height);
const ctx = canvas.getContext('2d');
ctx.drawImage(baseImage, 0, 0);
const data = ctx.getImageData(0, 0, baseImage.width, baseImage.height);
const pixels = chunk(data, 4);
/**
* @type unknown[]
*/
const convertedPixel = pixels.map((pixel) =>
pixel[0] = r;
pixel[1] = g;
pixel[2] = b;
return pixel;
).reduce((p, n) => [...p, ...n], []);
const imageData = createImageData(
new Uint8ClampedArray(convertedPixel),
baseImage.width, baseImage.height,
);
ctx.putImageData(imageData, 0, 0);
return canvas.toBuffer();
;
【讨论】:
以上是关于转换图像颜色而不更改其透明背景的主要内容,如果未能解决你的问题,请参考以下文章