Reputation: 694
I try to convert (with ImageIO -> https://github.com/haraldk/TwelveMonkeys) a image into a specific tiff, like imagemagick does. I have a input image and want to write a specific tiff with following:
PLANAR_CONFIGURATION = 1
SAMPLES_PER_PIXEL = 1
BITS_PER_SAMPLE = 1
Y_RESOLUTION = 196
X_RESOLUTION = 204
IMAGE_WIDTH = 1728
Any idea how to render the inputstream? Currently the image is just converted into tiff.
BufferedImage image = ImageIO.read(inputstream)
ImageIO.write( image, "tiff", outputstream );
Upvotes: 2
Views: 4243
Reputation: 27094
As @fmw42 says, making the image 1-bit you have do yourself. The TIFFImageWriter
plugin will write the image it is passed as-is. Fortunately, this is not difficult to do.
Here's an easy (but not very sophisticated) way to convert the image to binary:
private static BufferedImage toBinary(BufferedImage original) {
if (original.getType() == BufferedImage.TYPE_BYTE_BINARY) {
return original;
}
// Quick and unsophisticated way to convert to B/W binary, using default dither and threshold (fixed, 50% I think)
BufferedImage image = new BufferedImage(original.getWidth(), original.getHeight(), BufferedImage.TYPE_BYTE_BINARY);
Graphics2D g = image.createGraphics();
try {
g.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g.setComposite(AlphaComposite.Src);
g.drawImage(original, 0, 0, null);
}
finally {
g.dispose();
}
return image;
}
I'll leave it as an exercise to write more advanced solutions, using adaptive thresholding, error-diffusion dithering etc.
Now you can use the following code, and you're nearly there:
public static void main(String[] args) throws IOException {
BufferedImage original = ImageIO.read(new File(args[0]));
ImageIO.write(toBinary(original), "TIFF", new File("out.tif"));
}
Unfortunately, this will not set the X and Y Resolution tags. If you need that as well, you have to dig a little deeper into the ImageIO API, and figure out how to use the metadata to control the output. Note that only some of the values in the metadata may be set in this way. Other values will be computed from the image data passed in, and some may be filled in with default values by the writer.
You can use the following code (the toBinary
method is the same as above):
public static void main(String[] args) throws IOException {
BufferedImage original = ImageIO.read(new File(args[0]));
BufferedImage image = toBinary(original);
ImageWriter writer = ImageIO.getImageWritersByFormatName("TIFF").next();
try (ImageOutputStream stream = ImageIO.createImageOutputStream(new File("out.tif"))) {
// You may use the param to control compression
ImageWriteParam param = writer.getDefaultWriteParam();
IIOMetadata metadata = writer.getDefaultImageMetadata(ImageTypeSpecifier.createFromRenderedImage(image), param);
Node root = metadata.getAsTree("com_sun_media_imageio_plugins_tiff_image_1.0"); // "javax_imageio_tiff_image_1.0" will work in later versions
Node ifd = root.getFirstChild();
// Add X and Y resolution tags
ifd.appendChild(createResTag("282", "XResolution", "204/1"));
ifd.appendChild(createResTag("283", "YResolution", "196/1"));
// Merge changes back to metadata
metadata.mergeTree("com_sun_media_imageio_plugins_tiff_image_1.0", root);
// Write full image, with metadata
writer.setOutput(stream);
writer.write(null, new IIOImage(image, null, metadata), param);
}
finally {
writer.dispose();
}
}
private static IIOMetadataNode createResTag(String tagNumber, String tagName, String tagValue) {
IIOMetadataNode res = new IIOMetadataNode("TIFFField");
res.setAttribute("number", tagNumber);
res.setAttribute("name", tagName); // Tag name is optional
IIOMetadataNode value = new IIOMetadataNode("TIFFRational");
value.setAttribute("value", tagValue);
IIOMetadataNode rationals = new IIOMetadataNode("TIFFRationals");
rationals.appendChild(value);
res.appendChild(rationals);
return res;
}
PS: The TwelveMonkeys TIFF plugin currently don't write PlanarConfiguration: 1
, as this is the default value, and there's no way to force it. But it should not matter, as all compliant TIFF software must use the default value in this case.
Upvotes: 2