Meta data is missing after creating BufferedImage - java

I am writing meta data inside PNG image using below code:
public synchronized static byte[] writeMetaDataInPNGImage(BufferedImage buffImg,
String key, String value)
{
byte[][] image = null;
try
{
ImageWriter writer = ImageIO.getImageWritersByFormatName("png").next();
ImageWriteParam writeParam = writer.getDefaultWriteParam();
ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier
.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
// adding metadata
IIOMetadata metadata = writer.getDefaultImageMetadata(typeSpecifier, writeParam);
IIOMetadataNode textEntry = new IIOMetadataNode("tEXtEntry");
textEntry.setAttribute("keyword", key);
textEntry.setAttribute("value", value);
IIOMetadataNode text = new IIOMetadataNode("tEXt");
text.appendChild(textEntry);
IIOMetadataNode root = new IIOMetadataNode("javax_imageio_png_1.0");
root.appendChild(text);
int width = buffImg.getWidth();
int height = buffImg.getHeight();
metadata.mergeTree("javax_imageio_png_1.0", root);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageOutputStream stream = ImageIO.createImageOutputStream(baos);
writer.setOutput(stream);
writer.write(metadata, new IIOImage(buffImg, null, metadata), writeParam);
stream.close();
return baos.toByteArray();
// return ImageIO.read(new ByteArrayInputStream(baos.toByteArray()));
}
catch (Exception e)
{
System.out.println("Exception while writing \t " + e.getMessage() + " :: "
+ e.getStackTrace());
e.printStackTrace();
}
return null;
}
After writing meta data I am returning byte array with meta data and image data.
byte[] pngjdata = writeMetaDataInPNGImage(img.getAsBufferedImage(),"key", "hello");
If I save the image with pngjdata[] I am able to see the meta data inside image, but if I create BufferedImage from this byte array and save it I am not seeing written meta data.
InputStream in1 = new ByteArrayInputStream(pngjdata);
BufferedImage brImage = ImageIO.read(in1);
Why is brImage not having the meta data written by me?

Why is brImage not having the meta data written by me?
A BufferedImage does not contain the meta data you are looking for. The BufferedImage instance just represents pixel values, color model, sample model etc., or in other words the data necessary to display the image. There's also no API to set/get meta data (but it could be you are confused by the getProperty(name) and related methods that is inherited from the legacy AWT Image API).
Meta data in the ImageIO API is represented as instances of IIOMetadata and it's various DOM-like forms you can obtain via the getAsTree(..) method (like "javax_imageio_png_1.0" for PNG, or the "standard" "javax_imageio_1.0" format).
To keep both pixel data and meta data organized together in your application, you can use the IIOImage class.
You can read both pixel data and meta data together, using the ImageReader.readAll(index, param) method. You can write both at the same time, using ImageWriter.write(null, iioImage, param), like you already do above (just note that the first param to the write method is the stream meta data, which is different from the image meta data, for PNG just pass null).

Related

How to reduce size of a multipart file in java

I have Java Spring MVC application in which there is an option to upload an image and save to the server. i have the following method:
#RequestMapping(value = "/uploaddocimagecontentsubmit", method = RequestMethod.POST)
public String createUpdateFileImageContentSubmit(#RequestParam("file") MultipartFile file, ModelMap model)
{
//methods to handle file upload
}
I am now trying to reduce the size of the image refering the following:
increasing-resolution-and-reducing-size-of-an-image-in-java and decrease-image-resolution-in-java
The problem I am facing is that in the above examples, we are dealing with java.io.File Objects which are saved to a specified location. I dont want to save the image. Is there any way that I can use something similar to compress my Multipart Image file and continue with the upload.
Why don't you resize it on the client before upload? That will save some bandwidth
BlueImp JQuery Upload can do this
It was my first time taking a deep dive into the ImageIO package. I came across the MemoryCacheImageOutputStream, which allows you to write an image output stream to an output stream, i.e. ByteArrayOutputStream. From there, The data can be retrieved using toByteArray() and toString(), after compression. I used toByteArray, as I am storing images to postgresql and it stores the images as a byte array. I know this is late, but I hope it helps someone.
private byte[] compressImage(MultipartFile mpFile) {
float quality = 0.3f;
String imageName = mpFile.getOriginalFilename();
String imageExtension = imageName.substring(imageName.lastIndexOf(".") + 1);
// Returns an Iterator containing all currently registered ImageWriters that claim to be able to encode the named format.
// You don't have to register one yourself; some are provided.
ImageWriter imageWriter = ImageIO.getImageWritersByFormatName(imageExtension).next();
ImageWriteParam imageWriteParam = imageWriter.getDefaultWriteParam();
imageWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); // Check the api value that suites your needs.
// A compression quality setting of 0.0 is most generically interpreted as "high compression is important,"
// while a setting of 1.0 is most generically interpreted as "high image quality is important."
imageWriteParam.setCompressionQuality(quality);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// MemoryCacheImageOutputStream: An implementation of ImageOutputStream that writes its output to a regular
// OutputStream, i.e. the ByteArrayOutputStream.
ImageOutputStream imageOutputStream = new MemoryCacheImageOutputStream(baos);
// Sets the destination to the given ImageOutputStream or other Object.
imageWriter.setOutput(imageOutputStream);
BufferedImage originalImage = null;
try (InputStream inputStream = mpFile.getInputStream()) {
originalImage = ImageIO.read(inputStream);
} catch (IOException e) {
String info = String.format("compressImage - bufferedImage (file %s)- IOException - message: %s ", imageName, e.getMessage());
logger.error(info);
return baos.toByteArray();
}
IIOImage image = new IIOImage(originalImage, null, null);
try {
imageWriter.write(null, image, imageWriteParam);
} catch (IOException e) {
String info = String.format("compressImage - imageWriter (file %s)- IOException - message: %s ", imageName, e.getMessage());
logger.error(info);
} finally {
imageWriter.dispose();
}
return baos.toByteArray();
}

