【问题标题】:BufferedImage.setRGB pixel wrong colorBufferedImage.setRGB 像素颜色错误
【发布时间】:2017-03-13 02:26:58
【问题描述】:

尝试做一些相对简单的事情,给定一张 512x512 png 的地图,我正在尝试绘制点。我的代码相当简单,我尝试使用 setRGB 函数和 createGraphics 函数返回的 Graphics2D 对象。我必须忽略一些简单的事情。编辑:我应该提一下,我不是要创建新的 BufferedImage,而是要修改现有的 BufferedImage,因为连续的库调用将继续修改我正在使用的 BufferedImage。 (在下面的示例代码中,我从一个文件中读取了 BufferedImage,这是一种复制问题的简单方法。

            File outputImage = new File("before.png");
            BufferedImage img = ImageIO.read(outputImage);


            img.setRGB(255, 255, new Color(0f, 1f, 0).getRGB());

            File after = new File("after.png");
            ImageIO.write(img, "png", after);

如果放大生成的像素,它不是绿色,而是一些更深的灰色。由于这种行为与 Graphics2D 一致,我希望解决这个问题也能解决这个问题。

【问题讨论】:

  • 问题是原始图像使用的是IndexColorModel(如果您愿意,也可以使用颜色图或“调色板”)。没有与您指定的颜色匹配的绿色,因此颜色模型会进行查找以获取与您指定的颜色“最接近”的颜色。 @camickr 的解决方案应该可以正常工作,尽管问题在于颜色 model 而不是颜色 space (它们都是 RGB)。 ;-)

标签: java bufferedimage graphics2d javax.imageio


【解决方案1】:

BufferedImage 的色彩空间肯定有问题。

在下面的代码中,我使用您的原始图像并将其绘制到具有指定颜色空间的 BufferedImage:

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import java.io.*;
import javax.imageio.*;
import java.net.*;

public class SSCCE extends JPanel
{
    SSCCE()
    {
        try
        {
            BufferedImage original = ImageIO.read( new File("map.png") );
            int width = original.getWidth(null);
            int height = original.getHeight(null);
            int size = 100;
            BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = bi.createGraphics();
            g2d.drawImage(original, 0, 0, null);

            int color = new Color(0f, 1f, 0f).getRGB();
            bi.setRGB(10, 10, color);
            bi.setRGB(10, 11, color);
            bi.setRGB(11, 10, color);
            bi.setRGB(11, 11, color);
            add( new JLabel( new ImageIcon(bi) ) );
        }
        catch(Exception e2) {}
    }

    private static void createAndShowGUI()
    {
        JFrame frame = new JFrame("SSCCE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new SSCCE());
        frame.pack();
        frame.setLocationByPlatform( true );
        frame.setVisible( true );
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater( () -> createAndShowGUI() );
/*
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI();
            }
        });
*/
    }
}

【讨论】:

  • 谢谢,但是有没有办法用现有的 BufferedImage 做到这一点?不创建一个新的?我从库调用中获取了 BufferedImage,需要对其进行修改,而不是创建一个新的。
  • @ParthMehrotra,我给了你我能想到的最好的解决方案。我不知道 ColorSpaces 是如何工作的,也不知道如何在两者之间进行转换。阅读 BufferedImage API 并查看 ColorSpace 类。也许你能想出一些办法。您的要求是将图像保存到文件中。我看不出您是否首先创建原始图像的副本有多重要。重要的是带有更改的图像被保存到一个文件中。
  • 好吧,我的要求是绘制多个点,我从来没有真正提到将任何内容保存到文件中。感谢您告诉我这个问题是由色彩空间引起的,这是我可以进一步研究的问题。但是,不幸的是,这并不能解决我的问题。
  • 我已经赞成你的回答,因为它对我有帮助,也许将来会对某人有所帮助,但接受的答案必须与给定的 BufferedImage 一起使用,我会尽力提供今晚接受的答案。
  • @haraldK,感谢您理解我的建议。无论这个“图书馆”是什么,OP 都在谈论什么。当图像加载到“库”中时,“库”可以转换图像,然后访问“库”图像的任何其他进程都将使用相同的转换图像。
【解决方案2】:

根据 cmets 中的讨论,尝试描述一些可以解决该问题的方法:

问题在于原始图像使用的是IndexColorModel(如果您愿意,也可以使用颜色图或“调色板”)。没有与您指定的颜色完全匹配的绿色,因此颜色模型会进行查找以将 "closest" 颜色与您指定的颜色相匹配(您可以不同意这种颜色是最接近的匹配,但给出了使用的算法)。

如果您将颜色设置为与图像颜色匹配的颜色,则可以使用该颜色进行绘制。尝试new Color(0.8f, 0.9019608f, 0.6392157f) 或RGB 值0xffcce6a3 为浅绿色。使用new Color(0.6392157f, 0.8f, 1f)0xffa3ccff 表示浅蓝色。

如果您想知道我是如何找到这些值的,下面是解释。假设colorModelIndexColorModel,您可以使用:

int[] rgbs = new int[colorModel.getMapSize()]; 
colorModel.getRGBs(rgbs); 

...获取颜色图中的颜色。选择其中一种颜色应该总是有效的。

现在,如果您的“库”(您没有透露太多细节)正在使用固定调色板来生成这些图像,那么您很好,可以使用我提到的一种颜色,或者使用描述的方法获取颜色,然后选择合适的颜色。如果没有,则需要动态找到最佳颜色。如果你真的不走运,可能根本没有合适的颜色可用(即,你的地图图块全是海洋,唯一可用的颜色是海蓝色,不可能绘制绿点)。那么除了修改库之外,真的没有其他方法可以解决这个问题。

一种完全不同的方法,可能类似于@camickr 的解决方案,您将图像临时转换为真彩色(BufferedImage.TYPE_INT_RGBTYPE_3BYTE_BGR),将您的更改绘制到这个临时图像上,然后将该图像重新绘制到原来的。这可能工作得更好的原因是合成机制将使用抖动和更好的颜色查找算法。但是您仍然会遇到与上一段中所述的可用颜色相关的相同问题。


这是一个代码示例,使用暖黄色,输出:

Color color = new Color(0.89411765f, 0.5686275f, 0.019607844f);
int argb = color.getRGB();

Graphics2D g = image.createGraphics();
try {
    g.setColor(color);
    g.fillRect(10, 10, 50, 50);
}
finally {
    g.dispose();
}

【讨论】:

    猜你喜欢
    • 2020-12-07
    • 2018-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多