0

Im having trouble getting pixel data.

My program takes screenshots, every loop it will store the previous screenshot.

My goal is to do a comparison at the pixel level between the current screenshot and the old one.

Ive ran this code which tells me what format the screenshots are in: System.out.println(image.getType());

The output of this (for my program) is 1 meaning its a BufferedImage.TYPE_INT_RGB

From what ive read, the types determine what order the pixel values are in the byte array.

I'm using this code to convert my Buffered image to a byte array (The buffered image is created using awt.Robot class):

public byte[] convertToByteArray(BufferedImage img){
        byte[] imageInByte = null;
        try {



            // convert BufferedImage to byte array
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ImageIO.write(img, "png", baos);
            baos.flush();
            imageInByte = baos.toByteArray();
            baos.close();

        } catch (IOException ex) {
            Logger.getLogger(OverlayWindow.class.getName()).log(Level.SEVERE, null, ex);
        }
        return imageInByte;
}

Finally i use a comparison method to check the byte array. For now this only prints the color values of the array:

  final byte[] pixels = convertToByteArray(image);
  final int pixelLength = 3;
        for (int pixel = 0, row = 0, col = 0; pixel < 1; pixel += pixelLength) {
        int argb = 0;
        argb += -16777216; // 255 alpha
        argb += ((int) pixels[pixel] & 0xff); // blue
        argb += (((int) pixels[pixel + 1] & 0xff) << 8); // green
        argb += (((int) pixels[pixel + 2] & 0xff) << 16); // red


        int r = (argb >> 16) & 0xff, g = (argb >> 8)& 0xff, b = argb & 0xff;
        System.out.println("R = " + r);
        System.out.println("G = " + g);
        System.out.println("B = " +  b);

        col++;
        if (col == width) {
           col = 0;
           row++;
        }




     }

My issue with this code is that even though i take a screenshot of a solid color, the pixel values are all over the place. Im expecting each pixel to have the same color values.

-Edit-

I'm avoiding using getRGB for performance reasons. Iterating through two large images calling getRGB each time is very costly in my application.

user3712476
  • 118
  • 1
  • 11
  • we are not gonna know what happens - do you have a complete running program with all the details that you do?? – gpasch Nov 16 '16 at 01:23
  • First of all, `pixels` isn't really pixel values, it's the bytes of a compressed PNG file... So that won't work no matter. Second, if you already have the pixel values as separate R, G and B samples (which your code assumes), why try to pack them into an 32 bit ARGB format and unpack them again? I suggest first just using `int argb = image.getRGB(x, y);` and then splitting into R, G and B as you already do. Then optimise from there, if it isn't fast enough... – Harald K Nov 16 '16 at 13:34
  • I updated my question. So the byte array is the problem? I tried using this to get the byte array initially: `((DataBufferByte) bufferedImage.getData().getDataBuffer()).getData();` this throws the following error when i try to run it: `java.lang.ClassCastException: java.awt.image.DataBufferInt cannot be cast to java.awt.image.DataBufferByte` so what would be the proper way to get the byte array for pixel comparison? – user3712476 Nov 16 '16 at 15:08

1 Answers1

2

The easiest way to access the pixels values in a BufferedImage is to use the Raster:

BufferedImage image = ...
for (int y=0 ; y < image.getHeight() ; y++)
    for (int x=0 ; x < image.getWidth() ; x++)
        for (int c=0 ; c < image.getRaster().getNumBands() ; c++)
            final int value = image.getRaster().getSample(x, y, c) ; // Returns the value of the channel C of the pixel (x,y)

The raster will take care of the encoding for you, making it the easiest way to access the pixel values. However, the fastest way is to use the DataBuffer, but then you have to manage all the encodings.

/* This method takes a BufferedImage encoded with TYPE_INT_ARGB and copies the pixel values into an image encoded with TYPE_4BYTE_ABGR.*/
public static void IntToByte(BufferedImage source, BufferedImage result)
    {
    final byte[] bb = ((DataBufferByte)result.getRaster().getDataBuffer()).getData() ;
    final int[] ib  = ((DataBufferInt)source.getRaster().getDataBuffer()).getData() ;

    switch ( source.getType() )
        {
        case BufferedImage.TYPE_INT_ARGB :
            for (int i=0, b=0 ; i < ib.length ; i++, b+=4)
                {
                int p   = ib[i] ;
                bb[b]   = (byte)((p & 0xFF000000) >> 24) ;
                bb[b+3] = (byte)((p & 0xFF0000) >> 16) ;
                bb[b+2] = (byte)((p & 0xFF00) >> 8) ;
                bb[b+1] = (byte)( p & 0xFF) ;
                }
            break ;
        // Many other case to manage...
        }
    }
FiReTiTi
  • 5,597
  • 12
  • 30
  • 58
  • is `BufferedImage.TYPE_INT_RGB` the same type of formatting as `BufferedImage.TYPE_INT_ARGB`? Also i dont need to encode it into another image but i can see where you show how to get pixel values, my only issue with this is `((DataBufferByte)result.getRaster().getDataBuffer()).getData()` throws an exception : `java.lang.ClassCastException: java.awt.image.DataBufferInt cannot be cast to java.awt.image.DataBufferByte` – user3712476 Nov 16 '16 at 18:38
  • BufferedImage.TYPE_INT_RGB and BufferedImage.TYPE_INT_ARGB is the same encoding into a single int, it's just the Alpha channel that is present or not. – FiReTiTi Nov 16 '16 at 18:56
  • The exception is because you want to cast a DataBufferInt into a DataBufferByte. So you try to access an image encoded with int, by casting it into byte. As I said, when using the DataBuffer, you have to manage ALL the encodings. – FiReTiTi Nov 16 '16 at 18:58
  • hmm ok so i tried using DataBufferInt instead of Byte, this doesnt throw the exception but im not sure how to manipulate this array, is there something you can refer me, to help me get the pixel data from this? – user3712476 Nov 16 '16 at 19:52
  • Yes, my function IntToByte, I read the source image that is a DataBufferInt, and I extract the value of all the pixels. – FiReTiTi Nov 16 '16 at 20:09
  • Ahhh i see now, i was casting from int to byte and back to int when i didnt really have to. Thanks for helping me understand, good answer – user3712476 Nov 16 '16 at 20:37