【问题标题】:How do I create an Image from a byte array?如何从字节数组创建图像?
【发布时间】:2022-01-29 01:01:30
【问题描述】:

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

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 流用于选择与足够蓝色像素相对应的所有像素位置
  4. 这些蓝色像素被放置在一个称为输出的数组中,它们位于相同的位置。但是,数组的其余部分的值为 0。
  5. 为我修改后的图像创建了一个目标文件
  6. 我创建了一个 4 倍于输出数组长度的字节缓冲区,并将输出数组中的数据放入其中。
  7. 我从缓冲区创建一个字节数组,然后用它创建一个输入流
  8. 最后,我读取流并从中创建图像
  9. 我使用 System.out.println() 从图像中打印一些数据以查看图像是否存在。

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

【问题讨论】:

  • 修改第 4 步,将像素值复制回pixels,并清除数组中的所有其他索引。然后跳过第 6、7 和 8 步。最后,修改第 9 步以打印并写入您的原始图像img,它现在包含您修改后的蓝色像素。 ???更快,可能更容易推理并希望是正确的。
  • (您的版本不工作的原因是因为ImageIO.read() 是用于读取定义文件格式的图像,而不是读取像素)。
  • @HaraldK 清除索引是指将它们设置为 0,对吗?
  • @HaraldK 另外,“写下你的原始图像”是什么意思?
  • 1:是的。或者 0xFF000000 如果您的图像包含透明度。 2:“您的原始图像”是指img(屏幕截图)。要编写它,请将最后一行替换为:ImageIO.write(img, "png", f);

标签: java image-processing file-io nullpointerexception


【解决方案1】:

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

要从像素数组创建BufferedImage,您可以在数组周围创建一个光栅,选择合适的颜色模型并使用带有RasterColorModel 参数的构造函数创建一个新的BufferedImage。您可以在我的其他答案之一中看到如何做到这一点。

但是,由于您已经拥有 BufferedImage 并可以访问其像素,因此重用它会更容易(并且在 CPU/内存方面更便宜)。

您可以使用以下代码替换您的代码(有关详细信息以及与您的步骤的关系,请参阅 cmets):

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);
    }
}

【讨论】:

  • 非常感谢,我已经在这个问题上工作了 3 个多小时,甚至可能 5 个。我一直在查看 StackOverflow 和互联网,这是我唯一的解决方案可以理解到足以实现自己。我会赞成你所有的答案,但上次我这样做时,我收到了来自 SO moderation 的警告,并删除了我的选票。不过还是谢谢你。
  • 很高兴它有帮助! ? 是的,不鼓励“连续”向下/向上投票。每当您发现有帮助的问题或答案时,只需点赞即可。编码愉快!
猜你喜欢
  • 1970-01-01
  • 2010-10-12
  • 1970-01-01
  • 2012-08-22
  • 2014-02-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多