I need to compress every image that is larger than 500 kbytes.
So, i'm trying to create a code that will test every compression quality until i get <= 500 kb, then i'll have the best quality with the lowest length.
My biggest problem here is that writer method from ImageWriter appends my new image to the old. So, if i have a 600kb image and write a new one with low quality, i'll have 600kb + probably 500kb (size of the new image) in the same .jpg file and with a low quality.
My code:
public byte[] imageCompressor(String filePath, String newFileName, String formatName) throws IOException {
File input = new File(filePath);
BufferedImage image = ImageIO.read(input);
File compressedImageFile = new File(newFileName);
String compressedWithFormat = compressedImageFile + "." + formatName;
OutputStream os = new FileOutputStream(compressedWithFormat);
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(formatName);
ImageWriter writer = (ImageWriter) writers.next();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageOutputStream ios = ImageIO.createImageOutputStream(os);
writer.setOutput(ImageIO.createImageOutputStream(baos));
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
writer.write(null, new IIOImage(image, null, null), param);
float contador = 1f;
while (imageSizeChecker(compressedWithFormat) > 500) {
param.setCompressionQuality(contador -= 0.09f); // Change the quality value you prefer
writer.write(null, new IIOImage(image, null, null), param);
System.out.println(imageSizeChecker(compressedWithFormat));
writer.dispose();
}
writer.dispose();
return baos.toByteArray();
}
public Long imageSizeChecker(String filePath) {
File insertFile = new File(filePath);
Long fileSize = insertFile.length() / 1024;
return fileSize;
}
Also, i want to return the image in a byte array (as it follows), and i'm trying to send the image file on the parameter as a byte array (replacing String filepath for byte[] filepath)
You keep writing multiple images to the same ByteArrayOutputStream. To reset the output stream and discard the data between each image, you can use:
baos.reset();
There are several other problems -- such as inspecting a file you never write -- but if you're finding that appended data is the major problem, this seems to be why.
Related
I'm trying to compress an image to upload to S3 Bucket but after the image is compressed, the size of the compressed image is larger than the original. From 227KB -> 236KB
Can anybody explain for me why this happened?
Here's my code:
BufferedImage bufferedImage = ImageIO.read(photoFile);
File compressedImageFile = new File("compressed_image.jpeg");
OutputStream outputStream = new FileOutputStream(compressedImageFile);
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpeg");
ImageWriter imageWriter = writers.next();
ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(outputStream);
imageWriter.setOutput(imageOutputStream);
ImageWriteParam imageWriteParam = imageWriter.getDefaultWriteParam();
if (imageWriteParam.canWriteCompressed()) {
imageWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
imageWriteParam.setCompressionQuality(0.5f);
}
imageWriter.write(null, new IIOImage(bufferedImage, null, null), imageWriteParam);
log.info("Close stream");
outputStream.close();
imageOutputStream.close();
imageWriter.dispose();
Here's my image:
original - 227KB
compressed - 236KB
I tried to change the compression quality but it doesn't seem working, and this problem happens in some case, not all
jpeg images are already compressed. “Compressing” an already compressed file makes it a bit bigger (header data) each time.
A text file on the other hand will get much smaller.
my problem is when using imageio.write i am seeing that is using hdd, also read about jDeli (but too expensive), Apache Commons, JAI that are much faster....
I wanna use the encoded bytes returned by routine... at a custom Remote Desktop Utility...
public static byte[] imageToJPEGByteArray(Image aImage, int width, int height, int qualityPercent) throws IOException {
byte[] imageBytes = new byte[0];
float quality = 75 / 100f;
BufferedImage destImage;
destImage = SwingFXUtils.fromFXImage(aImage, null);
// Output JPEG byte array
ByteArrayOutputStream baos = new ByteArrayOutputStream();
if(qualityPercent != -1) {
// Start to create JPEG with quality option
ImageWriter writer = null;
Iterator iter = ImageIO.getImageWritersByFormatName("gif");
if (iter.hasNext()) {
writer = (ImageWriter) iter.next();
}
ImageOutputStream ios = ImageIO.createImageOutputStream(baos);
writer.setOutput(ios);
ImageWriteParam iwparam = new JPEGImageWriteParam(Locale.getDefault());
iwparam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
iwparam.setCompressionQuality(quality);
writer.write(null, new IIOImage(destImage, null, null), iwparam);
ios.flush();
writer.dispose();
ios.close();
// Done creating JPEG with quality option
} else {
// This one line below created a JPEG file without quality option
ImageIO.write(destImage, "gif", baos);
}
baos.flush();
imageBytes = baos.toByteArray();
baos.close();
// Done
return imageBytes;
}
If you are saying that you've observed that this code appears to cause disk activity when saving to ByteArrayOutputStream perhaps you should try setting the ImageIO "use cache" flag to false:
ImageIO.setUseCache(false);
Javadoc for setUseCache says:
Sets a flag indicating whether a disk-based cache file should be used when creating ImageInputStream and ImageOutputStreams.
The byte[] content is the content of the image in the database. So after getting it I am checking its size and in case of size of 2MB I am compressing it then I want to update the entity in the database.
Currently I am able to write the compressed image as a jpg file. How can write the image as Outputstream without the need of compressedImageFile? Then I want to convert it to byte array and update the entity in the database.
private void compressImage(byte[] content){
try {
InputStream inputStream = new ByteArrayInputStream(content);
BufferedImage image = ImageIO.read(inputStream);
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg");
ImageWriter imageWriter = writers.next();
//With this two lines I can write the content to the file.
//File compressedImageFile = new File("C:\\Users\\photos\\temp\\image.jpg");
//OutputStream os = new FileOutputStream(compressedImageFile);
//OutputStream os = new OutputStream();
ImageOutputStream ios = ImageIO.createImageOutputStream(os);
imageWriter.setOutput(ios);
getLogger().info("ios: " + ios);
ImageWriteParam param = imageWriter.getDefaultWriteParam();
if (param.canWriteCompressed()) {
System.out.println("Yes it can write compress!");
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
String[] types = param.getCompressionTypes();
param.setCompressionQuality(0.5f);
System.out.println("canWriteCompressed is true");
}
imageWriter.write(null, new IIOImage(image, null, null), param);
getLogger().info("ios at the end: " + ios);
} catch (IOException e) {
e.printStackTrace();
}
}
Code works fine for other formats of images but for .png compression is negligible or no compression at all
here is a sample how the image is loaded and saved
Or someone suggest me better way of compressing png image
This is myfirst question posted on stackoverflow. please ignore any mistakes
I have taken sample images from local folder
File input = new File("C:/Users/Public/Pictures/Sample Pictures/bz_nela19911.png");
InputStream is = new FileInputStream(input);
BufferedImage image = ImageIO.read(is);
File compressedImageFile = new File("C:/Users/Public/Pictures/Sample Pictures/me1.png");
OutputStream os =new FileOutputStream(compressedImageFile);
Iterator<ImageWriter>writers = ImageIO.getImageWritersByFormatName("png");
// works fine for other formats of images
ImageWriter writer = null;
while (writers.hasNext()) {
ImageWriter candidate = writers.next();
if (candidate.getClass().getSimpleName().equals("CLibPNGImageWriter")) {
writer = candidate; // This is the one we want
break;
}
else if (writer == null) {
writer = candidate;
}
}
ImageOutputStream ios = ImageIO.createImageOutputStream(os);
writer.setOutput(ios);
ImageWriteParam param = writer.getDefaultWriteParam();
if (param.canWriteCompressed()) {
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(0.1f);
}
writer.write(null, new IIOImage(image, null, null), param);
i need to store on disk a base64 image but i have an error: "Out of memory" when i decode base64 image into byte[]. The size image is about 6MB
This is my code:
byte[] decodedBytes = DatatypeConverter.parseBase64Binary(photo); //HERE I HAVE THE ERROR!!
log.debug("binary ok");
BufferedImage bfi = ImageIO.read(new ByteArrayInputStream(decodedBytes));
String nomeEdata = String.valueOf(Calendar.getInstance().getTimeInMillis() + ".jpg");
String nomeImg = resourceBundle.getString("schede.pathSaveImage") + nomeEdata;
File outputfile = new File(nomeImg);
ImageIO.write(bfi , "png", outputfile);
bfi.flush();
Please, Any suggests?
You could write the "photo" content to a temporary file and then read from it using a Base64InputStream.
In the end, however, the BufferedImage will have the entire raw image in memory. This will require that you have a heap size large enough to accommodate this. You may just have to increase the Xmx value.
final BufferedImage bi = ImageIO.read(new Base64InputStream(new ReaderInputStream(new StringReader(photo), "ascii"));
final File file = ...
final FileOutputStream fos = new FileOutputStream(file);
try
{
ImageIO.write(bi, "png", new Base64OutputStream(fos));
}
finally
{
fos.close();
}
http://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/input/ReaderInputStream.html