Size prediction of a scaled down image - java

I have a JPEG file of 2550x3300 size (this file was created with quality level 90). The physical size of the file is 2.5 MB. I would like to scale down this image to 1288x1864 (50% of the original dimension) and save with same quality 90. But I want to know the physical size of the down sampled image in advance, even before doing the actual scale down.
Any help is appreciated!
Here is the code I am using,
`
//Decodes the existing file to bitmap
Bitmap srcBP = BitmapFactory.decodeFile(filePath, options);
//Calculates required width and height
int reqWidth = options.outWidth * .50;
int reqHeight = options.outHeight * .50;
//Creates scaled image
Bitmap outBP = Bitmap.createScaledBitmap(srcBP, reqWidth, reqHeight, false);
//Save modified as JPEG
File tempFile = new File(imageOutPath);
FileOutputStream out = new FileOutputStream(tempFile);
outBP.compress(Bitmap.CompressFormat.JPEG, compression, out);
out.close();
`

Is hard to predict the size of the image after compression because compression depends on the actual content of the image, some images compress to smaller size than others even they have the same dimensions. The approach I would suggest is to try and compress the image in memory like a byte array, then get the size of this array and that would be the size of the file give or take a few bytes. This code taken from another answer and modified a little bit to your needs:
Java BufferedImage JPG compression without writing to file
ByteArrayOutputStream compressed = new ByteArrayOutputStream();
ImageOutputStream outputStream =
ImageIO.createImageOutputStream(compressed);
ImageWriter jpgWriter =
ImageIO.getImageWritersByFormatName("jpg").next();
ImageWriteParam jpgWriteParam = jpgWriter.getDefaultWriteParam();
jpgWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
jpgWriteParam.setCompressionQuality(0.9f);
jpgWriter.setOutput(outputStream);
int reqWidth = options.outWidth * .50;
int reqHeight = options.outHeight * .50;
BufferedImage img = new BufferedImage(reqWidth , reqHeight, BufferedImage.TYPE_INT_ARGB);;
try {
img = ImageIO.read(new File(filepath));
} catch (IOException e) {
}
jpgWriter.write(null, new IIOImage(img, null, null), jpgWriteParam);
jpgWriter.dispose();
byte[] jpegData = compressed.toByteArray()
Now jpegData.size() will be very close to the size of your image in bytes.

Related

Java Image BufferedImage loosing aspect ratio of upright image

