9

What is the fastest way to get the RGB value of each pixel of a BufferedImage?

Right now I am getting the RGB values using two for loops as shown in the code below, but it took too long to get those values as the nested loop runs a total of 479999 times for my image. If I use a 16-bit image this number would be even higher!

I need a faster way to get the pixel values.

Here is the code I am currently trying to work with:

BufferedImage bi=ImageIO.read(new File("C:\\images\\Sunset.jpg"));

int countloop=0;  

for (int x = 0; x <bi.getWidth(); x++) {
    for (int y = 0; y < bi.getHeight(); y++) {
        Color c = new Color(bi.getRGB(x, y));
        System.out.println("red=="+c.getRed()+" green=="+c.getGreen()+"    blue=="+c.getBlue()+"  countloop="+countloop++);                                                                                                                                                  
    }
}
Anil
  • 655
  • 1
  • 11
  • 25
Jony
  • 1,035
  • 7
  • 17
  • 43
  • `it can even rise when i will use 16 bit image` - Why would the number of iterations depend on the bits per pixel? And what's the use case for that? Note that creating `Color` objects as well as printing to the console takes a while. If you want to access all 479999 pixels you can't get rid of a loop (you could merge them to one but that shouldn't make that big a difference). – Thomas Apr 10 '12 at 12:08
  • 2
    The output slows the whole looping down. Try it without. – Matthias Bruns Apr 10 '12 at 12:09
  • 2
    It might go a lot faster without the println – Romain Hippeau Apr 10 '12 at 12:11
  • Look at here http://stackoverflow.com/questions/4348613/working-in-a-bufferedimages-int-pixels-array – dash1e Apr 10 '12 at 12:11
  • 2
    Taking out the `System.out.println()` calls will make it substantially faster. You could possibly define `c` outside of the loop, and even avoid instantiating a `Color` if you needed to, but it's probably not necessary. – beerbajay Apr 10 '12 at 12:11
  • @beerbajay Well i am using System.out.println() just to check and letter on i will remove it but the thing is i have to apply some equation on this pixel values which may take even more time – Jony Apr 10 '12 at 12:17
  • @Thomas well it rises because height and width of 16 bit image is more as it contains 2^16 pixels...... – Jony Apr 14 '12 at 11:49
  • @AshishDonvir the height and width of an image normally doesn't depend on the bits per pixels. A 800x600 pixel image has 480000 pixels whether it's 8-, 16-, 24- or 32-bit (or even more). Only the number of bytes rises but not the number of pixels which you are looping over. If the width depends on the bits per pixel in your case then please explain why. – Thomas Apr 14 '12 at 15:53

5 Answers5

15

I don't know if this might help and I haven't tested it yet but you can get the rgb values this way:

BufferedImage bi=ImageIO.read(new File("C:\\images\\Sunset.jpg"));
int[] pixel;

for (int y = 0; y < bi.getHeight(); y++) {
    for (int x = 0; x < bi.getWidth(); x++) {
        pixel = bi.getRaster().getPixel(x, y, new int[3]);
        System.out.println(pixel[0] + " - " + pixel[1] + " - " + pixel[2] + " - " + (bi.getWidth() * y + x));
    }
}

As you can see you don't have to initialize a new Color inside the loop. I also inverted the width/height loops as suggested by onemasse to retrieve the counter from data I already have.

mastaH
  • 1,234
  • 3
  • 15
  • 30
7

By changing from a bunch of in individual getRGB's to one big getRGB to copy the entire image into an array, the execution time dropped by an order of magnitude from 33,000 milliseconds to 3,200 milliseconds, while the time create the array was only 31 milliseconds.

No doubt about it, one big read into an array and direct indexing of the array is much faster than many individual reads.

Performance difference appears related to the use of a breakpoint statement at the end of the class. While the breakpoint was outside the loop, every line of code within the class appears to be tested for the breakpoint. Changing to individual gets does NOT improve speed.

Since the code is still correct, the remainder of the answer may still be of use.

Old read statement

colorRed=new Color(bi.getRGB(x,y)).getRed();

Read statement to copy an bit image into an array

int[] rgbData = bi.getRGB(0,0, bi.getWidth(), bi.getHeight(), 
                null, 0,bi.getWidth());        

The getRGB into an array puts all 3 color values into a single array element, so individual colors must be extracted by rotating and an "and". The y coordinate must be multiplied by the width of the image.

Code to read individual colors out of the array

colorRed=(rgbData[(y*bi.getWidth())+x] >> 16) & 0xFF; 

colorGreen=(rgbData[(y*bi.getWidth())+x] >> 8) & 0xFF; 

colorBlue=(rgbData[(y*bi.getWidth())+x]) & 0xFF; 
Fred F
  • 1,027
  • 1
  • 9
  • 18
2

Did you try BufferedImage.getRGB(int, int ,int ,int, int[] , int , int)?

Something like:

int[] rgb = bi.getRGB(0,0, bi.getWidth(), bi.getHeight(), new int[bi.getWidth() * bi.getHeight(), bi.getWidth()])

Didn't try, so not sure if it's faster.

edit Having looked @ the code, it probably isn't, but worth a shot.

soulcheck
  • 36,297
  • 6
  • 91
  • 90
2

You should loop the rows in the outer loop and the columns in the inner. That way you'll avoid cache misses.

onemasse
  • 6,514
  • 8
  • 32
  • 37
0

I found a solution here https://alvinalexander.com/blog/post/java/getting-rgb-values-for-each-pixel-in-image-using-java-bufferedi

BufferedImage bi = ImageIO.read(new File("C:\\images\\Sunset.jpg"));

for (int x = 0; x < bi.getWidth(); x++) {
    for (int y = 0; y < bi.getHeight(); y++) {
        int pixel = bi.getRGB(x, y);
        int red = (pixel >> 16) & 0xff;
        int green = (pixel >> 8) & 0xff;
        int blue = (pixel) & 0xff;
        System.out.println("red: " + red + ", green: " + green + ", blue: " + blue);                                                                                                                                                  
    }
}
pipi
  • 21
  • 1
  • 3