I am attempting to increase the quality of an image produced from a BufferedImage. The final aim is for a JPEG to be input (here it is retrieved from a file on the computer), be converted to a grayscale TIFF and then output as a byte array. I have included code to save the final image to the PC so it is easier to discern the problem.
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
import java.awt.image.MultiPixelPackedSampleModel;
import java.awt.image.SampleModel;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.media.jai.ImageLayout;
import javax.media.jai.JAI;
import javax.media.jai.KernelJAI;
import javax.media.jai.LookupTableJAI;
import javax.media.jai.PlanarImage;
import javax.media.jai.operator.ErrorDiffusionDescriptor;
public class ByteConversionService {
private static ByteArrayOutputStream baos;
private static ImageWriter writer;
private static ImageOutputStream ios;
private static ImageWriteParam writeParam;
public static void main(String args[]) {
try {
convertBufferedImageToByteArray();
} catch (Exception e) {
}
}
private static byte[] convertBufferedImageToByteArray()
throws Exception {
byte[] convertedByteArray = null;
resourceSetup();
try {
File file = new File("../proj/src/image.jpg");
BufferedImage image = ImageIo.read(file);
convertImageToTif(image);
createImage(baos);
convertedByteArray = baos.toByteArray();
} finally {
resourceCleanup();
}
return convertedByteArray;
}
private static void resourceSetup() throws Exception {
baos = new ByteArrayOutputStream();
writer = ImageIO.getImageWritersByFormatName(
"tif").next();
ios = ImageIO.createImageOutputStream(baos);
writer.setOutput(ios);
writeParam = writer.getDefaultWriteParam();
writeParamSetUp(writeParam);
}
private static void writeParamSetUp(ImageWriteParam writeParam) {
writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
writeParam.setCompressionType("CCITT T.4");
}
private static void convertImageToTif(BufferedImage image) throws Exception {
try {
BufferedImage blackAndWhiteImage = imageToBlackAndWhite(image);
writeToByteArrayStream(blackAndWhiteImage);
IIOImage iioImage = new IIOImage(blackAndWhiteImage, null, null);
writer.write(null, iioImage, writeParam);
} finally {
image.flush();
}
}
private static BufferedImage imageToBlackAndWhite(BufferedImage image) {
PlanarImage surrogateImage = PlanarImage.wrapRenderedImage(image);
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,
surrogateImage.getWidth(), surrogateImage.getHeight(), 1);
layout.setSampleModel(sm);
RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout);
PlanarImage op = ErrorDiffusionDescriptor.create(surrogateImage, lut,
KernelJAI.ERROR_FILTER_FLOYD_STEINBERG, hints);
BufferedImage blackAndWhiteImage = op.getAsBufferedImage();
return blackAndWhiteImage;
}
private static void writeToByteArrayStream(BufferedImage image) throws Exception {
ImageIO.write(image, "tif", baos);
}
private static void createImage(ByteArrayOutputStream baos) throws Exception {
ByteArrayInputStream bis = new ByteArrayInputStream(baos.toByteArray());
ImageReader reader = (ImageReader) ImageIO.getImageReadersByFormatName(
"tif").next();
Object source = bis;
ImageInputStream iis = ImageIO.createImageInputStream(source);
reader.setInput(iis, true);
ImageReadParam param = reader.getDefaultReadParam();
Image image = reader.read(0, param);
BufferedImage buffered = new BufferedImage(image.getWidth(null),
image.getHeight(null), BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = buffered.createGraphics();
g2.drawImage(image, null, null);
File file = new File("../proj/src/image2.tif");
ImageIO.write(buffered, "tif", file);
}
private static void resourceCleanup() throws Exception {
ios.flush();
ios.close();
baos.flush();
baos.close();
writer.dispose();
}
}
The current issue is that the final image is of low quality - zooming in shows a lot of white space between the pixels composing the picture. My understanding is this is possibly due to the dithering algorithm used (Floyd-Steinberg) so the image is not technically reproduced in grayscale.
I have attempted multiple solutions which I will post in the comments, but with no success. My question is if the final quality can be increased with my current solution or if my conversion to grayscale is flawed and the imageToBlackAndWhite
method is incorrect for my needs.