I am storing an Image in the filesystem like:
FileImageOutputStream fos = new FileImageOutputStream(newFile);
int len;
while ((len = zis.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
fos.close();
And all images are stored correctly in the filesystem. (Also with correct aspect ratio with width and height!)
However, later, I am loading my image like:
File imgFile ...
FileImageInputStream stream = new FileImageInputStream(imgFile);
BufferedImage srcImage = ImageIO.read(stream);
And I want to get the width and height for my images like:
int actualHeight = srcImage.getHeight();
int actualWidth = srcImage.getWidth();
This works totally fine for images in landscape format.
However, for images in upfront format, the width and height is always swapped, so that the width is the height of the orginial image. So, e.g. I have an image that is 200x500 (width x height) and the two integers above are actualHeight = 200 and actualWidth = 500.
Am I missing something?
Most likely, your images are Exif images ("Exif-in-JPEG") from a digital camera/phone. For such images, the pixel data is often stored in the "natural" orientation of the sensor, which is always the same (usually landscape). For portrait images, the orientation is only stored in the Exif metadata, and the ImageIO JPEG plugin doesn't take this orientation into account.
Some cameras, like my Canon DSLR, has an option to do in-camera rotation to match the orientation, but this feature is typically disabled by default. This is obviously only a possible fix if you control the input images.
To fix this in the Java side, you hava some options. You already mentioned using Thumbnailator:
BufferedImage srcImage = Thumbnails.of(new FileInputStream(imgFile))
.scale(1)
.asBufferedImage();
Another option is to use EXIFUtilities from TwelveMonkeys (I'm the author of that library):
IIOImage image = EXIFUtilities.readWithOrientation(imgFile);
BufferedImage srcImage = (BufferedImage) image.getRenderedImage();
Or, if you don't need the other metadata for anything:
BufferedImage srcImage = (BufferedImage) EXIFUtilities.readWithOrientation(imgFile)
.getRenderedImage();

How can I convert png/gif/jpg in grails to jpg maintaining a high quality in the image?

I use the following method for converting images to jpg. The problem with my solution is that it will reduce the quality to much.
What is a good way to maintain quality of the image, while reducing the file size?
def convertToJpg(currentImage) {
try {
InputStream inStreamCrop = new ByteArrayInputStream(currentImage)
BufferedImage bufferedImage = ImageIO.read(inStreamCrop)
// create a blank, RGB, same width and height, and a white background
BufferedImage newBufferedImage = new BufferedImage(bufferedImage.getWidth(),
bufferedImage.getHeight(), BufferedImage.TYPE_INT_RGB);
newBufferedImage.createGraphics().drawImage(bufferedImage, 0, 0, Color.WHITE, null);
ByteArrayOutputStream baos=new ByteArrayOutputStream()
// write to jpeg file
ImageIO.write(newBufferedImage, "jpg", baos);
baos.flush()
def image = baos.toByteArray()
baos.close()
return image
} catch (IOException e) {
e.printStackTrace();
}
}
You can control the quality of the JPEG by getting an ImageWriter object and setting it through the parameters. For example:
import javax.imageio.stream.*
import javax.imageio.*
BufferedImage bufferedImage = ImageIO.read(new File("test.png"));
float quality = 0.9;
ImageWriter writer = ImageIO.getImageWritersByFormatName("jpg").next();
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(quality);
writer.setOutput(new FileImageOutputStream(new File("test.jpg")));
writer.write(null, new IIOImage(bufferedImage, null, null), param);
The quality parameter can range between 0 and 1, with 1 having the least compression. Try different values; I've found 0.9 is a good choice for low compression (but large file sizes).
Note that JPEG images are not supported with OpenJDK: ImageIO not able to write a JPEG file

antialiasing in java image scaling library

I am currently using http://code.google.com/p/java-image-scaling/ this library to generate scaled images for my web app.But when I scale down the image to about 100x100 size there are some leftover artifacts visible in some images. Is this an issue with antialiasing? And how do I use antialiasing with this library.The api documentation doesn't say any thing about it.
Here is the code
File f = new File("C:\\Users\\ad min\\Pictures\\30-whisky-3d-wallpaper-1152x864.jpg");
BufferedImage src = ImageIO.read(f);
//ResampleOp resampleOp = new ResampleOp(76, 76);
ResampleOp resampleOp = new ResampleOp(200,200);
resampleOp.setUnsharpenMask(AdvancedResizeOp.UnsharpenMask.VerySharp);
BufferedImage rescaled = resampleOp.filter(src, null);
ImageIO.write(rescaled, "JPG", new File(
"C:\\Users\\ad min\\Pictures\\scaleddown.jpg"));
what am I doing wrong?
I finally didn't need antialiasing I simply used this code given in the foloowing link and it worked :) whewww
http://www.universalwebservices.net/web-programming-resources/java/adjust-jpeg-image-
compression-quality-when-saving-images-in-java
Iterator<ImageWriter> iter = ImageIO
.getImageWritersByFormatName("jpeg");
ImageWriter writer = (ImageWriter) iter.next();
// instantiate an ImageWriteParam object with default compression
// options
ImageWriteParam iwp = writer.getDefaultWriteParam();
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
iwp.setCompressionQuality(1); // an integer between 0 and 1
// 1 specifies minimum compression and maximum quality
File file = new File("C:\\Users\\ad min\\Pictures\\scaleddown.jpg");
FileImageOutputStream output = new FileImageOutputStream(file);
writer.setOutput(output);
IIOImage image = new IIOImage(rescaled, null, null);
writer.write(null, image, iwp);
writer.dispose();

encoding and decoding in JPG images in Android

I am creating an application over Android where I need to manipulate my JPG files. I am not getting much of header information for JPG format so for that I am converting it to Bitmap, manipulated the pixel values in bitmap and then again convert it back to JPG.
Here what problem I am facing is- after manipulating only some pixels of bitmap and
converting it back to JPG, I do not get the same set of pixels I got earlier (for those pixels which I did not manipulate). I am getting the same image as the original in the new image. But when I check new image pixels values for decoding, the untouched pixels are different...
File imagefile = new File(filepath);
FileInputStream fis = new FileInputStream(imagefile);
Bitmap bi = BitmapFactory.decodeStream(fis);
int intArray[];
bi=bi.copy(Bitmap.Config.ARGB_8888,true);
intArray = new int[bi.getWidth()*bi.getHeight()];
bi.getPixels(intArray, 0, bi.getWidth(), 0, 0, bi.getWidth(), bi.getHeight());
int newArray[] = encodeImage(msgbytes,intArray,mbytes); // method where i am manipulating my pixel values
// converting the bitmap data back to JPG file
bi = Bitmap.createBitmap(newArray, bi.getWidth(), bi.getHeight(), Bitmap.Config.ARGB_8888);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bi.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] data = baos.toByteArray();
Bitmap bitmapimage = BitmapFactory.decodeByteArray(data, 0, data.length);
String filepath = "/sdcard/image/new2.jpg";
File imagefile = new File(filepath);
FileOutputStream fos = new FileOutputStream(imagefile);
bitmapimage.compress(CompressFormat.JPEG, 100, fos);
Help me if I am wrong somewhere or whether I should use some other method to manipulate JPG pixel values...
JPEG is an image format that is usually based on lossy compression. That means that some information that is not important for the human eye is thrown away to further shrink the file size. Try to save your image as a PNG (a lossless format).
Be careful with using
Bitmap bi = BitmapFactory.decodeStream(fis);
bi = bi.copy(Bitmap.Config.ARGB_8888, true);
At the point where you have the first bi you may have already lost a lot of information, instead try using BitmapFactory.Options to force 8888 (which is the default too):
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
options.inDither = false;
Bitmap bi = BitmapFactory.decodeStream(fis, options);
If you stay with copy you should still recycle() the one that you throw away.

How to save jpeg Image file size after read--rotate-write operations in Java?

Im trying to read a JPEG image as BufferedImage, rotate and save it as another jpeg image from file system. But there is a problem : after these operations I cannot proceed same file size.
Here the code
//read Image
BufferedImage img = ImageIO.read(new File(path));
//rotate Image
BufferedImage rotatedImage = new BufferedImage(image.getHeight(),
image.getWidth(), BufferedImage.TYPE_3BYTE_BGR);
Graphics2D g2d = (Graphics2D) rotatedImage.getGraphics();
g2d.rotate(Math.toRadians(PhotoConstants.ROTATE_LEFT));
int height=-rotatedImage.getHeight(null);
g2d.drawImage(image, height, 0, null);
g2d.dispose();
//Write Image
Iterator iter = ImageIO.getImageWritersByFormatName("jpeg");
ImageWriter writer = (ImageWriter)iter.next();
// instantiate an ImageWriteParam object with default compression options
ImageWriteParam iwp = writer.getDefaultWriteParam();
try {
FileImageOutputStream output = null;
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
iwp.setCompressionQuality(0.98f); // an integer between 0 and 1
// 1 specifies minimum compression and maximum quality
File file = new File(path);
output = new FileImageOutputStream(file);
writer.setOutput(output);
IIOImage iioImage = new IIOImage(image, null, null);
writer.write(null, iioImage, iwp);
output.flush();
output.close();
writer.dispose();
Is it possible to access compressionQuality parameter of original jpeg image in the beginning. when I set 1 to compression quality, the image gets bigger size. Otherwise I set 0.9 or less the image gets smaller size. How can i proceed the image size after these operations?
Thank you,
What do you mean - do you want to read the compression rate of the original file?
Please note that even with the same compression settings, the filesize might vary, due to the JPEG compression algorithm. So a rotated image does not always have the exact same size as the unrotated/original version, even if all options (like compression rate, quality settings etc.) are the same.
If there is really now way to read the compression quality from the metadata, as a last resort, what you could do is use a binary search for the quality. Start with 0.5, if the file is too small try 0.75 and so on (up to 5 tries or so). This is a bit slow of course, but depending on your use case it might be OK.

Categories