ImageIO saves back to original size - java

I've been searching for some solutions from the internet yet I still haven't found an answer to my problem.
I've been working or doing a program that would get an image file from my PC then will be edited using Java Graphics to add some text/object/etc. After that, Java ImageIO will save the newly modified image.
So far, I was able to do it nicely but I got a problem about the size of the image. The original image and the modified image didn't have the same size.
The original is a 2x3inches-image while the modified one which supposedly have 2x3inches too sadly got 8x14inches. So, it has gone BIGGER than the original one.
What is the solution/code that would give me an output of 2x3inches-image which will still have a 'nice quality'?
UPDATE:
So, here's the code I used.
public Picture(String filename) {
try {
File file = new File("originalpic.jpg");
image = ImageIO.read(file);
width = image.getWidth();
}
catch (IOException e) {
throw new RuntimeException("Could not open file: " + filename);
}
}
private void write(int id) {
try {
ImageIO.write(image, "jpg", new File("newpic.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
}
2nd UPDATE:
I now know what's the problem of the new image. As I check it from Photoshop, It has a different image resolution compared to the original one. The original has a 300 pixels/inch while the new image has a 72 pixels/inch resolution.
How will I be able to change the resolution using Java?

To set the image resolution (of the JFIF segment), you can probably use the IIOMetatada for JPEG.
Something along the lines of:
public class MetadataTest {
public static void main(String[] args) throws IOException {
BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_3BYTE_BGR);
ImageWriter writer = ImageIO.getImageWritersByFormatName("jpeg").next();
writer.setOutput(ImageIO.createImageOutputStream(new File("foo.jpg")));
ImageWriteParam param = writer.getDefaultWriteParam();
IIOMetadata metadata = writer.getDefaultImageMetadata(ImageTypeSpecifier.createFromRenderedImage(image), param);
IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree(metadata.getNativeMetadataFormatName());
IIOMetadataNode jfif = (IIOMetadataNode) root.getElementsByTagName("app0JFIF").item(0);
jfif.setAttribute("resUnits", "1");
jfif.setAttribute("Xdensity", "300");
jfif.setAttribute("Ydensity", "300");
metadata.mergeTree(metadata.getNativeMetadataFormatName(), root);
writer.write(null, new IIOImage(image, null, metadata), param);
}
}
Note: this code should not be used verbatim, but adding iteration, error handling, stream closing etc, clutters the example too much.
See JPEG Image Metadata DTD for documentation on the metadata format, and what options you can control.

Related

ImageIO write specific tiff

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 );
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.

Copying image and maintain orientation?

Edit
It turns out that the 2nd snippet is actually working but the images in question still show incorrectly in my IDE (IntelliJ IDEA) for some reason.
I am trying read an image, place a watermark and save it in a different folder and the below code does a good job, but it randomly orientates my images.
try {
final Image image = ImageIO.read(file);
int w = ((BufferedImage) image).getWidth();
int h = ((BufferedImage) image).getHeight();
final BufferedImage finalImage =
new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
//Graphics2D g = finalImage.createGraphics();
Graphics2D g = (Graphics2D) finalImage.getGraphics();
g.drawImage(image, 0, 0, null);
g.drawImage(watermark, 0, 0, null);
g.dispose();
File outputFile = new File("watermarked/" + folderName + "/" + file.getName());
outputFile.mkdirs();
ImageIO.write(finalImage, "jpg", outputFile);
} catch (IOException e) {
// TODO: notify client
e.printStackTrace();
}
After some reading I learned that ImageIO.read(...) does not maintain orientation or other "metadata" of the image it is processing. I also read about using the ImageReader to extract the metadata. According to the docs, using ImageReader.readall() should include the metadata in the returned IIOImage but I still end up with some of my images upside down. The below code demonstrates the copying without adding a watermark.
File out = new File("watermarked/" + folderName + "/" + file.getName());
out.getParentFile().mkdirs();
ImageInputStream input = ImageIO.createImageInputStream(file);
ImageOutputStream output = ImageIO.createImageOutputStream(out);
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
ImageReader reader = readers.next();
reader.setInput(input);
IIOImage image = reader.readAll(0, null);
// Should not be needed since readAll should already take care of it.
IIOMetadata metadata = reader.getImageMetadata(0);
image.setMetadata(metadata);
ImageWriter writer = ImageIO.getImageWriter(reader);
writer.setOutput(output);
writer.write(image);
System.out.println(writer.canReplaceImageMetadata(0)); // Returns false
writer.replaceImageMetadata(0, metadata); // Results in a "Unsupported write variant" error.
Both code snippets reside in a method that get passed a folderName as a string and the actual image file.
Edit
The above snippet works and the issue is something else. In my windows folder all my images made with a Galaxy S8 show in the correct orientation. But when I copy them to my project and open them in IntelliJ IDEA some are oriented differently. So I added sanselan as a dependency to get more insight in the meta data of the images and the images that get a different orientation in the IDE do indeed show a different orientation in the metadata. But why aren't they oriented like that in the windows folder, am I missing a metadata field or is windows storing additional data somewhere outside the image metadata?

How to save indexed color PNG in java

How to save image in java as java.awt.image.IndexColorModel PNG? I'm loading indexed color png with ImageIO, manipulate it with Catalino library which unfortunately converts the color space to java.awt.image.DirectColorModel.
Now I want to save the result in the exactly same format as the original image. I tried the following snippet of code.
private static void testIndexedColor() throws IOException {
FastBitmap input = new FastBitmap("test.png");
BufferedImage bi = new BufferedImage(input.getWidth(), input.getHeight(), BufferedImage.TYPE_BYTE_INDEXED);
bi.getGraphics().drawImage(input.toBufferedImage(), 0, 0, null);
ImageIO.write(bi, "PNG", new File("test_result.png"));
}
But in the result weird light gray pixel artifacts appeared in the white background, and PPI decreased . How to correctly convert back to indexed color mode without quality loss and distortion?
Assuming I'm correct about the Catalano framework, you should be able to re-write your methods as this:
private static void testIndexedColor() throws IOException {
BufferedImage bi = ImageIO.read(new File("test.png"));
FastBitmap input = new FastBitmap(bi);
Graphics2D g = bi.createGraphics();
try {
g.drawImage(input.toBufferedImage(), 0, 0, null);
}
finally {
g.dispose(); // Good practice ;-)
}
ImageIO.write(bi, "PNG", new File("test_result.png"));
}
At least you should get away with the fixed palette and the artifacts.
However, this will likely still modify the PPI (but this won't affect the pixels). And even in some cases the image might be written back as a non-palette PNG.
Update: It seems the PNGImageWriter (through the PNGMetadata) actually re-writes an IndexColorModel containing a perfect grayscale, to a grayscale PNG by default. This is normally a good idea, as you reduce file size by not writing the PLTE chunk. You should be able to get around this, by passing the metadata from the original, along with the image pixel data, to instruct the writer to keep the IndexColorModel (ie. write PLTE chunk):
private static void testIndexedColor() throws IOException {
File in = new File("test.png");
File out new File("test_result.png");
try (ImageInputStream input = ImageIO.createImageInputStream(in);
ImageOutputStream output = ImageIO.createImageOutputStream(out)) {
ImageReader reader = ImageIO.getImageReaders(input).next(); // Will fail if no reader
reader.setInput(input);
ImageWriter writer = ImageIO.getImageWriter(reader); // Will obtain a writer that understands the metadata from the reader
writer.setOutput(output); // Will fail if no writer
// Now, the important part, we'll read the pixel AND metadata all in one go
IIOImage image = reader.readAll(0, null); // PNGs only have a single image, so index 0 is safe
// You can now access and modify the image data using:
BufferedImage bi = (BufferedImage) image.getRenderedImage();
FastBitmap fb = new FastBitmap(bi);
// ...do stuff...
Graphics2D g = bi.createGraphics();
try {
g.drawImage(fb.toBufferedImage(), 0, 0, null);
}
finally {
g.dispose();
}
// Write pixel and metadata back
writer.write(null, image, writer.getDefaultWriteParam());
}
}
This should (as a bonus) also keep your PPI as-is.
PS: For production code, you also want to dispose() of the reader and writer above, but I left it out to keep focus and avoid further discussion on try/finally. ;-)

How to Read JPEG image into BufferedImage object using Java

This is not a duplicated question here, because I've been searching for the solution for a long time in Google and StackOverflow, and still cannot find a solution.
I have these two images:
These are two images from the same website with same prefix and same format. The only difference is the size: the first is larger, while the second is smaller.
I downloaded both of the images to local folder and used Java to read them into BufferedImage objects. However, when I outputted the BufferedImages to local files, I found that the first image was almost red, while the second was normal(same as original). What's wrong with my code?
byte[] rawData = getRawBytesFromFile(imageFilePath); // some code to read raw bytes from image file
ImageInputStream iis = ImageIO.createImageInputStream(new ByteArrayInputStream(rawData));
BufferedImage img = ImageIO.read(iis);
FileOutputStream fos = new FileOutputStream(outputImagePath, false);
ImageIO.write(img, "JPEG", fos);
fos.flush();
fos.close();
PS: I used GIMP to open the first image and detected that the Color Mode is 'sRGB', no alpha or other stuff.
This is apparently a know bug, I saw several suggestions (this is one) that suggest using Toolkit#createImage instead, which apparently ignores the color model.
I tested this and it seems to work fine.
public class TestImageIO01 {
public static void main(String[] args) {
try {
Image in = Toolkit.getDefaultToolkit().createImage("C:\\hold\\test\\13652375852388.jpg");
JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(in)), "Yeah", JOptionPane.INFORMATION_MESSAGE);
BufferedImage out = new BufferedImage(in.getWidth(null), in.getHeight(null), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = out.createGraphics();
g2d.drawImage(in, 0, 0, null);
g2d.dispose();
ImageIO.write(out, "jpg", new File("C:\\hold\\test\\Test01.jpg"));
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
nb- I used the JOptionPane to verify the incoming image. When using ImageIO it comes in with the red tinge, with Toolkit it looks fine.
Updated
And an explantation
I checked your code in netbeans and faced with your problem, then I changed the code as below that has no problem:
public class Test {
public static void main(String args[]) throws IOException {
byte[] rawData = getRawBytesFromFile(imageFilePath); // some code to read raw bytes from image file
// ImageInputStream iis = ImageIO.createImageInputStream(new ByteArrayInputStream(rawData));
// BufferedImage img = ImageIO.read(iis);
FileOutputStream fos = new FileOutputStream(outputImagePath, false);
fos.write(rawData);
// ImageIO.write(img, "JPEG", fos);
fos.flush();
fos.close();
}
private static byte[] getRawBytesFromFile(String path) throws FileNotFoundException, IOException {
byte[] image;
File file = new File(path);
image = new byte[(int)file.length()];
FileInputStream fileInputStream = new FileInputStream(file);
fileInputStream.read(image);
return image;
}
}
Please check it and inform me of the result ;)
Good Luck
I suspect this solution might work just fine in the original poster's case.
String fileName = imageFilePath;
File inFile = new File(fileName);
BufferedImage img = ImageIO.read(inFile);
...
Best,

How to write an 8 bit grey scale tiff with Java?

I am trying to write a grey scale image to an TIFF file using Sanselan. Obviously would like the save the data to be 8 bit grey scale file but somehow I always end up with a 24 bit colour file.
I have searched for file formats in general and ExifTagConstants.EXIF_TAG_PIXEL_FORMAT in particular but was unable to find anything helpful.
I also considered colour profiles but there seem to be none for grey scale images. But then they all have wacky names — I might just have overlooked the right one.
Or do I have to use a different library?
Here the code I use (without business logic and experimental stuff:
Test_Only_Sanselan:
try
{
final Map <String, Object> parameter = new HashMap <>();
parameter.put(org.apache.sanselan.SanselanConstants.PARAM_KEY_COMPRESSION,
org.apache.sanselan.formats.tiff.constants.TiffConstants.TIFF_COMPRESSION_UNCOMPRESSED);
final java.io.OutputStream output = new java.io.FileOutputStream("Test-1.tiff");
org.apache.sanselan.Sanselan.writeImage(image, output,
org.apache.sanselan.ImageFormat.IMAGE_FORMAT_TIFF, parameter);
output.close();
}
catch (final IOException exception)
{
LdfImage.log.info("! Could not create tiff image.", exception);
}
catch (final org.apache.sanselan.ImageWriteException exception)
{
LdfImage.log.info("! Could not create tiff image.", exception);
}
I wonder if I need to add a special parameter. But I have not found a useful parameter yet.
The following is a test of the Java 7 image writer which creates a correct grey scale image. But as PNG and not TIFF:
Test_Only_Java_7:
try
{
final java.util.Iterator imageWriters = javax.imageio.ImageIO.getImageWritersByMIMEType ("image/png");
final javax.imageio.ImageWriter imageWriter = (javax.imageio.ImageWriter) imageWriters.next();
final java.io.File file = new java.io.File("Test-2.png");
final javax.imageio.stream.ImageOutputStream output = javax.imageio.ImageIO.createImageOutputStream(file);
imageWriter.setOutput(output);
imageWriter.write(image);
}
catch (final IOException exception)
{
LdfImage.log.info("! Could not create tiff image.", exception);
}
Is there a TIFF pug-in for imageio?
I'm not familiar with Sanselan but I see there is a ImageInfo.COLOR_TYPE_GRAYSCALE constant, I'm guessing it could be used in the params parameter map given to writeImage.
I didn't find the javadoc for Sanselan, their link is broken, would you have one more up-to-date?
If you want to use JAI you can get your inspiration here:
void get8bitImage(byte[] data, int width, int height) {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
byte[] rasterData = ((DataBufferByte)image.getRaster().getDataBuffer()).getData();
System.arraycopy(pixels, 0, rasterData, 0, pixels.length); // A LOT faster than 'setData()'
}
void main(String[] argv) {
TIFFEncodeParam ep = new TIFFEncodeParam();
FileOutputStream out = new FileOutputStream("c:\\test.tiff");
BufferedImage img = get8bitImage(myData);
ImageEncoder encoder = ImageCodec.createImageEncoder("tiff", out, ep);
encoder.encode(img);
}
Be sure that the BufferedImage has BufferedImage.TYPE_BYTE_GRAY as imageType.

Categories

Resources