I'm loading a jpeg-file via BitmapFactory and try to save it again (later I want to do some calculation on the pixel data before I save it again).
But if I try to save it with
FileOutputStream fos = new FileOutputStream(new File("/sdcard/test.jpg"));
originalImage.compress(Bitmap.CompressFormat.JPEG, 100, fos);
then it is not exactly the same result as in the original picture. Some pixel have got different color values and this ist not useful for my later calculation.
Is there a possibility to safe it lossless? Or is the problem already when I load the picture with
Bitmap originalImage = BitmapFactory.decodeFile("/sdcard/input.jpg");
few lines before?
Is there a possibility to safe it lossless?
No. The JPEG format uses a lossy compression. It makes no formal guarantees even if you set the quality to 100.
Or is the problem already when I load the picture with [...]
No, bitmaps are... maps of bits, i.e. they represent the exact bits of the image data.
Related
I had a question concerning jpg image creation ImageIO.write(imgStega, "jpeg", file) :
I am doing some steganography, and I have to hide data in least significant bit of each pixel. I do this with getRGBA()[pos], which provide me Red, Blue, Green, Alpha components. Then I change each value with a +1 or -1 depending on a %2.
The problem is, every time I use ImageIO.write, it changes all my image at random (it is compressing). So, how can I save my image as it is ? I don't see any solution to do steganography on a real image.
Whether I use png or jpg is the same, the weight changes. Do you know a way to save my image the way it is ?
Thanks in advance !
JPEG is lossy by definition, so the data modifications that you see are expected and there is not much you can do about it in your context.
On the other hand, PNG is also compressed but in a lossless manner. The size of the png file changes because the png compression is similar to regular file compression (called LZ): very grossly explained, it detects repeated byte patterns and encodes them in fewer bytes. Changing the bytes of your image changes these patterns, and this may change the efficiency of the compression. You could as well see an increase in size. But when an application opens your modified image, it should see exactly the bytes that you have stored.
Is the change of size a concern because this might allow someone to detect your modifications? In that case, I don't see any other solution than using only uncompressed formats.
I use Base64 system for encode from image to string this code
Bitmap bitmap = BitmapFactory.decodeFile(picturePath);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, stream);
byte[] image = stream.toByteArray();
String img_str = Base64.encodeToString(image, 0);
and decode this code
byte[] decodedString = Base64.decode(decode, Base64.NO_WRAP);
Bitmap decodedByte = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
imageView.setImageBitmap(decodedByte);
but string is too long, very very long. I can't use this way. How can I do short string ?,
You can't. Images typically contain a lot of data. When you convert that to text as base64 it becomes even bigger (4 characters for every 3 bytes). So yes, that will typically be very long if it's a large image.
You could compress the image more heavily in order to reduce the size, but eventually it will be hard to even recognize as the original image - and may well be quite large even so.
Another way of reducing the size in bytes is to create a smaller image in terms of the number of pixels - for example, shrinking a 1000x1000 image to 100x100... is that an option in your case?
You haven't given us much context, but could you store the data elsewhere and then just use a URL instead?
I believe the only answer for this is that if you want a shorter string, you should use smaller image
Depends on the size of the image. A larger image is gonna yield a larger string. Images contain a lot of data. That is why people usually only do base64 encoding for very small images like icons, etc.
You could try reducing the quality of the JPEG compression, but I doubt you'd save much space. Reducing the dimensions (if possible) of the image would probably save some space. Either way, doing base64 on anything larger than a really small gif or png image is almost always counter productive.
I'm a bit of a Java noob, and I have read some basics about sockets and I can successfully send images over socket using ImageIO, but I want to reduce the amount of data that is sent. Ultimately I want the image (screen capture) to be send as fast as possible with the smallest possible file size.
Right now, I have imageIO set up as such;
DataInputStream in=new DataInputStream(client.getInputStream());
DataOutputStream out = new DataOutputStream(client.getOutputStream());
ImageIO.write(captureImg(),"JPG",client.getOutputStream());
And the receiver:
BufferedImage img=ImageIO.read(ImageIO.createImageInputStream(server.getInputStream()));
File outputfile = new File("Screen"+(date.toString())+".jpg");
ImageIO.write(img, "jpg", outputfile);
In case you're wondering, this is my method that is used to take the image.
Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
BufferedImage capture = new Robot().createScreenCapture(screenRect);
I have heard about Byte arrays, where you can send the bytes then draw the image at the other end. However I'm not sure if this is more efficient.
Any help would be greatly appreciated, please ask if you would like me to add any extra info or code for the byte array!
Thanks.
EDIT: Patrick:
ByteArrayOutputStream bScrn = new ByteArrayOutputStream();
ImageIO.write(captureImg(), "JPG", bScrn);
byte imgBytes[] = bScrn.toByteArray();
out.write((Integer.toString(imgBytes.length)).getBytes());
out.write(imgBytes,0,imgBytes.length);
There already has been an extensive discussion in the comments, but to summarize a few points that I find important:
You have a trade-off between several criteria:
Minimize network traffic
Minimize CPU load
Maximize image quality
You can reduce the network traffic with a high image compression. But this will increase the CPU load and might reduce the image quality.
Whether it reduces the image quality depends on the compression type: For JPG, you can make the image arbitrarily small, but the quality of the image will then be ... well, arbitrarily bad. For PNG, the image quality will stay the same (since it is a lossless compression), but the CPU load and the resulting image size may be greater.
The option of ZIPping the image data was also mentioned. It is true that ZIPping the JPG or PNG data of an image will hardly reduce the amount of data (because the data already is compressed). But compressing the raw image data can be a feasible option, as an alternative to JPG or PNG.
Which compression technique (JPG, PNG or ZIP) is appropriate also depends on the image content: JPG is more suitable for "natural" images, like photos or rendered images. These can withstand a high compression without causing artefacts. For artifical images (like line drawings), it will quickly cause undesirable artefacts, particularly at sharp edges or when the image contains texts. In contrast to that: When the image contains large areas with a single color, then a compression like PNG (or ZIP) can reduce the image size due to the "run length compression" nature of these compression methods.
I already made some experiments for such an image transfer quite a while ago, and implemented it in a way that easily allowed tweaking and tuning these parameters and switching between the different methods, and comparing the speed for different application cases. But from the tip of my head, I can not give a profound summary of the results.
BTW: Depending on what you actually want to transfer, you could consider obtaining the image data with a different technique than Robot#createScreenCapture(Rectangle). This method is well-known to be distressingly slow. For example, when you want to transfer a Swing application, you could let your application directly paint into an image. Roughly with a pattern like
BufferedImage image = new BufferedImage(w,h,type);
Graphics g = image.getGraphics();
myMainFrame.paint(g);
g.dispose();
(This is only a sketch, to show the basic idea!)
Additionally, you could consider further options for increasing the "percieved speed" of such an image transfer. For example, you could divide your image into tiles, and transfer these tiles one after another. The receiver will possibly appreciate it if the image would at least be partially visible as quickly as possible. This idea could be extended further. For example, by detecting which tiles have really changed between two frames, and only transfer these changed tiles. (This approach could be extended and implemented in a rather sophisticated way, by detecting the "minimum regions" that have to be transferred)
However, for the case that you first want to play around with the most obvious tuning parameter: Here is a method that allows writing a JPG image with a quality value between 0.0 and 1.0 into an output stream:
public static void writeJPG(
BufferedImage bufferedImage,
OutputStream outputStream,
float quality) throws IOException
{
Iterator<ImageWriter> iterator =
ImageIO.getImageWritersByFormatName("jpg");
ImageWriter imageWriter = iterator.next();
ImageWriteParam imageWriteParam = imageWriter.getDefaultWriteParam();
imageWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
imageWriteParam.setCompressionQuality(quality);
ImageOutputStream imageOutputStream =
new MemoryCacheImageOutputStream(outputStream);
imageWriter.setOutput(imageOutputStream);
IIOImage iioimage = new IIOImage(bufferedImage, null, null);
imageWriter.write(null, iioimage, imageWriteParam);
imageOutputStream.flush();
}
I am using a common format all over application for images as png.Any jpg image uploaded still gets saved as png using code as below.
java.awt.image.BufferedImage bufferedImage = ImageIO.read(jpgImagePAth);
if(!IsExtensionPng(jpgImagePath)){
ImageIO.write(bufferedImage, "png", new File(pptFolder, justNamePng));
}
But this preserves alpha even though it was not there in the jpg so makes a 2MB Image 7MB and 6MB to 16MB . Is there anyway to save png without maintaining the alpha ?
The reason I need to conver to PNG is that later on when I add text on image it looses the actual resolution. I already tried loseless JPEG which didnt fix it.
It's not the alpha channel that is causing the file size to grow, it's the file type. JPG uses lossy compression; PNG is lossless compression. In other words, JPG is throwing out some data to reduce the size of the file. That's why you get to choose a "quality" level when saving to JPG - that determines how much is thrown out.
How do you know you're getting the alpha channel anyway? If you still want PNG and want to be sure you're dropping the alpha channel, set the image type to BufferedImage.TYPE_RGB, e.g.
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_RGB);
You'll have to get the graphics object associated with your new BufferedImage and copy the jpg onto it, then write it out. This question isn't quite the same as yours but has sample code you may find useful.
Paul
I don't know exactly in what situation you are. But I should keep JPEG JPEG. The only advantage of converting JPEG to PNG is wasting hdd space.
I am generating lots of images in java and saving them through the ImageIO.write method like this:
final BufferedImage img = createSomeImage();
ImageIO.write( img, "png", new File( "/some/file.png" );
I was happy with the results until Google's firefox addon 'Page Speed' told me that i can save up to 60% of the size if i optimize the images. The images are QR codes, their size is around 900B each and the firefox-plugin optimized versions are around 300B.
I'd like to save such optimized 300B Images directly from java.
So here my question again: How to save optimized png images with java's ImageIO?
Use PngEncoderB to convert your BufferedImage into a PNG encoded byte array.
You can apply a filter to it, which helps prepare the image for better optimization. This is what OptiPNG does, only OptiPNG calculates which filter will get you the best compression.
You might have to try applying each filter to see which one is consistently better for you. With 2 bit color, I think the only filter that might help is "up", so I'm guessing that's the one to use.
Once you get the image to a PNG encoded byte array, you can write that directly to a file.