在Java中对图像应用色彩调整

23

我正在尝试为我的程序创建几个类似的视觉风格,每个风格都有不同的颜色主题。为了实现这个目标,我已经使用图标来表示JCheckBoxJRadioButton的不同状态。不想为每种可能的颜色制作一个完整的图标集,是否有办法在显示图片之前只改变图像的色调/饱和度/亮度/透明度?

7个回答

14
有一种方法,但需要利用一些BufferedImage变换。一旦创建它们,请将它们缓存或保存下来以便稍后轻松重复使用。基本上,您需要从黑色图像(源颜色#000000)开始,只使用alpha层关闭像素(还提供平滑的反锯齿效果)。例如,在您的源图像中,每个像素都是黑色的,但是alpha通道因像素而异。
首先,请阅读此文章以获得一些背景信息:http://www.javalobby.org/articles/ultimate-image/ 完成这篇文章后,您需要将图像加载到BufferedImage中:
BufferedImage loadImg = ImageUtil.loadImage("C:/Images/myimg.png");

接下来你需要创建一个新的BufferedImage来进行转换:

public BufferedImage colorImage(BufferedImage loadImg, int red, int green, int blue) {
    BufferedImage img = new BufferedImage(loadImg.getWidth(), loadImg.getHeight(),
        BufferedImage.TRANSLUCENT);
    Graphics2D graphics = img.createGraphics(); 
    Color newColor = new Color(red, green, blue, 0 /* alpha needs to be zero */);
    graphics.setXORMode(newColor);
    graphics.drawImage(loadImg, null, 0, 0);
    graphics.dispose();
    return img;
}

实际上,setXORMode将使用您提供的颜色与源图像中的颜色进行XOR运算。如果源图像是黑色的,则无论您提供什么颜色都将按您指定的方式编写。使用“0”作为Alpha通道的新颜色,原始Alpha通道值将受到尊重。最终结果就是您要寻找的复合图像。

编辑:

您可以通过两种方式之一加载初始BufferedImage。最简单的方法是使用Java的较新的ImageIO API:http://download.oracle.com/javase/6/docs/api/javax/imageio/ImageIO.html 将文件直接加载到BufferedImage中。调用应该类似于这样:

BufferedImage img = ImageIO.read(url); 

或者,你可以创建一个使用 ToolKit 读取图像的方法。

public BufferedImage loadImage(String url) {
    ImageIcon icon = new ImageIcon(url);
    Image image = icon.getImage();

    // Create empty BufferedImage, sized to Image
    BufferedImage buffImage = 
      new BufferedImage(
        image.getWidth(null), 
        image.getHeight(null), 
        BufferedImage.TYPE_INT_ARGB);

    // Draw Image into BufferedImage
    Graphics g = buffImage.getGraphics();
    g.drawImage(image, 0, 0, null);
    return buffImage;
}
当然,如果你留心的话,我们必须像着色一样做同样的事情来将图像读入缓冲图像中。简而言之,如果你改变了 colorImage 方法的签名以接受 Image 对象,则只需要对 getWidth() getHeight()方法进行一些更改即可使其正常工作。

我可以用这个来处理 ImageImageIcon 吗? - Ky -
1
好的,我再做了一点调查,这就是我的结果:Image img = Toolkit.getDefaultToolkit().getImage(URL 或文件路径); - Berin Loritsch
其他应该保持不变。 - Berin Loritsch
请注意,BufferedImage.TRANSLUCENT 不是有效的 BufferedImage 类型。实际的常量值恰好与 BufferedImage.TYPE_INT_ARGB_PRE 相同。 - Grodriguez
1
请考虑过去6年中API的变化。当这篇文章在2010年写作时,代码是正确的,Java是由Sun拥有的,世界也不同了。如果现在有一种新的更好的方法来完成这个问题,而不是对这个答案进行贬低,请提供一个新的更新的答案。 - Berin Loritsch
显示剩余4条评论

4

计算每个颜色分量的平均值并保留原始透明度:

public static void tint(BufferedImage image, Color color) {
    for (int x = 0; x < image.getWidth(); x++) {
        for (int y = 0; y < image.getHeight(); y++) {
            Color pixelColor = new Color(image.getRGB(x, y), true);
            int r = (pixelColor.getRed() + color.getRed()) / 2;
            int g = (pixelColor.getGreen() + color.getGreen()) / 2;
            int b = (pixelColor.getBlue() + color.getBlue()) / 2;
            int a = pixelColor.getAlpha();
            int rgba = (a << 24) | (r << 16) | (g << 8) | b;
            image.setRGB(x, y, rgba);
        }
    }
}

这对我的情况最有效。


3
public static void tint(BufferedImage img) {

    for (int x = 0; x < img.getWidth(); x++) {
        for (int y = 0; y < img.getHeight(); y++) {

            Color color = new Color(img.getRGB(x, y));

            // do something with the color :) (change the hue, saturation and/or brightness)
            // float[] hsb = new float[3];
            // Color.RGBtoHSB(color.getRed(), old.getGreen(), old.getBlue(), hsb);

            // or just call brighter to just tint it
            Color brighter = color.brighter();

            img.setRGB(x, y, brighter.getRGB());
        }
    }
}

我不想创建一个新的图像文件。我想在程序内对其进行着色。 - Ky -
如何将Image或ImageIcon转换为BufferedImage? - Ky -
你需要创建一个大小和颜色分辨率相同的BufferedImage。然后,你可以在BufferedImage上调用getGraphics方法,并使用该图形上下文将Image或ImageIcon绘制在0,0位置。之后,你可以自由地处理像素数据。 - Johncl

3
最简单的方法是使用JH Labs图像滤镜。您只需调用以下代码即可调整HSB,
public BufferedImage setHSB(BufferedImage source, float hValue, float sValue, float bValue) {        
    com.jhlabs.image.HSBAdjustFilter hsb hsb = new HSBAdjustFilter();
    BufferedImage destination = hsb.createCompatibleDestImage(source, null);
    hsb.setHFactor(hValue);
    hsb.setSFactor(sValue);
    hsb.setBFactor(bValue);
    BufferedImage result = hsb.filter(bi, destination);

    return result;
}

2
bi 代表什么? - Adam Arold

1
我尝试了这个页面上的每一个解决方案,但都没有成功。Xor(被接受的答案)对我不起作用 - 不管参数是什么,它都会变成奇怪的黄色,而不是我给定的颜色。最终我找到了一个适合我的方法,虽然有点混乱。我想把它加进来,以防其他人遇到与我同样的问题。干杯!
/** Tints the given image with the given color.
 * @param loadImg - the image to paint and tint
 * @param color - the color to tint. Alpha value of input color isn't used.
 * @return A tinted version of loadImg */
public static BufferedImage tint(BufferedImage loadImg, Color color) {
    BufferedImage img = new BufferedImage(loadImg.getWidth(), loadImg.getHeight(),
            BufferedImage.TRANSLUCENT);
    final float tintOpacity = 0.45f;
    Graphics2D g2d = img.createGraphics(); 

    //Draw the base image
    g2d.drawImage(loadImg, null, 0, 0);
    //Set the color to a transparent version of the input color
    g2d.setColor(new Color(color.getRed() / 255f, color.getGreen() / 255f, 
        color.getBlue() / 255f, tintOpacity));

    //Iterate over every pixel, if it isn't transparent paint over it
    Raster data = loadImg.getData();
    for(int x = data.getMinX(); x < data.getWidth(); x++){
        for(int y = data.getMinY(); y < data.getHeight(); y++){
            int[] pixel = data.getPixel(x, y, new int[4]);
            if(pixel[3] > 0){ //If pixel isn't full alpha. Could also be pixel[3]==255
                g2d.fillRect(x, y, 1, 1);
            }
        }
    }
    g2d.dispose();
    return img;
}

1
这并不是完全的染色,更像是在上面应用另一层,但对我有效:
public static BufferedImage colorImage(BufferedImage loadImg, int red, int green, int blue, int alpha /*Also the intesity*/) {
    Graphics g = loadImg.getGraphics();
    g.setColor(new Color(red, green, blue, alpha));
    g.fillRect(0, 0, loadImg.getWidth(), loadImg.getHeight());
    g.dispose();
    return loadImg;
}

这不仅仅返回一个纯色的图像吗? - Ky -
不,如果你设置了alpha值,它会变得有点透明,所以不要将alpha值设为255,而是将其设置为100之类的数值,这样就可以正常工作了。 - APerson
1
如果我尝试对一个已经带有 alpha 通道的图像进行着色,那么这种方法就不起作用了,因为它会将透明区域填充为半透明颜色。 - Ky -
这就是这个问题的问题。 - APerson
如果您添加alpha合成,它将起作用。使用不同的选项检查((Graphics2D) g).setCompositr(...)。 - Soley
设置 g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP)) - Danon

