【问题标题】:Attempting to set alpha value of color changes color尝试设置颜色的 alpha 值会改变颜色
【发布时间】:2021-10-21 10:16:02
【问题描述】:

我创建此代码是为了从图像中删除所有半透明颜色并使它们完全不透明。出于某种原因,图像的颜色发生了巨大的变化,即使我只改变了 alpha。附件是代码和图像发生情况的示例。

之前:

之后:

public class Main {
    
    public static void main(String args[]) throws IOException
    {
        
        File file = new File("karambitlore.png");
        FileInputStream fis = new FileInputStream(file);
        BufferedImage image = ImageIO.read(fis);
        
        image = convertToType(image, BufferedImage.TYPE_INT_ARGB);
        
        BufferedImage image2 = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
        
        for (int width = 0; width < image.getWidth(); width++)
        {
            for (int height = 0; height < image.getHeight(); height++)
            {

                int rgb = image.getRGB(width, height);

                boolean transparent = (rgb & 0xFF000000) == 0x0;
                boolean opaque = (rgb & 0xFF000000) == 0xFF000000;
                
                if (!transparent && !opaque)
                {
                    
                    rgb = rgb | 0xFF000000;
                    
                    image2.setRGB(width, height, rgb);
                    
                } else
                {
                    image2.setRGB(width, height, image.getRGB(width, height));

                }
                
            }
        }
       
        fis.close();
        ImageIO.write(image2, "png", file);
        
        System.out.println(image.getType());
        
    }
    
    public static BufferedImage convertToType(BufferedImage image, int type) {
        BufferedImage newImage = new BufferedImage(image.getWidth(), image.getHeight(), type);
        Graphics2D graphics = newImage.createGraphics();
        graphics.drawImage(image, 0, 0, null);
        graphics.dispose();
        return newImage;
            
    }

}

【问题讨论】:

  • 我没有看到颜色变化很大。我确实看到第二张图像的边缘没有那么清晰。我猜这是因为抗锯齿效果不佳?不知道如何解决。
  • @camickr 代码只影响那些最外面的部分,因为它们是整个图像上唯一的半透明区域。每个被“rgb = rgb | 0xFF000000”修改的像素都会变色。如果放大图像,您会看到一些不同颜色的像素似乎是凭空出现的
  • 这些可能是原始图像中几乎透明的彩色像素。
  • 与其让半透明像素完全不透明,不如将它们与背景颜色混合。
  • 为了获得更好的结果,您需要实际计算每个半透明像素的 RGB 贡献(这与混合基本相同)。您可能还应该使用一个阈值,比如 50%,并且只使比该阈值更不透明的像素完全不透明(低于阈值应设置为透明)。

标签: java image image-processing


【解决方案1】:

输入图像的主要问题是它在完全透明的像素周围有一个几乎透明的“光环”,有点像“抖动”的透明度。当您将这些像素完全不透明时,它们 a)在您不想要它们的地方创建不透明像素,并且 b)使这些像素过于饱和,因为像素没有与 alpha 预乘(如果你是,你可以谷歌“预乘 alpha”对 alpha 合成/混合感兴趣)。请注意,像素并没有真正“改变颜色”,只是它们通常对最终结果贡献不大,因为它们几乎是完全透明的。

无论背景颜色如何都可以正常工作的简单解决方法是使用阈值来决定像素是否应该是透明的。

如果您知道背景颜色(纯色或某种“平均”颜色),您可以使用 alpha 值将像素颜色与背景颜色混合。这将创建更平滑的边缘,但仅针对此背景。在这种情况下,您可以使用较低的阈值。

这是您的代码的修改版本,可为您的输入产生更好的结果:

public static void main(String args[]) throws IOException {
    File file = new File(args[0]);

    BufferedImage image = convertToType(ImageIO.read(file), BufferedImage.TYPE_INT_ARGB);

    Color bg = Color.ORANGE; // Change to the color of your background, if you want to blend

    int bgR = bg.getRed();
    int bgG = bg.getGreen();
    int bgB = bg.getBlue();


    for (int width = 0; width < image.getWidth(); width++) {
        for (int height = 0; height < image.getHeight(); height++) {
            int rgb = image.getRGB(width, height);

            int opaqueness = (rgb >>> 24) & 0xFF;

            if (opaqueness > 100) { // The threshold 100 works fine for your current input, tune to suit other needs
                // Fully opaque
                image.setRGB(width, height, rgb | 0xFF000000);

                // Or if you prefer blending the background:
/*
                // Multiply alpha
                int r = ((rgb >> 16 & 0xFF) * opaqueness) + (bgR * (0xFF - opaqueness)) >> 8;
                int g = ((rgb >>  8 & 0xFF) * opaqueness) + (bgG * (0xFF - opaqueness)) >> 8;
                int b = ((rgb       & 0xFF) * opaqueness) + (bgB * (0xFF - opaqueness)) >> 8;

                image.setRGB(width, height, r << 16 | g << 8 | b | 0xFF000000);
*/
            } else {
                // Fully transparent
                image.setRGB(width, height, rgb & 0xFFFFFF);
            }
        }
    }

    ImageIO.write(image, "png", new File(file.getParentFile(), file.getName() + "_copy_bg_mult.png"));
}

public static BufferedImage convertToType(BufferedImage image, int type) {
    BufferedImage newImage = new BufferedImage(image.getWidth(), image.getHeight(), type);
    Graphics2D graphics = newImage.createGraphics();
    graphics.setComposite(AlphaComposite.Src);
    graphics.drawImage(image, 0, 0, null);
    graphics.dispose();
    return newImage;

}

按原样的代码将创建一个像这样的图像,它的边缘会有轻微的锯齿状,但“光晕”被移除了:

或者,如果您注释掉“完全不透明”版本,并在“乘 alpha”部分中添加注释,您可以获得这样的图像(显然只在黄色背景下看起来不错):

【讨论】:

    猜你喜欢
    • 2013-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多