Creating animated GIF with ImageIO? - java

Has anybody managed to convince ImageIO to write an animated GIF, and in particular setting the correct metadata? My first attempt is roughly as follows (where b1 and b2 are BufferedImages):
ImageTypeSpecifier spec = new ImageTypeSpecifier(b1);
ImageWriter wr = ImageIO.getImageWriters(spec, "GIF").next();
wr.setOutput(ImageIO.createImageOutputStream(new File("C:\\Flashing.gif")));
ImageWriteParam param = wr.getDefaultWriteParam();
IIOMetadata meta = wr.getDefaultImageMetadata(spec, param);
wr.prepareWriteSequence(meta);
wr.writeToSequence(new IIOImage(b1, null, null), param);
wr.writeToSequence(new IIOImage(b2, null, null), param);
This appears to almost work, but:
I clearly need to somehow set "proper" metadata to set the time between the images and ideally make them loop (I was naively hoping the default would do something sensible, but hey...)
whatever metadata it is writing by default is obviously not quite right: the GIMP gives a couple of error messages when loading the file, although embedding the image in a test web page in Firefox does display the two images in very quick succession, which is tantilisingly close to what I want...
If anyone has got writing animated GIFs from ImageIO to work and can share a snippet of working code or how to fix the above problems, it would be greatly appreciated!

I ran across this question, and decided to try it out; It took a small but non-trivial amount create a usable class (thanks ax) -- so I thought I might share the code around: here is a small class for creating an animated gif image from a number of other images. Archived version

Related

Implementing automatic rotation of JPEG images according to EXIF Orientation in ImageIO plugin

Background
I've been working on an ImageIO plugin for reading JPEGs for a while. One of the features I'd like to implement, is automatic rotating of the image, according to EXIF/TIFF Orientation tag, if present.
I'm not asking how to implement the rotation itself, rather my issue is with the ImageReader API. For many users, simply calling ImageIO.read(myJPEG) is all they ever do, and I think thery're happy with having the image rotated to the correct orientation. However, if you look at the ImageReader API, it has many methods to get information and metadata about the image, and also various ways to read the pixel data.
The question(s)
Should I expose a way to read the data as is in the file, using ImageReadParam (I could do it the other way around too, but that would not allow the common case mentioned above to have the benefit of the functionality)?
I'm working on metadata support (as in IIOMetadata), should the metadata report the orientation tag as written in the file, or updated to reflect the rotated image? This is where I feel things break down... The real issue here, is what happens when someone reads the image and metadata, does some manipulations to the image and stores it again with the metadata (should the writer then do the oposite rotation, or 'magically' discard the orientation tag and update w/h)?
...or optionally, should I just give it up, and leave it to clients to read the EXIF metadata, and apply the rotation themselves if needed?
Input and suggestions from implementors as well as ImageIO users are welcome! :-)
Reasons why not to rotate the image
The orientation is stored in Exif metadata, what is not part of the JPEG/JFIF standard
Reading the Exif metadata adds additional complexity
You can get it wrong if Exif metadata is wrong
You have to modify the Exif metadata if you include it in the modified file.
Rotating the image might be costly in terms of cpu load and memory
Undefined behaviour if you read tiles
Reasons why to rotate the image
Exif metadata is written by almost all digital cameras manufactured after 2010
Most digital cameras do not rotate the image but store only the orientation in the metadata. Probably because additional hardware would be needed.
Because of the last two reasons the Exif orientation can be considered a standard and a piece of information that must be respected.
Many viewers for non-technical users like web browsers rotate the image according to Exif to provide better user experience. Consequently users will consider a view that does not rotate to be incomplete or broken.
Other metadata information like colour conversion is also applied during reading.
I think ImageIO should rotate the image and update the Exif. That way the developer can work with ImageIO without worrying about the image file format. And the behaviour of the new JPEGReader is compatible with the old one.
Additional features would be
to provide a method to read the image unrotated and leave the exif untouched.
to provide a method to write the image with a specifed orientation.
I would not make the JPEG write change its behaviour according to the Exif metadata because there can be a lot of stuff in Exif.
I think we should leave the past behind (Exif not beeing standard) and consider the orientation in Exif to be part of JPEG. But I think we still should ignore all the other Exif information when putting pixels into memory or on the screen ;-)
Given that every image reader in the World automagically flips the image correctly, I'm betting that virtually everybody would be fine with this happening automatically. In a sense, the image "is" the it's rotated version even if the bitmap portion of the image file says different. The whole file is the standard.
And yes, I'd be very happy if ImageIO.read() would just, um, work. It should give me an image that is as it would be displayed by every known viewer on the planet. Sure, include some APIs to get at it the raw way if somebody really wants that, but the default behavior should be the prevailing usage pattern, and the way that makes sense. Why would you ever have a need to view it in the wrong orientation? There might be odd use cases, but I bet they are very rare compared to the normal one.
Any idea when they are going to fix this in the default JDK? Writing my own code to do my own rotation to put the image back felt sick and wrong.

Toolkit.getDefaultToolkit().createImage() vs ImageIO.read()

I'm creating a UI using Swing and I want to display an image in a JLabel. The code I use is the following:
JLabel label = new JLabel(new ImageIcon(ImageIO.read(new File("img.jpg"))));
This works fine if I use png images but when it comes to jpg (only some of them), I get a redish image (a different one than the one I see in Paint.NET).
The image I used is this one: img.jpg
So I tried (as an alternative):
Toolkit.getDefaultToolkit().createImage(new File("img.jpg").getAbsolutePath());
Does anyone have an idea of why this happening? Is it a special JPEG format which is not supported?
I've read on this forum that most people recommend to use ImageIO (here for example). Why?
Thanks a lot
As discussed here, your JPEG image may contain spurious transparency information. One simple expedient is to render the image in a buffer having a compatible color model, as shown here.
It looks like you have found a bug in ImageIO.read... (I can reproduce the red tint, and it is definitely not how it should look like).
You can try to
save the JPEG files with other settings
open/re-save the file with other programs (hoping to get a more common JPEG-encoding)
or use the Toolkit method (if you don't control the images).
The only problem with the Toolkit method is that the getImage() method returns immediately after it is invoked and the loading is happening on a background thread, so you cannot start working with the Image object immediately.

How do I make java's ImageBuffer to read a PNG file correctly?

For some reason, opening up some PNG files using ImageBuffer and ImageIO does not work. Here's some code I am using that works fine for resizing/cropping JPGs:
BufferedImage image = ImageIO.read(new File(location));
BufferedImage croppedImage = image.getSubimage(
cropInfo.getX(), cropInfo.getY(), cropInfo.getW(), cropInfo.getH());
BufferedImage resizedImage = new BufferedImage(
TARGET_WIDTH, TARGET_HEIGHT, croppedImage.getType());
Graphics2D g = resizedImage.createGraphics();
g.drawImage(croppedImage, 0, 0, TARGET_WIDTH, TARGET_HEIGHT, null);
g.dispose();
this.changeContentType("image/png", ".png"); // not really relevant. just a property
ImageIO.write(resizedImage, "png", new File(location));
return resizedImage;
The goal of this function is to take whatever type is given, resize and crop the image, and then save it to PNG with the same filename.
It works on Windows, but if I crop/resize on Linux (lenny), it crashes altogether and complains about the type of the file (it says the type is 0).
java.lang.IllegalArgumentException: Unknown image type 0
java.awt.image.BufferedImage.<init>(BufferedImage.java:490)
trainingdividend.domain.file.ServerImage.resizeImage(ServerImage.java:68)
trainingdividend.domain.file.ServerImage.cropAndResize(ServerImage.java:80)
trainingdividend.service.user.UserAccountManagerImpl.cropAvatar(UserAccountManagerImpl.java:155)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
Solutions?
Is there another library I can use altogether?
When running my function on Windows, croppedImaged.getType() returns the value 5. So, the simple "hack" is to store the type, check to see if it's 0... and if it is, set the value to 5 manually.
int imageType = croppedImage.getType();
if(imageType == 0) imageType = 5;
We then pass in imageType instead and it should work on Linux.
I am sure this has the drawback that if the value is 0 in other cases, it will set it to 5 and that will be wrong. However, this seems to work for common image types on Linux and it hasn't caused any problems.
It's pretty clear that the Windows version of Java 1.6 is perfectly fine, but the Linux version has a bug in it.
egervari, you can use a library like imgscalr (Apache 2) to do all the resizing for you "properly" that fixes issues like these with a very simple API -- it won't help with the cropping, but the resizing is what it does best (different speeds, qualities, even anti-aliasing if you want).
I would point out that the code you are using now (forcing a CUSTOM type into a 3BYTE_BGR type) should also account for inbound images with an alpha channel.
Also if you want to keep using your custom code, RGB and ARGB are two of the best supported image types in Java2D -- if you use a poorly supported image type, when Java2D goes to perform the image op, it falls back to its software rendering pipeline and doesn't use the specialized hardware accelerated ones. This doesn't just effect performance as you'll see the result actually look worse (e.g. in GIF You see this a lot).
Again, imgscalr takes care of all this for you automatically if you wanted to give it a try, but if not, I figured I'd just give a heads up incase you were running into some of these headaches.
java image processing is... temperamental :)
A workaround solution would be to convert the file to jpeg first, and then process it. The type 0 bug seems to mostly affect PNG images.

Resize Image files

