Increasing Resolution and Reducing Size of an Image in Java - java

When I first asked this, I included as much information as I could, since I never know what will help someone if they can give m a solution, but from the answers, it seems I didn't make my point clear.
I'm leaving the original text at the bottom and shortening this. I hope that makes it clear.
I am working with images returned to me from another program. I can get an image that's, for example, 8x10 at 72 DPI or the same image at 16x20, also at 72DPI.
I'm working in Java. What I'd like to do is take the 16x20 image and resize it. If possible, I'd like to just change the file so instead of being 16x20 at 72DPI, it's 8x10 at 144DPI.
I know that it's still the same number of pixels, but viewers will say how big an image is and what the DPI is and also some open an image to its full size, so I'd rather them treat the 16x20 as a smaller image with a higher resolution.
Can I do that in Java? Will I lose any data? How would I do it - can I just change the metadata in the png or jpg file?
-----(original question below here)-----
I've seen a few questions that come close to this, but this is a little different.
I know you can decrease image resolution with an image in Java (also here). Any searches I've done have been concerned with decreasing the resolution of an image.
I have the opposite problem. I'm working with an image that I'm getting back in 72DPI. The advantage is I can increase the size of the picture, so I could make the size 100%, 200%, 400% and so on. All images are at 72DPI, so if I use 200% for the zoom, then open the image in a viewer and make it the same size as the 100% image, I am seeing a better resolution.
For clarity - I am getting an image from someone else, it could be generate from a PDF or picture, but I am given the image from another program. No matter what I do, this image is at 72DPI. I can't change that, but I can specify the size at 200% and it still comes back at 72DPI, just twice as big and with more detail.
I'm no graphics expert and this is the first time I've done any programming that uses graphics. The output could be jpg or png, so the format isn't that important. (I could probably also use other formats if needed.)
Let's say the original image is a photo - 8" x 10", and at 72 DPI. If I ask for a 200% zoom, I get a 16" x 20" image at 72DPI.
Can I, within Java, change that larger 16" x 20" 72DPI image to a 144DPI image that's 8" x 10" without losing data? In other words, double the DPI, but cut the size in half?
And, most importantly, can I do it without losing resolution? (I would think that can be done.)

No, if you take an 8x10 72dpi image and ask for a 200% zoom you'll get a 16x20 at 36 dpi. You can't magically increase image resolution.
What counts is the total number of pixels in the image. The stated DPI is just what the DPI will be at a specific size. Take your 8x10 image at 72dpi. It has 576x720 pixels. If you zoom to 200% you still have the same number of pixels, but now they'll be spread out over twice the linear dimensions, meaning the individual pixels will be bigger. Some software will attempt to increase resolution by interpolating pixels, i.e. creating new pixels by averaging the nearest neighbors. This can give the impression of higher resolution but the image does not contain any more data. It always has the effect of softening the image, especially any sharp edges or lines.
If you reduce the size of an image without resampling, you do increase the printed or displayed resolution, but the image gets smaller. For the same image, increasing the DPI to 144 will reduce the image size to 4x5.

