使 BufferedImage 变灰

Graying out a BufferedImage

我正在尝试使 BufferedImage 变灰(不是将其转换为灰度,只是在顶部添加灰色调)。现在,我正在通过使用另一张图像,使其半透明,然后将其覆盖在我的原始图像之上来做到这一点。这是我现在的代码:

package com.mypkg;

import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.URL;

import javax.imageio.ImageIO;

import org.imgscalr.Scalr;

public class Overlay {
    public static void main(String args[]){
        URL url = null;
        try{
            //The gray image used for overlay
            url = new URL("https://hoursofidleness.files.wordpress.com/2012/06/gray-card.jpg");
            BufferedImage img1 = ImageIO.read(url);

            //The original image which I want to gray out
            url = new URL("http://www.staywallpaper.com/wp-content/uploads/2016/01/Colorful-Wallpaper-HD-pictures-STAY015.jpg");
            BufferedImage img2 = ImageIO.read(url);
            BufferedImage reImg2 = Scalr.resize(img2, Scalr.Method.BALANCED, Scalr.Mode.FIT_EXACT, 150, 150);

            //Make the gray image, which is used as the overlay, translucent
            BufferedImage transparent = new BufferedImage(img1.getWidth(), img1.getHeight(),BufferedImage.TRANSLUCENT);
            Graphics2D g2d = transparent.createGraphics();
            g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) 0.50));
            g2d.drawImage(img1, null, 0, 0);
            g2d.dispose();
            BufferedImage reImg1 = Scalr.resize(transparent, Scalr.Method.BALANCED, Scalr.Mode.FIT_EXACT, 150, 150);

            //Merge both images
            BufferedImage result = new BufferedImage(150, 150, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g = result.createGraphics();
            g.drawImage(reImg2, 0, 0, null);
            g.drawImage(reImg1, 0, 0, null);
            g.dispose();
            ImageIO.write(result,"png",new File("/result.png"));
        } catch(Exception e){
            e.printStackTrace();
        }
    }
}

有没有其他方法可以在不使用附加图像进行叠加的情况下实现这一点?我可以简单地在原始图像上添加灰色调吗?我已经尝试了很多我在其他帖子上看到的建议,但是 none 有效。

谢谢。

你的方法主要做什么:

  • 加载一个灰色图像,其中所有像素都具有相同的灰色。
  • 创建新图像。
  • 在该图像上绘制灰色图像,使其半透明。
  • 在要变灰的图像上绘制半透明灰色图像。

首先,没有真正需要创建 transparent 图像。您可以直接在真实图像上使用合成绘图。

其次,完全灰色的图像与普通矩形没有区别,Graphics2D class 有一个 fillRect 绘制填充矩形的方法,可能很多比绘制图像更快。

因此,在将原始图像加载并缩放到 reImg2 后,您可以使用:

    Graphics2D g2d = reImg2.createGraphics();
    g2d.setColor(new Color(20,20,20,128));
    g2d.fillRect(0, 0, reImg2.getWidth(), reImg2.getHeight());
    g2d.dispose();

就是这样,现在 reImg2 变暗了,您可以将其写入您的文件。调整值 - 将 20s 更改为较低的值以获得更深的灰色或更高的值(最高 255)以获得更浅的灰色。将 128(50% alpha)更改为更高的值以获得更灰显的图像,或更改为更低的值以获得更灰显的图像。

这当然不是最有效的方法(因为您必须遍历图像中的每个像素),但此代码会降低源图像的饱和度并将结果保存到新文件中。请注意,我已经删除了缩放代码以尝试使事情变得简洁:

package com.mypkg;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.imageio.ImageIO;

public class Overlay
{
    private static int adjust_saturation(int argb, float factor)
    {
        float[] hsb = new float[3];

        // Extract out the components of the color from the 32 bit integer
        int alpha = (argb >> 24) & 0xff;
        int red   = (argb >> 16) & 0xff;
        int green = (argb >> 8) & 0xff;
        int blue  = argb & 0xff;

        // Converts RGB into HSB and fills the 'hsv' array with 3 elements:
        //
        // hsb[0] = hue
        // hsb[1] = saturation
        // hsb[2] = brightness
        Color.RGBtoHSB(red, green, blue, hsb);

        // Adjust the saturation as desired
        hsb[1] *= factor;

        // Convert back to RGB and return
        return Color.HSBtoRGB(hsb[0], hsb[1], hsb[2]);
    }

    public static void main(String args[])
            throws MalformedURLException, IOException
    {
        URL url = new URL("my-input-file.jpg");
        BufferedImage image = ImageIO.read(url);

        // For every column
        for (int x = 0; x < image.getWidth(); x++) {
            // For every row
            for (int y = 0; y < image.getHeight(); y++) {
                // For each pixel of the image, grab the RGB (alpha, red, green, blue)
                int argb = image.getRGB(x, y);

                // Calculate a desaturated pixel and overwrite the current pixel
                // A value of 0.2 will desaturate by 80%, a value of 1.5 will
                // increase the saturation by 50%, etc.
                image.setRGB(x, y, adjust_saturation(argb, 0.2f));
            }
        }

        ImageIO.write(image, "png", new File("my-output-file.png"));
    }
}

这是结果,左边是原始的,右边是去饱和的: