ImageIO.write slow? - java

I've an application where I'm writing around 25 png image files to disk every second.
BufferedImage img = getBufferedImage();
// code below is very slow ~150ms.
File file = new File(count++ + ".png");
BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(file));
ImageIO.write(img, "png", os);
It usually takes 150ms per call, and achieving 25fps hence becomes impossible. Can I buffer IO so that I don't drop any frames?

PNG encoding takes a while and you can't improve it with any buffering ... if you want a speed up, use BMP (which will eat up your HDD) or if pixel-quality is not needed, try JPG (which should get encoded faster than PNG).

Related

Write animated-gif stored in BufferedImage to java.io.File Object

I am reading a gif image from internet url.
// URL of a sample animated gif, needs to be wrapped in try-catch block
URL imageUrl = new Url("http://4.bp.blogspot.com/-CTUfMbxRZWg/URi_3Sp-vKI/AAAAAAAAAa4/a2n_9dUd2Hg/s1600/Kei_Run.gif");
// reads the image from url and stores in BufferedImage object.
BufferedImage bImage = ImageIO.read(imageUrl);
// creates a new `java.io.File` object with image name
File imageFile = new File("download.gif");
// ImageIO writes BufferedImage into File Object
ImageIO.write(bImage, "gif", imageFile);
The code executes successfully. But, the saved image is not animated as the source image is.
I have looked at many of the stack-overflow questions/answers, but i am not able to get through this. Most of them do it by BufferedImage frame by frame which alters frame-rate. I don't want changes to the source image. I want to download it as it is with same size, same resolution and same frame-rate.
Please keep in mind that i want to avoid using streams and unofficial-libraries as much as i can(if it can't be done without them, i will use them).
If there is an alternative to ImageIO or the way i read image from url and it gets the thing done, please point me in that direction.
There is no need to decode the image and then re-encode it.
Just read the bytes of the image, and write the bytes, as is, to the file:
try (InputStream in = imageUrl.openStream()) {
Files.copy(in, new File("download.gif").toPath());
}

Exception on sending big files through java sockets

I'm creating a Chat in java for a university project, and one of the requirements is each user must have an image associate, this can be done through registration windows and data modification windows, in registration everything works great, but on the modification window, the program throws an exception when i try to send big files, both codes (registration and modification) are basiccally the same, changing only variables and path, but still gives my problem only in modification
Here is my code:
Client:
BufferedImage image = ImageIO.read(new File(usuario.getImagen().getCanonicalPath()));
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(image, "jpg", byteArrayOutputStream);
byte[] size = ByteBuffer.allocate(4).putInt(byteArrayOutputStream.size()).array();
salida.write(size);
salida.write(byteArrayOutputStream.toByteArray());
salida.flush();
Server:
dir = new File ("." + "/Documentos/Imagenes de Verificacion/" +
usuarioRegistro.getNombreDeUsuario() + ".jpg");
sizeAr = new byte[4];
entrada.read(sizeAr);
size = ByteBuffer.wrap(sizeAr).asIntBuffer().get();
imageAr = new byte[size];
entrada.readFully(imageAr);
image = ImageIO.read(new ByteArrayInputStream(imageAr));
ImageIO.write(image, "jpg", new File(dir.getCanonicalPath()));
usuarioRegistro.setImagen(dir.getCanonicalFile());
And the exception is:
Exception in thread "Thread-0" java.lang.IllegalArgumentException: image == null!
at javax.imageio.ImageTypeSpecifier.createFromRenderedImage(ImageTypeSpecifier.java:925)
at javax.imageio.ImageIO.getWriter(ImageIO.java:1591)
at javax.imageio.ImageIO.write(ImageIO.java:1520)
at com.ucab.javachat.Servidor.model.ServidorModel.run(ServidorModel.java:198)
The line ServiorModel.java:198 is: ImageIO.write(image, "jpg", new File(dir.getCanonicalPath()));
In my tests i can send images of 20, 30, 80, 200 Kb, but when i try to send the 2.1mb file gives the error.
I think this i related with some data loose on the byteArray (maybe header data?) but what i dont know is how to fix it, my register window method uses the same sockets and OutputStream to send data and i succesfully send a 24mb image.
As per the documentation:
Returns a BufferedImage as the result of decoding a supplied File with
an ImageReader chosen automatically from among those currently
registered. The File is wrapped in an ImageInputStream. If no
registered ImageReader claims to be able to read the resulting stream,
null is returned. The current cache settings from getUseCacheand
getCacheDirectory will be used to control caching in the
ImageInputStream that is created.
Note that there is no read method that takes a filename as a String;
use this method instead after creating a File from the filename.
This method does not attempt to locate ImageReaders that can read
directly from a File; that may be accomplished using IIORegistry and
ImageReaderSpi.
Make sure you register an ImageReader or wrap your file on a FileInputStream, but since your implementation works I bet it's the image causing issues therefore,
Make sure that your image is of type: GIF, PNG, JPEG, BMP, and WBMP for these are the types supported by the class.

ImageIO.read closes inputs stream

I write images and other data to binary file. When I read image via ImageIO.read(InputStream) from that file, it reads image, it is ok, but method closes given input stream and I cant proceed to read other data.
Why so it is made?
Then how read image without closing stream?
EDIT: It is simple code that writes image and string after into file:
File f = new File("test.bin");
if(f.exists())
f.delete();
f.createNewFile();
DataOutputStream os = new DataOutputStream(new FileOutputStream(f));
BufferedImage img = ImageIO.read(new File("test.jpg"));
ImageIO.write(img, "jpg", os);
os.writeUTF("test string after image");
os.close();
And code that reads all:
DataInputStream is = new DataInputStream(new FileInputStream(f));
BufferedImage img = ImageIO.read(is);
String s = is.readUTF(); // on this line EOFException occurs
System.out.println(s);
NetBeans output:
Exception in thread "main" java.io.EOFException
at java.io.DataInputStream.readUnsignedShort(DataInputStream.java:340)
at java.io.DataInputStream.readUTF(DataInputStream.java:589)
at java.io.DataInputStream.readUTF(DataInputStream.java:564)
at mediamanager.Main.test(Main.java:105)
at mediamanager.Main.main(Main.java:44)
May be I'm doing something wrong?
Quote from the documentation of ImageIO.read(InputStream)
This method does not close the provided InputStream after the read operation has completed; it is the responsibility of the caller to close the stream, if desired.
Emphasis not mine.
The problem is elsewhere. Probably in your code.
I can see two possible causes of such behaviour:
Image reader use buffer to read data from the stream to improve performance. So it reads more data from the stream.
Also image reader could try to read EXIF for already parsed image. Such information usually appended at the end of file to avoid full file rewriting when you are just adding a couple of piece of information about the image.
Try ImageIO.setUseCash(false) it could help.

How can I get a less pixelated screenshot in java?

I am using a BufferedImage in java after capturing the image out of a JFrame. I need some way to sharpen the image such that when it's enlarged it doesn't look so pixelated. Here's the thing it has to keep the same image size.
Here's the code I'm using to capture the image.
private void grabScreenShot() throws Exception
{
BufferedImage image = (BufferedImage)createImage(getSize().width, getSize().height);
paint(image.getGraphics());
try{
//ImageIO.write(image, "jpg", new File(TimeTable.path+"\\TimeLine.jpg"));
ImageIO.write(image, "jpg", new File("C:\\Users\\"+TimeTable.user+"\\AppData\\TimeLineMacroProgram\\TimeLine.jpg"));
//ImageIO.write(image, "jpg", new File("C:\\ProgramData\\TimeLineMacroProgram\\TimeLine.jpg"));
System.out.println("Image was created");
}
catch (IOException e){
System.out.println("Had trouble writing the image.");
throw e;
}
}
And here's the image it creates
JPG is ill suited for screenshots. It's designed for complex and colorful pictures wherein information loss during compression is nearly negligible, such as photos. For screenshots you should rather be using GIF or, better, PNG.
ImageIO.write(image, "png", new File("C:\\Users\\"+TimeTable.user+"\\AppData\\TimeLineMacroProgram\\TimeLine.png"));
You only end up with a bigger file, but you get pixelperfect sharpness and detail back, which is simply impossible with JPG.
I think it's because you're writing it out as a JPEG file.
I'd change the format to something non-lossy, or else force the writer to not use compression by accessing and changing its ImageWriteParam.

Android FileOutputStream creates corrupted file

I have an app that creates multiple files using a byte array it gets from a Socket InputStream. The file saves perfectly when I just save one file, but if I save the one file then re-instantiate the file stream and save a different file, the first file gets corrupted and the second file is saved perfectly. I opened the two files in a text editor and it seems (about...)the first 1/5th of the first file is blank spaces but the second file is full, and they both have the same size properties(9,128,731 bytes). The following example is a duplication of the senario but with the same corruption result:
FileOutputStream outStream;
outStream = new FileOutputStream("/mnt/sdcard/testmp3.mp3");
File file = new File("/mnt/sdcard/test.mp3");
FileInputStream inStream = new FileInputStream(file);
byte[] buffer = new byte[9128731];
inStream.read(buffer);
outStream.write(buffer, 0, buffer.length);
inStream.close();
outStream.flush();
outStream.close();
outStream = null;
outStream = new FileOutputStream("/mnt/sdcard/testmp32.mp3");
outStream.write(buffer, 0, buffer.length);
inStream.close();
outStream.flush();
outStream.close();
outStream = null;
I tried this EXACT code in a regular java application and both files were saved without a problem. Does anyone know why the android is doing this?
Any help would be GREATLY appreciated
As jtahlborn mentioned you cannot assume that InputStream.read(byte[]) will always read as many bytes as you want. As well you should avoid using such a large byte array to write out at once. At least not without buffering, you could potentially overflow something. You can handle these concerns and save some memory by copying the file like this:
File inFile = new File("/mnt/sdcard/test.mp3");
File outFile = new File("/mnt/sdcard/testmp3.mp3");
FileInputStream inStream = new FileInputStream(inFile);
FileOutputStream outStream = new FileOutputStream(outFile);
byte[] buffer = new byte[65536];
int len;
while ((len = inStream.read(buffer)) != -1) {
outStream.write(buffer, 0, len);
}
inStream.close();
outStream.close();
I see some potential issues that can get you started debugging:
You writing to the first output stream before you close the input stream. This is a bit weird.
You can't accurately gauge the similarity/difference between two binary files using a text editor. You need to look at the files in a hex editor (or better, Audacity)
I would use BufferedOutputStream as suggested by the Android docs:
out = new BufferedOutputStream(new FileOutputStream(file));
http://developer.android.com/reference/java/io/FileOutputStream.html
As a debugging technique, print the contents of buffer after the first write. Also, inStream.read() returns an int. I would additionally compare this to buffer.length and make sure they are the same. Regardless, I would just call write(buffer) instead of write(buffer, 0, buffer.length) unless you have a really good reason.
-tjw
You are assuming that the read() call will read as many bytes as you want. that is incorrect. that method is free to read anywhere from 1 to buffer.length bytes. that is why you should always use the return value to determine how many bytes were actually read. there are plenty of streams tutorials out there which will show you how to correctly read from a java stream (i.e. how to completely fill your buffer).
If anyone's having the same problem and wondering how o fix it I found out the problem was being caused by my SD card. I bought a 32gb kingston sd card and just yesterday I decided to try running the same code again accept using the internal storage instead and everything worked perfectly. I also tried the stock 2gb SD card it came with and it also worked perfectly. I glad to know my code works great but a little frustrated I spent 50 bucks on a defective memory card. Thanks for everyones input.

Categories

Resources