ImageWriteParam set compression quality not working

I am trying to create an app that combines several JPG into one big JPG without any compression. But in my test sample, when I combine 8 images (800KB), the output is only 280KB.
I used this as reference in writing the code. But no matter what value I inserted to param.setCompressionQuality(...), the output is always 280KB and doesn't change.
String name = dir.getName();
System.out.println("Combining images... #" + name);
BufferedImage result = combine(dir);
ImageWriter writer = ImageIO.getImageWritersByFormatName("jpeg").next();
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(1.0F); // set highest quality
try {
File outputFile = new File(outputFolder + "\\" + name + ".JPG");
ImageOutputStream ios = ImageIO.createImageOutputStream(outputFile);
writer.setOutput(ios);
writer.write(result);
} catch (IOException e) {
e.printStackTrace();
}
dir is a File type variable indicating my source folder location.
combine() is a method that returns BufferedImage.
The reason why it doesn't work, is because you need to pass the ImageWriteParam (param in your code) to the write method to have an effect.
The getDefaultWriteParam() method will only create it for you, it will not stay "attached" to the writer. See the ImageWriter.write API doc for further information.
The code will then look like this:
BufferedImage result = ...
ImageWriter writer = ImageIO.getImageWritersByFormatName("jpeg").next();
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
...
writer.setOutput(...);
writer.write(null, new IIOImage(result, null, null), param); // nulls are metadata and thumbnails, don't worry :-)

Write GeoTIFF metadata from one file to other

My task is to take one GeoTIFF, make some image segmentation on in, and save it to new GeoTIFF(with existing coordinates). If I understand correctly, the coordinates are preserved in GeoTIFF metadata.
So I grab metadata from the original file:
File file = new File(inputFilePath);
ImageInputStream iis = ImageIO.createImageInputStream(file);
Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
IIOMetadata metadata=null;
ImageReader reader=null;
if (readers.hasNext()) {
// pick the first available ImageReader
reader = readers.next();
// attach source to the reader
reader.setInput(iis, true);
// read metadata of first image
metadata = reader.getImageMetadata(0);
}
And when I do
System.out.println("Metadata: "+metadata);, I see the correct XML tree of metatags.
So I'm do some magic with image
System.out.println("Starting segmentation");
BufferedImage image = UtilImageIO.loadImage(inputImage);
// Select input image type. Some algorithms behave different depending on image type
ImageType<MultiSpectral<ImageFloat32>> imageType = ImageType.ms(3, ImageFloat32.class);
ImageSuperpixels alg = FactoryImageSegmentation.fh04(new ConfigFh04(500, 30), imageType);
// Convert image into BoofCV format
ImageBase color = imageType.createImage(image.getWidth(), image.getHeight());
ConvertBufferedImage.convertFrom(image, color, true);
// Segment and display results
performSegmentation(alg, color);
System.out.println("Segmentation finished");
In result I obtain a BufferedImage(resultBufferedImage) with successfully image segmentation.
And here starts my problems, I'm trying to save this BufferedImage with old metadata:
BufferedOutputStream out;
ImageWriter writer = ImageIO.getImageWriter(reader);
ImageOutputStream imgout = null;
FileOutputStream fos =null;
fos = new FileOutputStream(outputImage);
out = new BufferedOutputStream(fos);
imgout = ImageIO.createImageOutputStream(out);
writer.setOutput(imgout);
ImageWriteParam param = writer.getDefaultWriteParam();
IIOImage destIIOImage = new IIOImage(resultBufferedImage, null, metadata);
System.out.println("Before write");
writer.write(null, destIIOImage, null);
System.out.println("After write");
I get printed "After write". But program is still running, I tried to wait, but no results. So when I kill process the file is created successfully, even with geodata. How can I determine the finish of writing and stop program?
p.s. Image in default Ubuntu viewer seems to be nice, but when I opened it in QGIS I have transparent fields, and how can I make gray background transparent?
Not a real answer, but here's two answers on how to make a TIFF transparent:
QGis problem with raster transparent
How to make transparent the background of a topographic map in QGis 1.8.0?

