How to get image compression quality from IIOMetadata? - java

When saving a new image with ImageIO generic ImageWriteParam supports explicit setting of compressionQuality parameter from range 0.0f (high compression) to 1.0f (high quality) regardless of image compression algorithm used (eg. png, jpeg, gif).
Is there any way to read compressionQuality from an existing image?
Is that compressionQuality write parameter just a hint to ImageWriter and is not stored anywhere in image's metadata? If that's true how image processing software (e.g. GIMP) manages to provide the following option in 'Save As' dialog?
I managed to read IIOMetadata from ImageInputStream and iterate through its metadataFormatNames to print out image metadata in different XML formats (native and standard javax_imageio_1.0, usually). Although I couldn't find any indication of image's compressionQuality in there.

I don't think that compressionQuality is stored with image meta data, this is processing parameter only.

Related

Lossless image extraction from PDF

I'm using PDFBox to extract images out of a PDF file and feed it to another image processing library (that can handle different image formats). My current code is like this:
PDImageXObject pdImage;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
BufferedImage image = pdImage.getImage();
ImageIO.write(image, "png", baos);
byte[] imageBytes = baos.toByteArray();
This will take whatever is stored in the PDF file and use Java graphics to convert it to PNG. Is there a better way to avoid conversion and extract the image in whatever format it is embedded? I don't want to degrade image quality (I suppose mitigated by using a lossless format like PNG?) and incur conversion overhead.
The DEFLATE algorithm is used by the FlateDecode filter and by the PNG file format. However a stream of FlateDecode-compressed data isn't itself a PNG file.
Also, you need to consider the colorspace representation of the Image XObject (e.g. DeviceCMYK) versus what PNG actually supports.
By targeting lossless compression for your output image file you won't lose any information. (Be sure you actually need a lossless extracted image, often people assume lossy compression means their image will now have so many changes it's no longer recognizable. Though in many cases depending on the parameters the loss is hardly noticeable to the naked eye and you can substantially benefit from the size savings of Lossy compression.)
If performance is slow it could simply be the quality of your PDF software responsible for extracting the image and saving it.

How to convert .jpeg image to .jif in java? (Exif TO JFIF)

