I want to be able to take the data of an image as an array, modify it, and then use that array to create a modified image. Here is what I attempted:
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);
}
}
Here is how it works.
- The robot takes a screen capture of the screen, which is then stored into a BufferedImage.
- The data of the image is stored in an integer array
- An int stream is used to select all pixel locations that correspond to sufficiently blue pixels
- These blue pixels are placed in an array called output at the same locations they were taken from. However, the rest of the array has value 0.
- A destination file for my modified image is created
- I create a byte buffer that is 4 times the length of the output array, and data from the output array is placed in it.
- I create a byte array from the buffer then create an input stream with it
- Finally, I read the stream to create an Image from it
- I use System.out.println() to print some data from the image to see if the image exists.
Step 9 is where the problem shows up. I keep getting a NullPointerException, meaning that the image doesn't exist, it is null. I don't understand what I did wrong. I tried using ByteArrayInputStream instead of InputStream, but that doesn't work as well. Then, I thought that maybe the first couple of bytes encode the coding information for the image, so I tried copying that over to the output array, but that didn't solve the problem either. I am not sure why my byte array isn't turning into an image.
CodePudding user response:
Yo summarize the comments in an answer, the problem is that you have an array of "raw" pixels, and try to pass that to ImageIO.read()
. ImageIO.read()
reads images stored in a defined file format, like PNG, JPEG or TIFF (while the pixel array is just pixels, it does not contain information on image dimension, color model, compression etc.).
To create a BufferedImage
from the pixel array, you could create a raster around the array, pick a suitable color model and create a new BufferedImage
using the constructor taking a Raster
and ColorModel
parameter.
However, as you already have a BufferedImage
and access to its pixels, it's much easier (and cheaper CPU/memory wise) to just reuse that.
You can replace your code with the following (see comments for details and relation to your steps):
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...