How do you access an attachment stored as MIME Part?

It seems to me there are two ways to store an attachment in a NotesDocument.
Either as a RichTextField or as a "MIME Part".
If they are stored as RichText you can do stuff like:
document.getAttachment(fileName)
That does not seem to work for an attachment stored as a MIME Part. See screenshot
I have thousands of documents like this in the backend. This is NOT a UI issue where I need to use the file Download control of XPages.
Each document as only 1 attachment. An Image. A JPG file. I have 3 databases for different sizes. Original, Large, and Small. Originally I created everything from documents that had the attachment stored as RichText. But my code saved them as MIME Part. that's just what it did. Not really my intent.
What happened is I lost some of my "Small" pictures so I need to rebuild them from the Original pictures that are now stored as MIME Part. So my ultimate goal is to get it from the NotesDocument into a Java Buffered Image.
I think I have the code to do what I want but I just "simply" can't figure out how to get the attachment off the document and then into a Java Buffered Image.
Below is some rough code I'm working with. My goal is to pass in the document with the original picture. I already have the fileName because I stored that out in metaData. But I don't know how to get that from the document itself. And I'm passing in "Small" to create the Small image.
I think I just don't know how to work with attachments stored in this manner.
Any ideas/advice would be appreciated! Thanks!!!
public Document processImage(Document inputDoc, String fileName, String size) throws IOException {
// fileName is the name of the attachment on the document
// The goal is to return a NEW BLANK document with the image on it
// The Calling code can then deal with keys and meta data.
// size is "Original", "Large" or "Small"
System.out.println("Processing Image, Size = " + size);
//System.out.println("Filename = " + fileName);
boolean result = false;
Session session = Factory.getSession();
Database db = session.getCurrentDatabase();
session.setConvertMime(true);
BufferedImage img;
BufferedImage convertedImage = null; // the output image
EmbeddedObject image = null;
InputStream imageStream = null;
int currentSize = 0;
int newWidth = 0;
String currentName = "";
try {
// Get the Embedded Object
image = inputDoc.getAttachment(fileName);
System.out.println("Input Form : " + inputDoc.getItemValueString("form"));
if (null == image) {
System.out.println("ALERT - IMAGE IS NULL");
}
currentSize = image.getFileSize();
currentName = image.getName();
// Get a Stream of the Imahe
imageStream = image.getInputStream();
img = ImageIO.read(imageStream); // this is the buffered image we'll work with
imageStream.close();
Document newDoc = db.createDocument();
// Remember this is a BLANK document. The calling code needs to set the form
if ("original".equalsIgnoreCase(size)) {
this.attachImage(newDoc, img, fileName, "JPG");
return newDoc;
}
if ("Large".equalsIgnoreCase(size)) {
// Now we need to convert the LARGE image
// We're assuming FIXED HEIGHT of 600px
newWidth = this.getNewWidth(img.getHeight(), img.getWidth(), 600);
convertedImage = this.getScaledInstance(img, newWidth, 600, false);
this.attachImage(newDoc, img, fileName, "JPG");
return newDoc;
}
if ("Small".equalsIgnoreCase(size)) {
System.out.println("converting Small");
newWidth = this.getNewWidth(img.getHeight(), img.getWidth(), 240);
convertedImage = this.getScaledInstance(img, newWidth, 240, false);
this.attachImage(newDoc, img, fileName, "JPG");
System.out.println("End Converting Small");
return newDoc;
}
return newDoc;
} catch (Exception e) {
// HANDLE EXCEPTION HERE
// SAMLPLE WRITE TO LOG.NSF
System.out.println("****************");
System.out.println("EXCEPTION IN processImage()");
System.out.println("****************");
System.out.println("picName: " + fileName);
e.printStackTrace();
return null;
} finally {
if (null != imageStream) {
imageStream.close();
}
if (null != image) {
LibraryUtils.incinerate(image);
}
}
}
I believe it will be some variation of the following code snippet. You might have to change which mimeentity has the content so it might be in the parent or another child depending.
Stream stream = session.createStream();
doc.getMIMEEntity().getFirstChildEntity().getContentAsBytes(stream);
ByteArrayInputStream bais = new ByteArrayInputStream(stream.read());
return ImageIO.read(bais);
EDIT:
session.setConvertMime(false);
Stream stream = session.createStream();
Item itm = doc.getFirstItem("ParentEntity");
MIMEEntity me = itm.getMIMEEntity();
MIMEEntity childEntity = me.getFirstChildEntity();
childEntity.getContentAsBytes(stream);
ByteArrayOutputStream bo = new ByteArrayOutputStream();
stream.getContents(bo);
byte[] mybytearray = bo.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(mybytearray);
return ImageIO.read(bais);
David have a look at DominoDocument,http://public.dhe.ibm.com/software/dw/lotus/Domino-Designer/JavaDocs/XPagesExtAPI/8.5.2/com/ibm/xsp/model/domino/wrapped/DominoDocument.html
There you can wrap every Notes document
In the DominoDocument, there such as DominoDocument.AttachmentValueHolder where you can access the attachments.
I have explained it at Engage. It very powerful
http://www.slideshare.net/flinden68/engage-use-notes-objects-in-memory-and-other-useful-java-tips-for-x-pages-development

