I am reading in and processing TIF images using ImageIO and JAI. The results are all working perfectly, except that a number of the TIF images do not have square pixels. The aspect ratio of the pixels is being lost during the processing so the resulting image looks stretched.
I found this question which reads out the resolution in C#: Change tiff pixel aspect ratio to square but I cannot find any equivalent in java.
Does anyone know how either to read the horizontal and vertical resolution (not size) of a BufferedImage and/or TIF Image in Java or cause JAI to scale the image as it loads it so that the resulting pixels are square?
After an hour of Googling and trying things I think I have found a solution.
IIOMetadata iiom = ir.getImageMetadata(i);
TIFFDirectory dir = TIFFDirectory.createFromMetadata(iiom);
TIFFField fieldXRes = dir.getTIFFField(BaselineTIFFTagSet.TAG_X_RESOLUTION);
TIFFField fieldYRes = dir.getTIFFField(BaselineTIFFTagSet.TAG_Y_RESOLUTION);
int xRes = fieldXRes.getAsInt(0);
int yRes = fieldYRes.getAsInt(0);
As an alternative, you can also get the same values from the Standard Metadata, if you don't want to rely on the JAI API (or TIFF format specifics at all).
The Dimension element has the child elements HorizontalPixelSize and VerticalPixelSize which should be equivalent to the values you got from the TIFF tags above, as well as a PixelAspectRatio you could use directly.
Related
There are about 100 jpeg & png color images used in our JavaFX-built desktop app which, when the window is resized, become stretched and blurry so I'd like to have all the graphics remade in a format that will allow them to be dynamically resized without losing quality. What image format or procedure should be used to do this?
Currently, each image is simply in an ImageView and resized as follows, but I'm open to other suggestions:
if(isSmall){
Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
double sh = visualBounds.getHeight();
Scale scale = new Scale(sh, sh, 0, 0);
root.getTransforms().setAll(scale);
}
As has already been mentioned SVG is probably the way to go for you. JavaFX does not support SVG directly but you can find support here
javafxsvg and here svg-to-fxml-converter for example.
You can't resize an image to be bigger than it is without it getting blurry for most common formats. Instead make sure your images are big enough so you only need to downscale them.
The only format I ever heard of that could upscale further was using fractal compression, but AFAIK it is not in common use.
I'm using the itext PDF library to build a very image-intensive PDF document in Java. Each page has a dozen images on it. The original source images are very high resolution, and I'm using scaleToFit to render the image to the size I need.
The problem I have is that the PDF document is still very large. My understanding is that the entire original high resolution image is being included, and the scaling I'm using only affects the actual rendering, not the size of the image that's included in the file.
I've verified this by removing the scaling — the pages were rendered with the high resolution images overlapping each other and the edge of pages, and the PDF was the same size as when the scaling was in place.
So, here's the question — how can I reduce the size of the PDF file by scaling down each image? If I lose a little bit of image quality that's ok. Rescaling the source images manually will be difficult.
So I've found a way to do it. I now load the image into a BufferedImage, and then scale that using the hints found here: how do I scale a BufferedImage.
This gives me a BufferedImage — I then convert this into an iText image using
Image returnedImage = Image.getInstance ( pcb, bufferedImage, quality );
Where quality is currently 0.6. That's acceptable for the work I'm doing.
I try to get the compression ratio of an JPEG and GIF image in Java.
Searched everywhere but cant find anything. Is it possible to read the compression ratio of the files?
When not how could i compute this ratio?
To calculate the compression of an image you compare the actual file size to the size you'd get if you were storing the image "raw".
For example a jpeg file that's 1024x1024, true color (24bpp) that's 384Kb you'd get a ratio of (384x1024) / (1024x1024x3) = 0.125, this means the jpeg produced a file that's 12% of raw image. If you invert the division you can say the image was compressed 8x or 1:8 ratio.
Get the size and color info of the image from headers or by using Image API, no need to decompress the file to do this calculation
You could try comparing the filesize to it's pixels count - it gives you a sort of ratio. For example:
//Image 1
Image Dimensions = 607x800px
Number of pixels = 486K
File size = 143KB
//Good quality
//Image 2
Image Dimension s= 1719x2377px
Number pixels= 4.086M
File size = 408KB
//Bad quality
You can start with Java Image-IO, read in your image and use the appropriate methods of the ImageReader class.
You can download the JAI here.
Apologies for any ignorance, but I have never worked with jpeg images (let alone any types of images) in Java before.
Supposing I want to send a jpeg image from a web service to a client. Is there any way that I can reduce the jpeg file size by manipulating the colour profile of the image in some way?
I have already been able to reduce the image size by scaling it using a neat tool for BufferedImages called imgscalr. See here.
I would also like a jpeg that has less colours than a high quality jpeg image. For example, I would like to be able to use 8bit colour in my jpeg instead of say 16bit colour.
What exactly would I need to change if I have a BufferedImage from Java's 2D package?
Another way to reduce image size is to change compression level. You can do that using ImageWriter.
ImageWriter writer = null;
Iterator<ImageWriter> iwi = ImageIO.getImageWritersByFormatName("jpg");
if (!iwi.hasNext())
return;
writer = (ImageWriter) iwi.next();
ImageWriteParam iwp = writer.getDefaultWriteParam();
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT) ;
iwp.setCompressionQuality(compressionQuality);
writer.setOutput(...);
writer.write(null, image, iwp);
The easiest way to do this is to decompress the byte stream into a Java Image, optionally resize it (which makes it smaller) and then regenerate a JPEG image from this with the desired quality setting.
This new image is then what is sent to the client.
Have a look at the ImageIO class. As for reducing file size: since the image would already be a JPEG the only things you could do is reduce the quality or the image size.
Another thing to keep in mind: if the image is a CMYK jpeg it might be bigger. Unfortunately ImageIO can't handle those, but you can try JAI ImageIO to convert from CMYK to RGB (which should be much smaller).
Two of the possible solutions are downscaling the image, here's how you'd do it:
BufferedImage original = //your image here
scaled = original.getScaledInstance(finalWidth, finalHeight, Image.SCALE_SMOOTH); // scale the image to a smaller one
BufferedImage result = new BufferedImage(finalWidth, finalHeight, original.getType());
Graphics2D g = result.createGraphics();
g.drawImage(scaled, 0, 0, null); //draw the smaller image
g.dispose();
Obviously, you have to calculate the scaled width and height so the image stays by the same aspect ratio.
Once you have drawn it smaller, you can now turn this image into a JPEG file:
BufferedImage image = // this is the final scaled down image
JPEGImageEncoder jpegEncoder = JPEGCodec.createJPEGEncoder(output);
JPEGEncodeParam jpegEncodeParam = jpegEncoder.getDefaultJPEGEncodeParam(image);
jpegEncodeParam.setDensityUnit(JPEGEncodeParam.DENSITY_UNIT_DOTS_INCH);
jpegEncodeParam.setXDensity(92);
jpegEncodeParam.setYDensity(92);
jpegEncodeParam.setQuality( 0.8F , false);
jpegEncoder.encode(image, jpegEncodeParam);
These classes are from the JAI package (more exactly com.sun.image.codec.jpeg) and the JVM might complain that they should not be used directly, but you can ignore that.
You can possibly download JAI from here, if it does not work I have github mirrors setup for the two libraries, JAI core and JAI ImageIO.
I want to reduce image size (in KB) when its size is larger than 1MB.
when I apply the resize transformation with smaller width and smaller height the size of the transformed image (in bytes) is larger than the orig image.
The funny (or sad) part is even when I invoke the resize with the same width and height as the orig (i.e. dimensions are not changed) the size "transformed" image is larger than the orig
final byte[] origData = .....;
final ImagesService imagesService = ImagesServiceFactory.getImagesService();
final Image origImage = ImagesServiceFactory.makeImage(oldDate);
System.out.println("orig dimensions is " + origImage.getWidth() + " X " + origImage.getHeight());
final Transform resize = ImagesServiceFactory.makeResize(origImage.getWidth(), origImage.getHeight());
final Image newImage = imagesService.applyTransform(resize, origImage);
final byte[] newImageData = newImage.getImageData();
//newImageData.length > origData.length :-(
Image coding has some special characteristics that you are observing the results from. As you decode a image from its (file) representation, you generate a lot of pixels. The subsequent encoding only sees the pixels and does not know anything about the size of your original file. Therefore the encoding step is crusial to get right.
The common JPEG format, and also the PNG format, have different compression levels, i.e a quality setting. They can have this because they do lossy compressions. In general, images with a lot of details (sharp edges) should be compressed with high quality and blurry images with low quality; as you probably have seen, small images usually are more blurry and large images usually more sharp.
Without going into the techical details, this means that you should set the quality level accoring to the nature of your image, which also is determined by the size of the input image. In other words, if you encode a blurry image as a big file, you are wasting space, since you would get about the same result using less bytes. But the encoder does not have this information, so you have to configure it using the correct quality setting
Edit: In your case manually set a low quality for encoding if you started with a small file (compared to number of pixels) and then of course a high quality if the opposite is true. Do some experimentations, probably a single quality setting for all photos will be acceptable.
A pitfall I fell in was, that I requested PNG output ... and the image size didn't change either. The image service silently ignored quality parameter. According to a comment in implementation the quality parameter is considered only for JPEG.