3

How to create BufferedImage for 32 bits per sample, 3 samples image data

 2 years ago
source link: https://stackoverflow.com/questions/26875429/how-to-create-bufferedimage-for-32-bits-per-sample-3-samples-image-data
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

How to create BufferedImage for 32 bits per sample, 3 samples image data

I am trying to create a BufferedImage from some image data which is a byte array. The image is RGB format with 3 samples per pixel - R, G, and B and 32 bits per sample (for each sample, not all 3 samples).

Now I want to create a BufferedImage from this byte array. This is what I have done:

        ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] {32, 32, 32}, false, false, Transparency.OPAQUE, DataBuffer.TYPE_INT);
        Object tempArray = ArrayUtils.toNBits(bitsPerSample, pixels, samplesPerPixel*imageWidth, endian == IOUtils.BIG_ENDIAN);
        WritableRaster raster = cm.createCompatibleWritableRaster(imageWidth, imageHeight);
        raster.setDataElements(0, 0, imageWidth, imageHeight, tempArray); 
        BufferedImage bi = new BufferedImage(cm, raster, false, null);

The above code works with 24 bits per sample RGB image but not 32 bits per sample. The generated image is garbage which is shown on the right of the image. It is supposed to be like the left side of the image.

Note: the only image reader on my machine which can read this image is ImageMagick. All the others show similar results as the garbage one to the right of the following image.

The ArrayUtils.toNBits() just translates the byte array to int array with correct endianess. I'm sure this one is correct as I have cross checked with other methods to generate the same int array.

I guess the problem might arise from the fact I am using all the 32 bits int to represent the color which would contain negative values. Looks like I need long data type, but there is no DataBuffer type for long.

Instances of ComponentColorModel created with transfer types DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, and DataBuffer.TYPE_INT have pixel sample values which are treated as unsigned integral values.

The above quote is from Java document for ComponentColorModel. This means the 32 bit sample does get treated as unsigned integer value. Then the problem could be somewhere else.

Has any body met similar problem and got a workaround or I may have done some thing wrong here?

Update2: The "real" problem lies in the fact when 32 bit sample is used, the algorithm for the ComponentColorModel will shift 1 to the left 0 times (1<<0) since shift on int is always within 0~31 inclusive. This is not the expected value. To solve this problem (actually shift left 32 times), the only thing needs to be done is change 1 from int to long type as 1L as shown in the fix below.

Update: from the answer by HaraldK and the comments, we have finally agreed that the problem is coming from Java's ComponentColorModel which is not handling 32 bit sample correctly. The proposed fix by HaraldK works for my case too. The following is my version:

import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;

public class Int32ComponentColorModel extends ComponentColorModel {
   //
   public Int32ComponentColorModel(ColorSpace cs, boolean alpha) {
        super(cs, alpha, false, alpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE, DataBuffer.TYPE_INT);
   }

   @Override
   public float[] getNormalizedComponents(Object pixel, float[] normComponents, int normOffset) {
       int numComponents = getNumComponents();

       if (normComponents == null || normComponents.length < numComponents + normOffset) {
           normComponents = new float[numComponents + normOffset];
       }

       switch (transferType) {
           case DataBuffer.TYPE_INT:
               int[] ipixel = (int[]) pixel;
               for (int c = 0, nc = normOffset; c < numComponents; c++, nc++) {
                   normComponents[nc] = ipixel[c] / ((float) ((1L << getComponentSize(c)) - 1));
               }
               break;
           default: // I don't think we can ever come this far. Just in case!!!
               throw new UnsupportedOperationException("This method has not been implemented for transferType " + transferType);
       }

       return normComponents;
   }
}

enter image description here


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK