转换图像颜色而不更改其透明背景

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.getImageDataputImageData,它们允许您使用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();

;

【讨论】:

以上是关于转换图像颜色而不更改其透明背景的主要内容,如果未能解决你的问题,请参考以下文章

如何根据背景颜色自动更改视图的透明颜色

如何将背景转换为透明? [关闭]

透明背景div上的背景颜色过渡效果?

如何将半透明的“图片/图像”设置为黑白,而不影响子元素和背景颜色? [复制]

在 IrfanView 中设置透明图像背景

如何在不影响子元素的情况下更改背景图像的不透明度?