ImageIO: Just another example of better living by doing it yourself(收藏)

来源:互联网 发布:淘宝双11多少钱 编辑:程序博客网 时间:2024/06/08 03:24

ImageIO: Just another example of better living by doing it yourself

Posted by chet on July 19, 2004 at 03:17 AM | Comments (12)

I've always known that ImageIO was a good thing to use since itsinception. It reads and writes more formats than the original Imageloading APIs, it has a pluggable interface for new image formats, it'sthe way of the future, it's more robust than the previous APIs, it'ssynchronous without the need for MediaTracker or that hacky ImageIconworkaround, blah, blah, blah....

I knew all that without actually working with it because it was apparent from the API. It is the way of the future for image reading and writing, and it ismore robust and all the rest of that stuff. It's just a greatfull-featured package that's written from the ground up to provide whatdevelopers have been asking us to provide after using the old ImageAPIs for years.

But I'd never actually used the darned package...

(Petty justification: The folks working on the librariesfor Java don't usually get enough time to play around with using theAPIs; we're too busy making the stuff work. That's not to say that thepeople who wrote ImageIO didn't use it substantially in implementingit; of course they did. But I wasn't involved in that part of the API,and I'm busy enough in my little rendering and performance closet thatI didn't take the opportunity to play around with ImageIO since itfirst came out.)

This all changed in the last month.

See, JavaOne was coming, which meant that I had a few demos to write.And it turns out I needed a couple of image utilities, and I neededthem quickly. For both utilities, I began thinking "Now where could Ifind some simple program to do this ..." and then I realized how easyit would be to accomplish it from scratch in ImageIO. A few lines ofcode later and, presto!, I had my applications.

Not only did I get exactly the functionality that I waslooking for, but I got the satisfaction of actually having written thecode myself, which is so darned gratifying to us geeks. As they say,"Just another example of better living by doing it yourself." Or atleast that's what I say.

Anyway, on with the code.

ImageScaler

The first utility came from a need to scale some images. We hada demo that was going to show images in a particular size all the time.Rather than scaling them on the fly or caching the scaled versions inthe application, we figured it would be better to simply load imagesthat were already of the size needed.

Knowing how these projects tend to go, what I really wanted was the ability to scale our full-size images (alwayskeep the original full-size versions of media around...) down to somearbitrary size. And then, when the designers changed their minds aboutthe size at the last minute, be able to quickly re-scale the images tosome other arbitrary size.

We could do this through some image manipulation program, butit would be a major hassle to keep doing it (including having to gothrough the person that knew the program that ran the macro that ...you get the picture). So what I wanted was a simple standaloneapplication to scale the images.

What I wrote was ImageScaler. This applicationtakes the pathname to a directory (or uses "." as the default) andwidth/height values. It reads in all image files from that directory,creates a subdirectory ("scaled/"), and writes out scaled versions ofthose images in the specified width and height.

Here's the code:

package jcg.lhdemo;

import java.awt.*;
import java.awt.image.*;
import javax.imageio.ImageIO;
import java.io.File;

/**
* ImageScaler
*
* This class loads all images in a given directory and scales them to the
* given sizes, saving the results as JPEG files in a new "scaled/" subdirectory
* of the original directory.
*/
public class ImageScaler {

// Default w/h values; overriden by command-line -width/-height parameters
static int IMAGE_W = 150;
static int IMAGE_H = 250;

public static void main(String args[]) {
// Default directory is current directory, overridden by -dir parameter
String imagesDir = ".";
for (int i = 0; i < args.length; ++i) {
if (args[i].equals("-dir") && ((i + 1) < args.length)) {
imagesDir = args[++i];
} else if (args[i].equals("-width") && ((i + 1) < args.length)) {
IMAGE_W = Integer.parseInt(args[++i]);
} else if (args[i].equals("-height") && ((i + 1) < args.length)) {
IMAGE_H = Integer.parseInt(args[++i]);
}
}
// new subdirectory for scaled images
String scaledImagesDir = imagesDir + File.separator + "scaled";
// directory that holds original images
File cwd = new File(imagesDir);
// directory for scaled images
File subdir = new File(scaledImagesDir);
subdir.mkdir();
File files[] = cwd.listFiles();
// temporary image for every scaled instance
BufferedImage scaledImg = new BufferedImage(IMAGE_W, IMAGE_H,
BufferedImage.TYPE_INT_RGB);
Graphics2D gScaledImg = scaledImg.createGraphics();
// Note the use of BILNEAR filtering to enable smooth scaling
gScaledImg.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
for (int i = 0; i < files.length; ++i) {
try {
// For every file in the directory, assume it's an image and
// load it
BufferedImage img = ImageIO.read(files[i]);
// If we get here, we must have read the image file successfully.
// Create a new File in the scaled subdirectory
File scaledImgFile = new File(scaledImagesDir + File.separator +
files[i].getName());
// Scale the original image into the temporary image
gScaledImg.drawImage(img, 0, 0, IMAGE_W, IMAGE_H, null);
// Save the scaled version out to the file
ImageIO.write(scaledImg, "jpeg", scaledImgFile);
} catch (Exception e) {
System.out.println("Problem with " + files[i]);
}
}
}
}

Things to note about ImageScaler:
  • It assumes that the images are in JPEG format. Actually, it doesread in the files in any format, but it writes them out in JPEG formatwith the original filenames, so if the images weren't JPEG to beginwith, things will get a bit confusing. We could, of course, use anyformat that ImageIO supports (or any for which we have plugins); inthis case I knew that we were dealing with JPEG images so I made thatsimplifying assumption in the code. The code could be easily extendedto handle other or multiple formats instead.
  • Note the use of the BILINEAR rendering hint to the Graphics2Dobject; this tells Java 2D to do some smooth filtering on the image tomake the scaled version look better than it would by using the defaultNEAREST_NEIGHBOR method. NEAREST_NEIGHBOR works great for many purposes(and is faster in general), but if you're trying to get higher qualityand don't mind waiting just a tad longer for the result, use BILINEAR.And for those developers using jdk 5.0, note that the BICUBIC hint nowworks; you may get even better results with that value.
  • Aspect ratio: This code assumes that the original images are in thesame aspect ratio as that determined by the width/height that we arescaling to. This may not be the case in any given situation, and mightcause unattractive artifacts with widely differing aspect ratios. Amore robust application could provide the ability to preserve theaspect ratio of the original instead of using the width/heightparameters blindly.
  • A batch processing application like ImageScalerwhich acts on several files in the same format in a loop might be moreperformant by locating the appropriate reader and writer once, outsidethe for() loop, and then just calling the appropriate read() or write() functions inside the loop. I'll leave this as an exercise for the reader....

JpegConverter

A couple of weeks after I wrote and used ImageScaler,I ran into another problem where I needed a quick conversion utility. Ihad a series of BMP images that I wanted to convert to JPEG (in thiscase, because I needed a compressed image format). Once again I foundmyself writing a very quick program using ImageIO that did the job. Infact, I probably wrote and ran the program in less time than it wouldhave taken me to find an appropriate utility out on the net, installit, and run it. And, once again, "it's another example of better livingby doing it yourself".

This program takes a pointer to a directory (or uses "." bydefault), reads in all BMP image files in that directory (I constrainedJpegConverter to convert only BMP files, but, like ImageScaler, this application could be easily extended to handle multiple formats) and then saves out a JPEG version of each file.

Here's the code:

import java.awt.*;
import java.awt.image.*;
import javax.imageio.ImageIO;
import java.io.File;

/**
* JpegConverter
*
* This class loads all BMP images in a given directory and saves each as a JPEG
* file in the same directory. This code is specific to BMP, but it could be
* easily extended to read images of any type that ImageIO handles.
*/
public class JpegConverter {

public static void main(String args[]) {
// Default directory is current directory, overridden by -dir parameter
String imagesDir = ".";
for (int i = 0; i < args.length; ++i) {
if (args[i].equals("-dir") && ((i + 1) < args.length)) {
imagesDir = args[++i];
}
}
// directory that holds original images
File cwd = new File(imagesDir);
File files[] = cwd.listFiles();
for (int i = 0; i < files.length; ++i) {
String fileName = files[i].getName();
// converstion to lower case just for ease of replacing the
// filename extension
String fileNameLC = fileName.toLowerCase();
if (fileName.endsWith("bmp")) {
try {
// Replace original "bmp" filename extension with "jpg"
int extensionIndex = fileNameLC.lastIndexOf("bmp");
String fileNameBase = fileName.substring(0, extensionIndex);
BufferedImage img = ImageIO.read(files[i]);
// create new JPEG file
File convertedImgFile =
new File(imagesDir + File.separator +
fileNameBase + "jpg");
// store original file out in JPEG format
ImageIO.write(img, "jpeg", convertedImgFile);
} catch (Exception e) {
System.out.println("Problem with " + files[i]);
}
}
}
}
}

Notes:

  • Some of the notes for ImageScaler above apply to JpegConverter as well, so consider this note as a dereference to the notes above...

And so, in conclusion

Some readers might wonder: "What's the big deal?" After all, these applications are pretty simple, so why all the fuss?

Exactly my point; thanks for clarifying it. It iseasy. So easy that I would encourage everyone to think about usingImageIO next time you have a simple (or hard) image input/outputproblem come up; see if the ImageIO API doesn't make solving thatproblem just a tad easier.

原创粉丝点击