This is tricky because, like so much in Java, you can't just access something in a simple way. Java does not keep track of DPI, but it does use dots per millimeter. Also, another part that is confusing is that you cannot change this kind of information in an image, or in a BufferedImage. You can only change this information when you are writing a BufferedImage out through an ImageWriter.
I was able to do this. As I said, I could specify the zoom on the image that was returned to me. No matter what zoom level, the output was 72 DPI. My goal was 300DPI. I specified a zoom level of 400%. So, on an 8" wide image of 72 DPI, that returned to me a 32" image of 72 DPI. All I had to do was specify a DPI of 288 (72 x 4) to override the default 72 DPI I was dealing with and then when it was written out, the image had the same number of pixels, but was considered to be done at 288 DPI rather than 72 DPI.
Here's a code snippet:
//Assumed there's already an ImageWriter iw
ImageWriteParam writeParam = writer.getDefaultWriteParam();
ImageTypeSpecifier typeSpecifier =
ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
IIOMetadata metadata = writer.getDefaultImageMetadata(typeSpecifier, writeParam);
if (metadata.isReadOnly() || !metadata.isStandardMetadataFormatSupported()) {
continue;
}
// Set Parameters and info
int DPI = 72 * scaling/100;
double dotsPerMilli = 1.0 * DPI / 10 / 2.54;
double checkDots = 1.0 * 144 / 10 / 2.54;
System.out.println("Dots per inch: " + DPI + ", DotsPerMilli: " + dotsPerMilli + ",
CheckDots = " + checkDots);
IIOMetadataNode root = new IIOMetadataNode("javax_imageio_1.0");
IIOMetadataNode horiz = new IIOMetadataNode("HorizontalPixelSize");
horiz.setAttribute("value", Double.toString(dotsPerMilli));
IIOMetadataNode vert = new IIOMetadataNode("VerticalPixelSize");
vert.setAttribute("value", Double.toString(dotsPerMilli));
IIOMetadataNode dim = new IIOMetadataNode("Dimension");
dim.appendChild(horiz);
dim.appendChild(vert);
root.appendChild(dim);
metadata.mergeTree("javax_imageio_1.0", root);
// From here, just write out file using an ImageOutputStream
final ImageOutputStream stream = ImageIO.createImageOutputStream(outFile);
System.out.println("Output file: " + outFile);
try {
writer.setOutput(ImageIO.createImageOutputStream(outFile));
writer.write(metadata, new IIOImage(image_to_save, null, metadata),
writeParam);
} catch (Exception e) {
System.out.println("Caught exception " + e + " when trying to write out
file.");
System.exit(0);
} finally {
stream.close();
}

Related

Image getting printed with low resolution

I have a requirement to create pdf with images for printing. I am adding a high resolution image to pdf using iText. It's reducing the quality to 72 DPI.
Resulting in poor quality of image after printing.
The original image resolution is 2549*3304 and DPI (300).
I tried below options
image.scaleAbsolute(2549*.24 ,3304*.24 );
image.setDpi(300,300);
image.scaleToFit(2549*.24 ,3304*.24 );
current code looks like this
Document document = new Document(PageSize.LETTER);
document.open();
Image image = Image.getInstance("C:/Project/bg.png");
image.setAbsolutePosition(0,0);
image.scalePercent(24);
document.add(image);
document.close();
I went through some threads (Adding an image to a PDF using iTextSharp and scale it properly) still not able to solve the problem
Can anyone please help me on this to get better image quality while printing?
The setDpi() method is irrelevant in your code, so is the DPI of your original image. Pixels are treated as points in iText. This means that adding an image as-is will result in having 72 pixels per inch.
You scale the image to 24 percent of the original size. This means that you increase the resolution: you are showing the same number of pixels using only 24% of the space. In this case, you are showing 2549 pixels distributed over 611.76 points. This is about 8.5 inch, which means you indeed have a resolution of 300 DPI.
I think the problem isn't caused by the resolution of the image inside the PDF (but it's hard to tell because we can't inspect the PDF). I think the problem is caused by the printing process that prints the document using a resolution that is different from the resolution in the PDF.
In any case: iText does not reduce the number of pixels if you use the methods scalePercent(), scaleAbsolute() or scaleToFit().
Extra info based on Comments:
Asking a PDF for its "resolution" doesn't make sense, because a PDF doesn't have any resolution (although images inside a PDF may have one). I have no idea why Photoshop tells you the resolution is 72 DPI. Maybe that's a default value because the measurment unit in PDF corresponds with a point and there are 72 points in one inch.
I have examined the PDF that you shared. I don't see any loss of resolution when I look at it on my screen. I can see that the document measures 8.5 by 11 inch. As for the image, please take a look at the report generated by Acrobat:
It says width/height: 2550/3300 in pixel.
2550 / 8.5 = 300
3300 / 11 = 300
Hence the resolution is 300 pixels per inch. Or: the PDF is created exactly the way you want it.
However: you say that the resolution is worse when you print the document. This can be caused by many different things: maybe you're printing on a page that is larger than 8.5 by 11 inch, maybe the printer can't print at that resolution, maybe the PDF viewer can only print using "degraded printing",...
My advice is that you test this PDF on different printers using different viewers to find the culprit.

Java Images - "1 to 1" image scaling

I want to have a pixel-styled look for my test game, but when I scale them to the size I want them to be displayed at (50x50; the actual size of the tiles is 16x16), it gets that ugly smoothed out look I dont want to have.
img = new Image("test.png").getScaledCopy(50,50);
Size of test image is 16x16 and I want it to be displayed at a size of 50x50, but like 1:1 scaled, not the ugly smoothed out way.
A bit like in the sandbox game "Minecraft", the block tiles and such are much smaller than the displayed ones, yet they are not "smoothed".
Thank you a lot for any help :)
In your case, the anti aliasing filters look crummy because you have a very small image (16x16) and are upscaling it only a small bit (to 50x50). In your case, you likely want a sharper image, so you'd likely want to go with nearest neighbor interpolation, rather than the built-in default (either bi-linear or bi-cubic, not entirely sure which is the default).
Image original = …;
original.setFilter(Image.FILTER_NEAREST);
Image scaled = original.getScaledCopy();
So in your case, it would be something like:
Image original;
Image scaled;
original = new Image("test.png");
original.setFilter(Image.FILTER_NEAREST);
scaled = original.getScaledCopy(50, 50);
Please note that this won't be a true "1:1" scaling, since the scaling factor is not an integer ratio (ie: 50/16 isn't a whole non-decimal number). If you were to upscale to 64x64, you could have a "1:1" rescaling, since 16*k=64, where k is an integer.

