如何从字节数组创建图像?

How do I create an Image from a byte array?

我希望能够将图像的数据作为数组,对其进行修改,然后使用该数组创建修改后的图像。这是我的尝试:

public class Blue {
    public static void main (String [] args) throws AWTException, IOException {
        Robot robot = new Robot ();
        Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
        BufferedImage img = robot.createScreenCapture(new Rectangle(0,0,d.width,d.height));
        int[] pixels = ((DataBufferInt)img.getRaster().getDataBuffer()).getData();
        int[] newPixels = IntStream.range(0,pixels.length).parallel().filter(i ->{
            int p = pixels[i];
            // get red
            int r = (p >> 16) & 0xff;
            // get green
            int g = (p >> 8) & 0xff;
            // get blue
            int b = p & 0xff;
            return b >= 200;
        }).toArray();
        int[] output = new int[pixels.length];
        for(int i = 0; i<newPixels.length; i++) {
            output[newPixels[i]] = 0x0000FF;
        }
        File f = new File("Result.jpg");
        
        ByteBuffer byteBuffer = ByteBuffer.allocate(output.length * 4);   
        for (int i = 0; i< output.length; i++) {
            byteBuffer.putInt(output[i]);
        }
        byte[] array = byteBuffer.array();
        InputStream stream = new ByteArrayInputStream(array);
        BufferedImage image1 = ImageIO.read(stream);
        System.out.println(image1.getWidth());
        ImageIO.write(image1, "png", f);
    }
}

这是它的工作原理。

  1. 机器人对屏幕进行屏幕截图,然后将其存储到 BufferedImage 中。
  2. 图像的数据存储在整数数组中
  3. int 流用于select 对应于足够蓝色像素的所有像素位置
  4. 这些蓝色像素被放置在一个名为输出的数组中,位置与它们的来源相同。但是,数组的其余部分的值为 0。
  5. 为我的修改图像创建了一个目标文件
  6. 我创建了一个长度为输出数组长度 4 倍的字节缓冲区,并将输出数组中的数据放入其中。
  7. 我从缓冲区创建一个字节数组,然后用它创建一个输入流
  8. 最后,我读取流以从中创建图像
  9. 我使用 System.out.println() 从图像中打印一些数据以查看图像是否存在。

第 9 步出现问题。 我不断收到 NullPointerException,这意味着该图像不存在,它为空。 我不明白我做错了什么。 我尝试使用 ByteArrayInputStream 而不是 InputStream,但效果不佳。然后,我想也许前几个字节编码了图像的编码信息,所以我尝试将其复制到输出数组,但这也没有解决问题。我不确定为什么我的字节数组没有变成图像。

你在答案中总结了评论,问题是你有一个“原始”像素数组,并尝试将其传递给 ImageIO.read()ImageIO.read() 读取以定义的文件格式存储的图像,如 PNG、JPEG 或 TIFF(虽然像素数组只是像素,但它不包含有关图像尺寸、颜色模型、压缩等的信息)。如果没有为输入找到插件,该方法将 return null(因此 NullPointerException)。

要从像素阵列创建 BufferedImage,您可以围绕阵列创建光栅,选择合适的颜色模型并使用采用 [=17= 的构造函数创建新的 BufferedImage ] 和 ColorModel 参数。你可以在我的其他答案之一中看到如何做到这一点。

但是,由于您已经 拥有 一个 BufferedImage 并可以访问它的像素,因此重用它会更容易(并且更便宜 CPU/memory 明智) .

您可以用以下代码替换您的代码(有关详细信息和与您的步骤相关的信息,请参阅评论):

public class Blue {
    public static void main (String [] args) throws AWTException, IOException {
        // 1. Create screen capture
        Robot robot = new Robot ();
        Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
        BufferedImage img = robot.createScreenCapture(new Rectangle(0, 0, d.width, d.height));

        // 2: Get backing array
        int[] pixels = ((DataBufferInt) img.getRaster().getDataBuffer()).getData();

        // 3: Find all "sufficiently blue" pixels
        int[] bluePixels = IntStream.range(0, pixels.length).parallel()
                              .filter(i -> pixels[i] & 0xff >= 200).toArray();

        // 4a: Clear all pixels to opaque black
        for (int i = 0; i < pixels.length; i++) {
            pixels[i] = 0xFF000000;
        }

        // 4b: Set all blue pixels to opaque blue
        for (int i = 0; i < bluePixels.length; i++) {
            pixels[bluePixels[i]] = 0xFF0000FF;
        }

        // 5: Make sure the file extension matches the file format for less confusion...  
        File f = new File("result.png");
        // 9: Print & write image (steps 6-8 is not needed)
        System.out.println(img);
        ImageIO.write(img, "png", f);
    }
}