Is there a way to control which pixels go black (a threshold) in a TIFF-PNG conversion with bit depth=1 (black&white)

StackOverflow https://stackoverflow.com/questions/19571771

  •  01-07-2022
  •  | 
  •  

Question

Well, in the proccess of converting an image from TIFF to PNG format, I really want the final image to be the smallest possible so I am setting the bit depth to 1 (black&white), I am working with images of signatures. My problem is that I am "losing" some pixels because in the original image were "too white" and the process of conversion is ignoring them.

What I want is a way to tell the PNG encoder to apply a threshold in the image, for example if the gray level is greater than 240 then it is white, if not is black. Or maybe any other way in wich the image don't lose much of the pixels, because the image is a signature and I have to show it in a browser. Here is a sample:

sample signature

This is the code I use to convert a image from TIFF to PNG format:

public byte[]tiffToPng(byte[]tiffBytes) throws IOException {

    SeekableStream stream = new ByteArraySeekableStream(tiffBytes);
    ImageDecoder decoder = ImageCodec.createImageDecoder("tiff", stream, null);
    RenderedImage renderedImage = decoder.decodeAsRenderedImage(0);

    PNGEncodeParam pngEncodeParam = PNGEncodeParam.getDefaultEncodeParam(renderedImage);
    pngEncodeParam.setBitDepth(1);

    ByteArrayOutputStream pngBytesStream = new ByteArrayOutputStream();
    ImageEncoder encoder = ImageCodec.createImageEncoder("png", pngBytesStream, pngEncodeParam);
    encoder.encode(renderedImage);
    pngBytesStream.flush();
    return pngBytesStream.toByteArray();
}
Was it helpful?

Solution

Well, after some time, I first made the conversion of the RenderedImage to a BufferedImage, then a loop over the RGB values and the thresholding is done. Finally, I encoded the resulting image into a PNG (bit depth=1). This is it:

public byte[]tiffToPng(byte[]tiffBytes) throws IOException {
    SeekableStream stream = new ByteArraySeekableStream(tiffBytes);
    ImageDecoder decoder = ImageCodec.createImageDecoder("tiff", stream, null);
    RenderedImage tiffRenderedImage = decoder.decodeAsRenderedImage(0);

    BufferedImage tiffBufferedImage = toBufferedImage(tiffRenderedImage);
    for (int y = 0; y < tiffBufferedImage.getHeight(); y++) {
        for (int x = 0; x < tiffBufferedImage.getWidth(); x++) {
            int rgb = tiffBufferedImage.getRGB(x, y);
            int a = (rgb>>24)&0xFF;
            int r = (rgb>>16)&0xFF;
            int g = (rgb>>8)&0xFF;
            int b = (rgb>>0)&0xFF;
            if(a == 0xFF && r == g && g == b) {
                if(r < 254) {
                    tiffBufferedImage.setRGB(x, y, 0xFF000000);
                } else {
                    tiffBufferedImage.setRGB(x, y, 0xFFFFFFFF);
                }
            }
        }
    }

    PNGEncodeParam pngEncodeParam = PNGEncodeParam.getDefaultEncodeParam(tiffBufferedImage);
    pngEncodeParam.setBitDepth(1);
    ByteArrayOutputStream pngBytesStream = new ByteArrayOutputStream();
    ImageEncoder encoder = ImageCodec.createImageEncoder("png", pngBytesStream, pngEncodeParam);
    encoder.encode(tiffBufferedImage);
    pngBytesStream.flush();
    return pngBytesStream.toByteArray();
}

public BufferedImage toBufferedImage(RenderedImage img) {
        if (img instanceof BufferedImage) {
            return (BufferedImage) img;
        }
        ColorModel cm = img.getColorModel();
        int width = img.getWidth();
        int height = img.getHeight();
        WritableRaster raster = cm.createCompatibleWritableRaster(width, height);
        boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
        Hashtable properties = new Hashtable();
        String[] keys = img.getPropertyNames();
        if (keys != null) {
            for (int i = 0; i < keys.length; i++) {
                properties.put(keys[i], img.getProperty(keys[i]));
            }
        }
        BufferedImage result = new BufferedImage(cm, raster, isAlphaPremultiplied, properties);
        img.copyData(raster);
        return result;
    }

And this is the final image:

signature succesfully converted PNG 1 bit depth

OTHER TIPS

What you want to do is more a image preprocessing thing, I wouldn't expect an encoder to allow for that. Just preprocess the image (brightness/contrast and/or gamma) and save.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top