使用Java的ImageIO将像素数组转换为图像对象?

28

我目前正在使用以下代码将像素值数组(最初由java.awt.image.PixelGrabber对象创建)转换为Image对象:

public Image getImageFromArray(int[] pixels, int width, int height) {
    MemoryImageSource mis = new MemoryImageSource(width, height, pixels, 0, width);
    Toolkit tk = Toolkit.getDefaultToolkit();
    return tk.createImage(mis);
}

是否可以使用ImageIO包中的类来实现相同的结果,这样我就不必使用AWT Toolkit了?

Toolkit.getDefaultToolkit()似乎并不完全可靠,有时会抛出AWTError,而ImageIO类应该始终可用,这就是我想改变我的方法的原因。

6个回答

29

您可以不使用ImageIO创建图像。只需使用与像素数组内容匹配的图像类型创建BufferedImage即可。

public static Image getImageFromArray(int[] pixels, int width, int height) {
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
            WritableRaster raster = (WritableRaster) image.getData();
            raster.setPixels(0,0,width,height,pixels);
            return image;
        }

使用PixelGrabber时,在调用 getImageFromArray 之前,请不要忘记从像素数组中提取RGBA信息。在PixelGrabber javadoc中的 handlepixelmethod 中有一个例子。完成此操作后,确保BufferedImage构造函数中的图像类型为 BufferedImage.TYPE_INT_ARGB


3
谢谢bcash,但是当我尝试那段代码时,出现了java.lang.ArrayIndexOutOfBoundsException。有什么想法吗? - Chris Carruthers
我觉得这很接近了,我们可能需要更多来自Chris的详细信息?看起来应该可以工作了...你的ints是什么?它们是RGB吗?ARGB?还是某种打包格式? - John Gardner
像素来自PixelGrabber,代码如下: int[] pixels = new int[width * height]; PixelGrabber pg = new PixelGrabber(img, 0, 0, width, height, pixels, 0, width); pg.grabPixels(); - Chris Carruthers
2
Chris,你遇到AOOB异常是因为BufferedImage“image”的大小为width * height * 3,因为它是int RGB类型。请参见编辑。 - Brendan Cashman
当我尝试运行这段代码时,我得到了一张黑色的图片。就好像raster.setPixels根本没有影响BufferedImage一样。 - Mutant Bob
1
正如@haraldK(在下面的帖子中)所提醒的,Java文档提到 BufferedImage.getData()返回一个Raster并且BufferedImage.getRaster()返回一个WritableRaster。因此,如果使用getData()似乎它会返回一份副本,并且在执行setPixels()后必须使用BufferedImage.setData()将其放回,这是一种额外的开销。因此最好使用getRaster()并对其进行操作。 - Alireza Kazemi

7

使用光栅时,即使我使用TYPE_INT_ARGB创建了BufferedImage,仍然出现了ArrayIndexOutOfBoundsException。然而,使用BufferedImagesetRGB(...)方法对我很有效。


我曾经遇到过类似的问题,即使输入和输出的数组大小匹配。最终我也使用了setRGB。它可能会稍微慢一些,但它能正常工作。我认为Alpha与非Alpha不应该改变数组大小,因为它只是单个int中最重要的2个字节。 - bobtheowl2
以上代码如何使用setRGB()函数?谢谢。 - GhostDede

3

JavaDoc中关于BufferedImage.getData()的说明是:"一个光栅(Raster),它是图像数据的副本。"

这段代码对我来说可行,但我怀疑它的效率:

        // Получаем картинку из массива.
        int[] pixels = new int[width*height];
            // Рисуем диагональ.
            for (int j = 0; j < height; j++) {
                for (int i = 0; i < width; i++) {
                    if (i == j) {
                        pixels[j*width + i] = Color.RED.getRGB();
                    }
                    else {
                        pixels[j*width + i] = Color.BLUE.getRGB();
                        //pixels[j*width + i] = 0x00000000;
                    }
                }
            }

BufferedImage pixelImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);    
    pixelImage.setRGB(0, 0, width, height, pixels, 0, width);

2
可以使用getRaster()代替getData() - Harald K

2

我使用java.awt.Robot成功地截取了屏幕截图(或者屏幕的一部分),但是为了能在ImageIO上工作,你需要将它存储在BufferedImage中而不是内存映像源中。然后,你可以调用ImageIO的一个静态方法并保存文件。尝试这样做:

// Capture whole screen
Rectangle region = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
BufferedImage capturedImage = new Robot().createScreenCapture(region);

// Save as PNG
File imageFile = new File("capturedImage.png");
ImageIO.write(capturedImage, "png", imageFile);

0

我和其他人一样遇到了同样的问题,尝试应用这个问题的正确答案,我的int数组实际上会出现OutOfboundException,我通过添加一个索引来修复它,因为数组的长度必须是widht*height*3,之后我无法获取图像,所以我将光栅设置为图像

public static Image getImageFromArray(int[] pixels, int width, int height) {
        BufferedImage image = new BufferedImage(width, height,     BufferedImage.TYPE_INT_ARGB);
        WritableRaster raster = (WritableRaster) image.getData();
        raster.setPixels(0,0,width,height,pixels);
        image.setData(raster); 
        return image;
    }

如果您在JFrame上的标签上显示它,您就可以看到该图像,就像这样

    JFrame frame = new JFrame();
    frame.getContentPane().setLayout(new FlowLayout());
    frame.getContentPane().add(new JLabel(new ImageIcon(image)));
    frame.pack();
    frame.setVisible(true);

设置图像在imageIcon()上。 最后的建议是尝试将Bufferedimage.TYPE_INT_ARGB更改为与您从中获取数组的图像匹配的其他类型,这种类型非常重要,我有一个由0和-1组成的数组,因此我使用了此类型BufferedImage.TYPE_3BYTE_BGR


0

由于这是在SO上标记为ImageIO的最高投票问题之一,我认为即使这个问题很旧,仍然有改进的空间。 :-)

请查看我的GitHub开源imageio项目中的BufferedImageFactory.java类。

有了它,您可以简单地编写:

BufferedImage image = new BufferedImageFactory(image).getBufferedImage();

另一个好处是,这种方法在最坏情况下的性能(时间)与本主题中已有的基于PixelGrabber的示例大致相同。对于大多数常见情况(通常是JPEG),它的速度大约快两倍。无论如何,它使用的内存更少。
作为附加奖励,原始图像的颜色模型和像素布局保持不变,而不是转换为具有默认颜色模型的int ARGB。这可能会节省额外的内存。
(PS:如果有人感兴趣,该工厂还支持子采样、感兴趣区域和进度侦听器。:-)

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