javabufferedimage

Error with some Image using Raster.getPixel


I ran into the problem yesterday using the BufferedImage Lib, I get an error:

java.lang.ArrayIndexOutOfBoundsException: 3

but only for pictures "PNG" I get from the net, but if I make my own in Paint, it all works. I have tried looking up the problem but can't see where I'm wrong.

package grayandconvert;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.File;
import java.io.IOException;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;


public class JavaCodeProject { // remain if needed Kim
   private final String PATH = "C:\\New folder\\"; //
   private final String graypath = PATH + "oZPX0bbg.png"; // filename for Grayscale pic
   private final String imgpath = PATH + "oZPX0bb.png";      // filename for Orginal pic 
   private final String textpath =PATH + "filename.txt";   // filename for Output textfile
   private final String imgtype = "png";                    // image file type for Grascale "png" "jpg"
    
    
    public static void main(String[] args) 
    {
        JavaCodeProject main = new JavaCodeProject();  //new class for use of the method
        main.grayscale();
        main.convert();
    }

    public void convert()
    {
        try
        {
            BufferedImage image =ImageIO.read(new File(graypath)); // called the gray pic for image
            int[] pixel;                                // int array named pixel
            System.out.print(image.getHeight());
            System.out.print(image.getWidth());
            for (int y = 0; y < image.getHeight(); y++) // outer forloop to control Y axel image.getWidth
            {
                for (int x = 0; x < image.getWidth(); x++) //inner forloop to control X axel
                {
                    pixel = image.getRaster().getPixel(x, y, new int[3]); // gets the RGB data from the buffer
                    if(pixel[0]< 255 && pixel[1]< 255 && pixel[2]< 255)
                    {    
                        System.out.print(" Y");
                        writefile("Y");
                    }
                    else
                    {
                        System.out.print(" N");
                        writefile("N");
                    }
                }
            System.out.print(" L");
            System.out.println("");
            writefile("L");

            }
            System.out.print("S");
            writefile("S");
        }
        
    catch (IOException e) // never used it but it needs to be here
    {
    }
}

    public void writefile(String value)
    {
            String array = value; //named it array. i know right :P

            File file = new File(textpath); //path for new file.txt


            try
            {
                if (!file.exists()) // if file doesn't exist, then this will create it ;)
                {
                    file.createNewFile();
                }

                FileWriter fw = new FileWriter(file.getAbsoluteFile(),true);
                try (BufferedWriter bw = new BufferedWriter(fw)) {
                    bw.write(array,0,array.length());
                }
            }

            catch (IOException e) // if IO exceptions happens this outputs Stacktrace
            {
            }
    }

    public void grayscale()
    {
        BufferedImage img = null;
        
        try
        {
          File f = new File(imgpath); //org pic
          img = ImageIO.read(f);
        }
        catch(IOException e)
        {
          System.out.println(e);
        }

        for(int y = 0; y < img.getHeight(); y++)
        {
            for(int x = 0; x < img.getWidth(); x++)
            {
            int p = img.getRGB(x,y);

            int a = (p>>24)&0xff;
            int r = (p>>16)&0xff;
            int g = (p>>8)&0xff;
            int b = p&0xff;

            //calculate average
            int avg = (r+g+b)/3;

            //replace RGB value with avg
            p = (a<<24) | (avg<<16) | (avg<<8) | avg;

            img.setRGB(x, y, p);
            }
        }

        try
        {
          File f = new File(graypath); //gray pic
          ImageIO.write(img,imgtype,f);
        }
        catch(IOException e)
        {
          System.out.println(e);
        }
    }
}

I get the error

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
at java.awt.image.ComponentSampleModel.getPixel(ComponentSampleModel.java:750)
at java.awt.image.Raster.getPixel(Raster.java:1519)
at grayandconvert.JavaCodeProject.convert(JavaCodeProject.java:41)
at grayandconvert.JavaCodeProject.main(JavaCodeProject.java:23)
128128C:\Users\clipcomet\Desktop\JavaApplication10\nbproject\build-impl.xml:1051:
The following error occurred while executing this line:
C:\Users\clipcomet\Desktop\JavaApplication10\nbproject\build-impl.xml:805: Java returned: 1
BUILD FAILED (total time: 1 second)

I just started programming a bit ago and I know I'm using a Lib I don't know fully but needed to use the BufferedImage, ignoring all the bad code I have can someone tell me why i only get that error on some pics


Solution

  • The reason you get the exception in some cases, is that Raster.getPixel(x, y, pixel) tries to copy all the samples for the pixel at x, y into the pixel array. And you have no control over how many samples per pixel your raster has, if you download random pictures from the net, yet you hardcode the pixel array to 3 elements.

    From the API doc (emphasis mine):

    ArrayIndexOutOfBoundsException - if the coordinates are not in bounds, or if iArray is too small to hold the output.

    Most likely, the images where you get the exception, have 4 components and are RGBA (while the ones from Paint have 3 components, RGB). You will probably get rid of the exception by creating a larger array (ie. new int[4]).

    However, the best way to fix the problem is to not create the array at all yourself, and instead leave that to the getPixel method, like this:

    int[] pixel = null;
    for (y...) {
        for (x...) {
            pixel = raster.getPixel(x, y, pixel);
            ...
        }
    }
    

    This also ensures that the allocation happens only once, which is obviously good for performance.


    That said, you still need to handle the fact that a random image may not have the expected number of samples per pixel. If your input is gray or uses a color map (IndexColoModel), it will only have one sample (and you'll have an ArrayIndexOutOfBoundsExpcetion for your pixel[1] and pixel[2] array accesses). And for the color map case, the sample values is unrelated to the RGB value you see on screen (it's only an index into a lookup table).

    For these reasons, you may find it easier and more intuitive to just use the BufferedImage.getRGB(x, y) method, which always gives you the ARGB values of the pixel as a single packed int sample, in sRGB color space.