BufferedImage getRGB vs Raster getSample

2019-02-07 09:16发布

问题:

I am trying to do some image processing in Java. I used ImageIO library for reading and writing images. I can read the image pixel value in two ways as follows (there might be other methods which do not know).

  1. Using BufferedImage's getRGB method:

    pixel = image.getRGB(x,y);

  2. Using Raster's getSample method:

    WritableRaster raster = image.getRaster();
    pixel = raster.getSample(x,y,0);

What is the difference in the above two approaches?

回答1:

1: The first approach will always return a pixel in int ARGB format, and in the sRGB color space. Regardless of the image's internal representation. This means that unless the image's internal representation is TYPE_INT_ARGB, some conversion has to be done. This is sometimes useful, because it's predictable, but just as often it's quite slow. As an example, color space conversion is quite expensive. Also, if the image has higher precision than 8 bits per sample and/or 4 samples per pixel, precision loss occurs. This may or may not be acceptable, given your use case.

2: The second approach may give you a pixel value, but not in all cases, as it gives you the sample value at (x,y) for the the band 0 (the first band). For TYPE_INT_ARGB this will be the same as the pixel value. For TYPE_BYTE_INDEXED this will be the index to use in the look up table (you need to look it up to get the pixel value). For TYPE_3BYTE_BGR this will give you the blue value only (you need to combine it with the samples in band 1 and 2 to get the full pixel value). Etc. for other types. For samples that are not internally represented as an int, data type conversion occurs (and in rare cases precision loss). It might work for you, but I've never had much use for the getSample(...) methods.

Instead I suggest you look into what I believe to be the fastest way to get at pixel data. That is using the getDataElements method:

Object pixel = null; // pixel initialized on first invocation of getDataElements

for (y) {
    for (x) {
       pixel = raster.getDataElements(x, y, pixel);
    }
}

This will give you the "native" values from the data buffer, without any conversion.

You then need to have special handling for each transfer type (see the DataBuffer class) you want to support, and perhaps a common fallback for non-standard types.

This will have the same "problem" as your approach 2 for pixel values vs normalized RGB values, so you might need to convert/look up "manually".


What approach is better, as always, depends. You have to look at each use case, and decide what's more important. Ease/simplicity, or the best possible performance (or perhaps best quality?).