Java: How to add metainformation to multiple image types in a generic way

What needs to be done
I need to add custom meta information to image files.
The supported types need to be: JPEG, GIF, BMP, TIF, PNG
How is it done
I have decided to use the imageio Standard metadata in order to provide generic solution for all file types.
I am also using Java Advanced Imaging library, as not all file types are supported by the general api.
Here is a code snippet of the core function that adds the metadata:
public static byte[] writeCustomMetaInformation(byte[] imageByteArray, String contentType) throws IOException {
ImageWriter writer = null;
String formatName = "javax_imageio_1.0";
ImageOutputStream stream = null;
if (contentType.toLowerCase().contains("gif")) {
IIORegistry registry = IIORegistry.getDefaultInstance();
registry.registerServiceProvider(new GIFImageWriterSpi());
writer = ImageIO.getImageWritersByFormatName("gif").next();
} else if (contentType.toLowerCase().contains("png")) {
writer = ImageIO.getImageWritersByFormatName("png").next();
} else if (contentType.toLowerCase().contains("bmp")) {
IIORegistry registry = IIORegistry.getDefaultInstance();
registry.registerServiceProvider(new BMPImageWriterSpi());
writer = ImageIO.getImageWritersByFormatName("bmp").next();
} else if (contentType.toLowerCase().contains("tif")) {
IIORegistry registry = IIORegistry.getDefaultInstance();
registry.registerServiceProvider(new TIFFImageWriterSpi());
writer = ImageIO.getImageWritersByFormatName("tif").next();
} else { //by default jpeg
writer = ImageIO.getImageWritersByFormatName("jpeg").next();
}
BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(imageByteArray));
ImageWriteParam writeParam = writer.getDefaultWriteParam();
ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
/*
* Add metadata to the file
* See http://docs.oracle.com/javase/6/docs/api/javax/imageio/metadata/doc-files/standard_metadata.html
* for reference on default image metadata DTD
*/
IIOMetadata metadata = writer.getDefaultImageMetadata(typeSpecifier, writeParam);
IIOMetadataNode root = new IIOMetadataNode(formatName);
//Add RGB tag, otherwise BMP writing throws NPE
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
IIOMetadataNode colorSpaceType = new IIOMetadataNode("ColorSpaceType");
colorSpaceType.setAttribute("name", "RGB");
chroma.appendChild(colorSpaceType);
root.appendChild(chroma);
IIOMetadataNode text = new IIOMetadataNode("Text");
IIOMetadataNode textEntry = new IIOMetadataNode("TextEntry");
textEntry.setAttribute("keyword", "metainformation");
textEntry.setAttribute("value", "This is some custom meta information!");
textEntry.setAttribute("encoding", "UTF-8");
textEntry.setAttribute("language", "EN");
textEntry.setAttribute("compression", "none");
text.appendChild(textEntry);
root.appendChild(text);
metadata.mergeTree(formatName, root);
// writing the data
ByteArrayOutputStream baos = new ByteArrayOutputStream();
stream = ImageIO.createImageOutputStream(baos);
writer.setOutput(stream);
IIOImage img = new IIOImage(bufferedImage, null, metadata);
writer.write(metadata, img, writeParam);
stream.close();
return baos.toByteArray();
}
What is the result
I use ImageMagick's identify -verbose to view the result of adding the meta information.
JPG, PNG and GIF files have meta information added
BMP and TIF either does not have the meta information added, or ImageMagick cannot read it.
So, does anyone know why meta information added to BMPs and TIFs doesn't seem to take effect? Is Standard Metadata DTD not applicable to them? Should I use BMP/TIF metadata instead?
If you want to test the behavior, here is a link to completely set-up project that demonstrates the conversion and its problems.

Categories