From customer i have request to send images in .JIF format (JFIF) . I have java aplication, but i coould not google anithing to topic of how to convert to that image type, i could even hardly google anithing to ".JIF" format itself.
EDITED :
Can somebody advice how to convert Exif image to JFIF in java ? And how to add coments to this JFIF image?
(tried to use jheader library sadly ended with nullpointer exception, not much more choices on google.)
Edit: Converting Exif JPEGs to JFIF JPEGs:
If you don't mind losing some quality (due to lossy JPEG re-encoding), you can convert the image as simply as:
File inFile = ...;
File outFile = ...; // Feel free to use ".jif" as extension
if (!ImageIO.write(ImageIO.read(inFile), "JPEG", outFile)) {
System.err.println("Could not write JPEG format"); // Should never happen
}
This will work, because the default JPEGImageWriter plugin only supports JFIF format. And because we don't read the metadata, the old Exif information will just be lost. Doing it this way, will not allow you to add comments, however.
To add comments, you could still use standard ImageIO API, but we'll have to access the metadata, making the code more verbose. See JPEG Metadata Format Specification for more information on the metadata format. If you need to convert comments from the Exif metadata, please update your question to specific on that, as it requires further parsing of the meta data and extra support not currently in the ImageIO API.
File inFile = ...;
File outFile = ...; // Feel free to use ".jif" as extension
BufferedImage image = ImageIO.read(inFile);
ImageWriter jpegWriter = ImageIO.getImageWritersByFormatName("JPEG").next(); // Should be a least one
// To write comments, we need to add it to the metadata
ImageWriteParam param = jpegWriter.getDefaultWriteParam();
IIOMetadata metadata = jpegWriter.getDefaultImageMetadata(ImageTypeSpecifier.createFromRenderedImage(image), param);
IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree("javax_imageio_jpeg_image_1.0");
IIOMetadataNode markerSequence = (IIOMetadataNode) root.getElementsByTagName("markerSequence").item(0); // Should be only one
// Insert a "COM" marker, with our comment
IIOMetadataNode com = new IIOMetadataNode("com");
com.setAttribute("comment", "Hello JFIF!");
markerSequence.appendChild(com);
// Merge edited metadata
metadata.mergeTree("javax_imageio_jpeg_image_1.0", root);
ImageOutputStream output = ImageIO.createImageOutputStream(outFile);
try {
jpegWriter.setOutput(output);
// Write image along with metadata
jpegWriter.write(new IIOImage(image, null, metadata));
}
finally {
output.close();
}
jpegWriter.dispose();
This way, we still re-encode the image in lossy JPEG, but we convert from Exif to JFIF and add comments.
Now, there is still another option, to do this completely lossless. But it does require a bit of a deeper understanding of the JIF segment structure, and how the Exif and JFIF formats work. Unfortunately, there's no standard Java API (that I know of) to do this, so you will have to roll your own. Feel free to use my JPEG segment parsing code as a starting point. The JHeader project you linked also looks very promising, but I don't have any experience with this library, so I can't provide any advice there.
Here's the basic idea:
Parse/skip the marker segments until the SOS (Start of Scan) segment (the data following the SOS will be the compressed image data).
Write the SOI marker (0xffd8)
Create an APP0/"JFIF" marker (I think you can just use defaults here, see JFIF segment for details). You can write 0, 0 for the thumb dimensions, and skip writing thumbnail data.
Add your COM segments with whatever comments you need (possibly extracted from the Exif metadata)
Write the SOF, DHT, DQT etc. standard segments as-is from the original stream (skip the APP1/"Exif" and other "custom" segments).
Write the SOS marker and the image data from the original stream
In theory, this should work. You might have some minor color space issues, as the Exif data might contain different color spaces (normally sRGB or AdobeRGB1998), while JFIF doesn't have a defined color space. If you need this add an APP2/"ICC_PROFILE" segment with the required profile (after step 3).
Good luck! :-)
Note: This is not a complete answer, but instead an attempt to clarify why you need to talk to your client, and figure out what is wrong with your JPEGs and what he actually means by "JIF".
First an foremost, JPEG is not a file format. JPEG is a still image compression standard. Part of this standard (usually referred to as "Annex B") is a description of an interchange format, sometimes referred to as JIF. The standard also specifies a full file format known as SPIFF, but this format is not very widespread (and I don't think this is what you want).
The files you find everywhere, referred to as "JPEG files" (and I assume this is what you refer to as "Classic JPEG"), is usually in one of two slightly different flavors of basically the same file format:
The most basic format is JFIF. This format starts with a SOI marker, immediately followed by an APP0 marker with "JFIF" (null-terminated) as its identifier. According to the original JFIF specification "The JPEG File Interchange Format is entirely compatible with the standard JPEG interchange format; the only additional requirement is the mandatory presence of the APP0 marker right after the SOI marker." (this part is left out of the ITU and ISO versions of the specification, but still applies). Put simply, JFIF constrains the JPEG data to be 1 or 3 components, encoded as either Y or YCbCr, and highly recommends baseline DCT, Huffman coded compression.
The other common format is Exif. This format starts with a SOI marker, immediately followed by an APP1 marker with "Exif" (null-terminated) as its identifier. This format is developed by the digital camera manufacturers, and allows much richer meta data to be recorded within the file (in the form of a TIFF meta data structure). From what I understand, Exif constrains the JPEG data to be 3 components, encoded as YCbCr, using baseline DCT, Huffman coded compression (the last part may be just a an interoperability recommendation, the language in the spec is a little hard to read...).
Both of these formats contains the same "segment" layout and the image data is compatible, but still they are mutually exclusive, due to the requirement of having "their" marker as the first segment in the stream (because of this, also a "third" format exists, which is a JFIF for compatibility, but still contains an Exif segment for richer meta data).
Yet another family of "JPEG files" lacks both JFIF and Exif markers, but still follows the same segment layout, with SOI, APPn markers, SOF, DHT, DQT, SOS and EOI markers, as described in "Annex B" (JIF). Most decoders will decode these images as well.
TL;DR: To summarize, what all the "JPEG" file formats have in common, is that they use JPEG compression, and follows the JIF structure. Because of this, it is somewhat hard to understand what someone means by "convert classic JPEG to JIF".
"Classic JPEG" is JIF.
First of all you need to read that image then you must write that image into the dimensions and format you want.
You must use ImageIO class and BufferedReader to read images
To write them use Graphics2D class
Replace format name with jif
File inputFile = new File(inputImagePath);
BufferedImage inputImage = ImageIO.read(inputFile);
// creates output image
BufferedImage outputImage = new BufferedImage(scaledWidth,
scaledHeight, inputImage.getType());
// scales the input image to the output image
Graphics2D g2d = outputImage.createGraphics();
g2d.drawImage(inputImage, 0, 0, scaledWidth, scaledHeight, null);
g2d.dispose();
// extracts extension of output file
String formatName = outputImagePath.substring(outputImagePath
.lastIndexOf(".") + 1);
// writes to output file
ImageIO.write(outputImage, formatName, new File(outputImagePath));

How to save photos in full resolution in android?

I have made an app that uses the camera to capture images. The images are passed back to my application as Bitmap. I want to know how to modify my code to save the Bitmap into JPEG format at its full resolution?
FileOutputStream out = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
I think the Bitmap is being compressed into smaller size!
Compression refers to the the reduction of physical disk space required to save the image. It doesn't automatically mean that the image quality or resolution is also reduced.
JPEG is part of a group of file formats that (mostly) belong to the lossy compression algorithms. In other words some minor image detail and quality is sacrificed to reduce the file size of the image, but it still wouldn't reduce the resolution of the image.
If you want to reduce the file size of the image, but don't want to loose any image quality you need to use a file format which supports lossless compression. You can for example use Bitmap.CompressFormat.PNG.
WEBP supports both lossy and lossless compression (and is even smaller than PNG and JPG in file size). But support for WEBP was only added in API level 14 so there might be some backwards compatibility problems. Just use WEBP if possible, otherwise PNG if you care about image quality.
In any case let's look at the compress() method:
public boolean compress (Bitmap.CompressFormat format, int quality, OutputStream stream)
As you can see you can choose the CompressFormat, pass an OutputStream in and pick a quality. The number you pass in as quality can be between 0 and 100 and it determines if you compress either lossy or lossless. Since you pass in 100 the compression will always be lossless regardless of which CompressFormat you pick!
As an aside: Since PNG only supports lossless compression it will ignore the quality parameter completely and always save the image without reducing its quality!

How can I read the metadata from bitmaps in Android (Java)?

How can I read the file format (png, jpg, gif, bmp, tiff, etc) and the width, height, and for those that have the value, the DPI for a bitmap?
Android does not have javax.imageio.* so the standard Java calls to read this are not available. metadata-extractor is a good product but does not support png or gif.
Width and height, along with bitdepth and color model, are not usually considered "metadata", rather they are basic image properties. By "image metadata" we usually refer to less essential data like physical resolution (DPI), EXIF-like data (timestamp, camera model, etc), etc; these are relatively format specific.
If you have decoded a image file (PNG, JPEG, GIF...) into a Bitmap, then you can ask the Bitmap object for the basic properties (eg, getHeight()).
But if you need to ask for some "metadata", which is format specific, a Bitmap object won't store that information, you need something like the metadata-extractor you linked.
For PNG, you can also use this library (my own).

How to get a better understanding of a png representation on java?

I want to dive in the low level of how a png file is represented on memory in java, so that i can iterate over its pixels, change them, create a modified png file using existing one, etc.
Where do i begin?
You could begin by reading it into a BufferedImage with ImageIO.read(file) .
The getRGB(...) methods can help you to obtain information about the individual pixels, and the corresponding setRGB(...) methods help you to change them.
The representation of an image in memory in Java, is essentially unrelated to the format of the file: be it PNG, JPEG, GIF or whatever, those are standards for encoding an image as a (language independent) stream of bytes. But when you are manipulating the pixels of an image in memory, you have already decoded it, and so you've "forgotten" from which format (PNG, JPEG...) it came from.
The most common way of manipulating an image in Java is using the BufferedImage class, included in the java.awt.image.* package. But that's not a requisite. For instance, I've worked on a low level PNG coder/encoder (PNGJ) that does not use BufferedImage, but instead gives you each image line as an int[] array.

Categories