0

因为我找到的所有方法出于某种原因都不适用于我,所以这里有一种简单的方法来解决这个问题(无需额外的库):

/**
 * Colors an image with specified color.
 * @param r Red value. Between 0 and 1
 * @param g Green value. Between 0 and 1
 * @param b Blue value. Between 0 and 1
 * @param src The image to color
 * @return The colored image
 */
protected BufferedImage color(float r, float g, float b, BufferedImage src) {

    // Copy image ( who made that so complicated :< )
    BufferedImage newImage = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TRANSLUCENT);
    Graphics2D graphics = newImage.createGraphics();
    graphics.drawImage(src, 0, 0, null);
    graphics.dispose();

    // Color image
    for (int i = 0; i < newImage.getWidth(); i++) {
        for (int j = 0; j < newImage.getHeight(); j++) {
            int ax = newImage.getColorModel().getAlpha(newImage.getRaster().getDataElements(i, j, null));
            int rx = newImage.getColorModel().getRed(newImage.getRaster().getDataElements(i, j, null));
            int gx = newImage.getColorModel().getGreen(newImage.getRaster().getDataElements(i, j, null));
            int bx = newImage.getColorModel().getBlue(newImage.getRaster().getDataElements(i, j, null));
            rx *= r;
            gx *= g;
            bx *= b;
            newImage.setRGB(i, j, (ax << 24) | (rx << 16) | (gx << 8) | (bx << 0));
        }
    }
    return newImage;
}

黑色图像将始终保持黑色,但白色图像将是您指定的颜色。此方法通过每个像素并使用参数乘以图像的红色、绿色和蓝色值。这是OpenGL glColor3f() 方法的确切行为。R、G 和 B 参数必须为 0.0F 到 1.0F。

此方法对 alpha 值没有问题。


这个不起作用。会产生LSD幻觉。 - Adam Arold
哎呀?你能发一下截图吗?我也用过这个方法…… - 2xsaiko
你的看起来像这样。我的(也不好)看起来像这样。请注意,红色的脸在你的解决方案中看起来,但它只是一个简单的红色(255, 0, 0)。其他颜色由于某种原因无法正常工作。未着色的原始图像看起来像这样 - Adam Arold
我想做的是给灰度图像添加颜色。 - Adam Arold
你可以贴出你 color() 方法的调用吗? - 2xsaiko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接