I have a lot of images that taken by my Digital camera with very high resolution 3000 * 4000 and it takes a lot of Hard disk space, I used Photoshop to open each Image and re-size it o be with small resolution, but it needs a lot of time and effort
I think that I can write simple program that open the folder of images and read each file and get it's width and height and if it's very high change it and overwrite the image with the small one.
Here some code I use in a Java-EE project (should work in normal application to:
int rw = the width I needed;
BufferedImage image = ImageIO.read(new File(filename));
ResampleOp resampleOp = new ResampleOp(rw,(rw * image.getHeight()) / image.getWidth() );
resampleOp.setFilter(ResampleFilters.getLanczos3Filter());
image = resampleOp.filter(image, null);
File tmpFile = new File(tmpName);
ImageIO.write(image, "jpg", tmpFile);
The resample filter comes from java-image-scaling library. It also contains BSpline and Bicubic filters among others if you don't like the Lanczos3. If the images are not in sRGB color space Java silently converts the color space to sRGB (which accidentally was what I needed).
Also Java loses all EXIF data, thought it does provide some (very hard to use) methods to retrieve it. For color correct rendering you may wish to at least add a sRGB flag to the file. For that see here.
+1 to what some of the other folks said about not specifically needing Java for this, but I imagine you must have known this and were maybe asking because you either wanted to write such a utility or thought it would be fun?
Either way, getting the image file listing from a dir is straight forward, resizing them correctly can take a bit more leg work as you'll notice from Googling for best-practices and seeing about 9 different ways to actually resize the files.
I wrote imgscalr to address this exact issue; it's a dead-simple API (single class, bunch of static methods) and has some good adoption in webapps and other tools utilizing it.
Steps to resize would look like this (roughly):
Get file list
BufferedImage image = ImageIO.read(files[i]);
image = Scalr.resize(image, width);
ImageIO.write(image);
There are a multitude of "resize" methods to call on the Scalr class, and all of them honor the image's original proportions. So if you scale only using a targetWidth (say 1024 pixels) the height will be calculated for you to make sure the image still looks exactly right.
If you scale with width and height, but they would violate the proportions of the image and make it look "Stretched", then based on the orientation of the image (portrait or landscape) one dimension will be used as the anchor and the other incorrect dimension will be recalculated for you transparently.
There are also a multitude of different Quality settings and FIT-TO scaling modes you can use, but the library was designed to "do the right thing" always, so using it is very easy.
You can dig through the source, it is all Apache 2 licensed. You can see that it implements the Java2D team's best-practices for scaling images in Java and pedantically cleans up after itself so no memory gets leaked.
Hope that helps.
You do not need Java to do this. It's a waste of time and resources. If you have photoshop you can do it with recording actions: batch resize using actions
AffineTransformOp offers the additional flexibility of choosing the interpolation type, as shown here.
You can individually or batch resize with our desktop image resizing application called Sizester. There's a full functioning 15-day free trial on our site (www.sizester.com).

Java2d thumbnails. Can I get thumbnail from OS

I develop an application in which I want to display a grid with a list of images. For each image I create an instance of a class myImage. MyImage class, extends JCompoment and create an thumbnail and after draw it with overide thepaintCompoment(Graphics g).
All is ok, but in big size images I have a lot of delay to create the thumbnail.
Now I think to when I scan the folders for image(to create the list I said above to create an thumbanail of each image and save it to disc. For each image I will have a database record to save the image path and thumbnail path.So is this a good solution of the problem?
Is there a way to get the thumbnails of the system creates for each image, in file manager. Or a more effective solution than I try.
Thank you!!
Your best bet is to use something like imagemagick to convert the image and create the thumbnail. There's a project called JMagick which provides JNI hooks into Imagemagick, but running a process work too.
Imagemagick is heavily optimized C code for manipulating images. It will also be able to handle images that Java won't and with much less memory usage.
I work for a website where we let users upload art and create thumbnails on the fly, and it absolutely needs to be fast, so that's what we use.
The following is Groovy code, but it can modified to Java code pretty easily:
public boolean createThumbnail(InputStream input, OutputStream output){
def cmd = "convert -colorspace RGB -auto-orient -thumbnail 125x125 -[0] jpg:-"
Process p = cmd.execute()
p.consumeProcessErrorStream(System.out)
p.consumeProcessOutputStream(output)
p.out << input
p.out.close()
p.waitForOrKill(8000)
return p.exitValue()==0
}
This creates a thumbnail using pipes without actually writing any data to disk. The outputStream can be to a file if you wanted to immediately write it as well.
One way to avoid OS dependance is to use getScaledInstance(), as shown in this example. See the cited articles for certain limitations. If it's taking too long, use a SwingWorker to do the load and scale in the background.
I haven't used it for the creation of thumbnails, but you may also want to take a look at the ImageIO API.
ImageIO

Categories