Question

I am trying to convert a direct color model image to a bitonal indexed image (1 bit per pixel) and save the indexed image as a BMP.

As stated on the Java Advanced Imaging API Home Page:

The bit depth of the encoded output is determined by that of the source image.

From looking through the source code of BMPImageWriter, the mechanism of this is the return value of ColorModel#getPixelSize().

Using a scaled-down copy of an image from Wikimedia Commons, I first perform color quantization to get a color lookup table and then error diffusion to apply Floyd–Steinberg dithering:

PlanarImage surrogateImage = PlanarImage.wrapRenderedImage(image);

PlanarImage op = ColorQuantizerDescriptor.create(surrogateImage, ColorQuantizerDescriptor.OCTTREE, 2, null, null, null, null, null);
LookupTableJAI lut = (LookupTableJAI)op.getProperty("LUT");
IndexColorModel cm = new IndexColorModel(1, lut.getByteData()[0].length, lut.getByteData()[0], lut.getByteData()[1], lut.getByteData()[2]);

op = ErrorDiffusionDescriptor.create(surrogateImage, lut, KernelJAI.ERROR_FILTER_FLOYD_STEINBERG, null);

image = op.getAsBufferedImage();

The problem is, image.getColorModel().getPixelSize() returns 8, so the image is saved as an 8bpp bitmap:

Result of color quantization and error diffusion on the sample image.

The size of this image is 167 KiB.

I saw somewhere that one way of passing a color model to error diffusion is to set a JAI.KEY_IMAGE_LAYOUT rendering hint:

ImageLayout layout = new ImageLayout();
layout.setTileWidth(image.getWidth());
layout.setTileHeight(image.getHeight());
layout.setColorModel(cm);
layout.setSampleModel(op.getSampleModel());
RenderingHints rh = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout);
op = ErrorDiffusionDescriptor.create(surrogateImage, lut, KernelJAI.ERROR_FILTER_FLOYD_STEINBERG, rh);

image.getColorModel().getPixelSize() now returns 1, but the resulting image is altered significantly:

Result of color quantization and error diffusion on the sample image passing an ImageLayout.

However, the size of this image is 21 KiB, about what it is when I use MS Paint to convert the sample image to a monochrome bitmap. So, it looks like JAI's BMPImageWriter is using the correct encoding, but if you look closely at the second image, adjacent columns of pixels are eight pixels apart. In fact, you can kind of see the first image, only each column of pixels from the first image is expanded to 8 columns of pixels.

Is this a bug in JAI? Is there something that I can do to collapse these 8-wide columns of pixels to single-column pixels?

Was it helpful?

Solution

This should work with a 24 BPP png:

    String filename = "jEEDL.png";

    PlanarImage image = PlanarImage.wrapRenderedImage(JAI.create("fileload", filename));

    LookupTableJAI lut = new LookupTableJAI(new byte[][] {{(byte)0x00, (byte)0xff}, {(byte)0x00, (byte)0xff}, {(byte)0x00, (byte)0xff}});
    ImageLayout layout = new ImageLayout();
    byte[] map = new byte[] {(byte)0x00, (byte)0xff};
    ColorModel cm = new IndexColorModel(1, 2, map, map, map);
    layout.setColorModel(cm);
    SampleModel sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
            image.getWidth(),
            image.getHeight(),
            1);
    layout.setSampleModel(sm);
    RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout);
    PlanarImage op = ErrorDiffusionDescriptor.create(image, lut, KernelJAI.ERROR_FILTER_FLOYD_STEINBERG, hints);

    BufferedImage dst  = op.getAsBufferedImage();

    JAI.create("filestore", dst, "jEEDL.bmp", "BMP");
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top