Java Tookit Screen Resolution - How can I use it?

When using int resolution = Toolkit.getDefaultToolkit().getScreenResolution(); to get my monitor's resolution, the integer resolution returns 96. What does this mean, how could it be used, and, if possible, could I get the resolution in a 4:3, 16:9, etc. format?
getScreenResolution() return dots-per-inch (DPI). If you want the size of the screen, just use getScreenSize().
Here's a quote that gets you the basic idea of DPI vs. pixels:
So, if you have a 600 pixel x 600 pixel image at 300DPI, it will output at 2 inches square. If you change this images DPI to 150, this will mean it will output at 4 inches square. So, as you can see, changing the DPI of an image changes it output size.
You can find rest of the explanation here.

Merged two images --> 4 times the size! How do I reduce the file size?

I merge two images using the code below. One base image without transparency, one overlay image with transparency.
The file-size of the images one there own is 20kb and 5kb, respectively.
Once I merged the two images, the resulting file-size is > 100kb, thus at least 4 times the combined size of 25kb. I expected a file-size less than 25kb.
public static void mergeTwoImages(BufferedImage base, BufferedImage overlay, String destPath, String imageName) {
// create the new image, canvas size is the max. of both image sizes
int w = Math.max(base.getWidth(), overlay.getWidth());
int h = Math.max(base.getHeight(), overlay.getHeight());
BufferedImage combined = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
// paint both images, preserving the alpha channels
Graphics2D g2 = combined.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.drawImage(base, 0, 0, null );
g2.drawImage(overlay, 0, 0, null);
g2.dispose();
// Save as new image
saveImage(combined, destPath + "/" + imageName + "_merged.png");
}
My application has to be with very good performance, thus can anyone explain me why this effect happens and how I can reduce the resulting file size?
Thanks a lot!
EDIT:
Thanks a lot for your answers. The saveImage code is:
public static void saveImage(BufferedImage src, String file) {
try {
File outputfile = new File(file);
ImageIO.write(src, "png", outputfile);
} catch (IOException e) {
e.printStackTrace();
}
}
Because PNG is a lossless format, there are only two major factors that are likely to impact the file size:
How many pixels are in the file, and
How well the format can be compressed.
Since it sounds like you're doing an overlay, I'm guessing #1 is not changing. Compare the pixel dimensions of the input and output files to double-check this.
Most likely you're seeing issues because your merged image is more complicated, so the PNG filtering algorithms have a harder time compressing the files. There's not much you can do about this, short of changing the images or switching to a lossy file format.
To explain just a bit further, let's say you have one all-white image and one all-red image. Both are 100x100 pixels. These images would be really easy to compress because you'd just need to encode: repeat red 10000 times. Now, say you merge these images in a way that every other pixel comes from a different image. Now it's checkered. If you have a good encoding mechanism set up, you might still be able to encode this well by saying: repeat [red,white] 10000 times. But you'll notice even with this ideal encoding algorithm, I've increased the size of my encoded message by quite a bit. And if you don't have an encoding format that's perfectly ideal for this sort of thing, it all goes downhill from there.
In general, the more varied and random-seeming the pixels of your image are, compared to one another, the larger the resulting file will be.
Save the image as JPEG with a higher compression ratio/lower quality. For further details see:
How to decrease image thumbnail size in java
This answer to "Java Text on Image" for an SSCCE.

GoogleAppEngine: how to prevent image resize failure for big images

I've got to manage some images taken from the client.
Up to now, my code is the following:
Image oldImage = null;
try {
oldImage = ImagesServiceFactory.makeImage(imageBuf);
} catch (Exception e) {
log.severe("Cannot make the image due to " + e);
return;
}
int newWidth = ??; //how can I guess this value to reach a size < 1MB??
int newHeight= ??; //how can I guess this value to reach a size < 1MB??
Transform resize = ImagesServiceFactory.makeResize(newWidth, newHeight);
Image newImage = imagesService.applyTransform(resize, oldImage); //Exception if > 1MB
Such images may exceed the size limit of GAE (1 MB), and I'd like to resize or crop them in order to reach a size of little less than 1 MB (say, 950kB).
How can I achieve this goal?
I cannot directly use the resize method, as I don't know the exact width & height which the image should be resized to.
Moreover, depending on the encoding of the picture (png, jpeg, etc.) the size of the image may vary also using fixed height and width.
I really have no efficient ideas on how to face this issue: the only way I can see a solution is to continue trying with lower and lower dimensions until I get a good-sized image, but this is obviously not acceptable in terms of performances...
Please, help me!!
Thank you very much
Bye
cghersi
If you store the images in the blobstore, you can resize much larger images than you can passing the image through your appserver, by using the getServingUrl() function. Since this does not pass its data through the API, it's not limited by the API response size limits.

Categories