- Main page
- Features
- Introductory tour
- Documentation
- License
- Download
- Status
- Users
- Screenshots
- Help wanted
- Links
- Credits
- Feedback
Given that the JIU manual as part of the documentation isn't quite up to speed at the moment (and won't be for the foreseeable future), this little tour of JIU gives you a first impression of how to use the library.
Make sure you're using JIU version 0.14.2 or higher, otherwise the examples won't work. If necessary, get the latest version from the download page first.
Table of contents
The classical introduction to a new programming language includes a small sample program that prints “Hello World!” somewhere on the screen. Given that JIU is not a language but an imaging library, it will print “Hello World!” into an image and write that to a PNG image file, which you can then load into your favourite image viewer.
Drawing text onto images is something JIU can't do and doesn't want to do, but the Java runtime library's AWT (Abstract Windowing Toolkit) is perfectly fine for that task. The JIU part comes when the AWT image of type BufferedImage is converted to a JIU image object, which is then written to a PNG image file using a JIU codec. The result looks like that:
Here's the code, which you can also find in the JIU distribution file in the subdirectory net/sourceforge/jiu/apps as JiuHelloWorld.java. The interesting part with regard to JIU is that
Note that the package declarations are missing from the code examples on this page.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import net.sourceforge.jiu.codecs.CodecMode;
import net.sourceforge.jiu.codecs.PNGCodec;
import net.sourceforge.jiu.data.RGB24Image;
import net.sourceforge.jiu.gui.awt.ImageCreator;
public class JiuHelloWorld
{
public static void main(String[] args) throws Exception
{
// AWT image creation
BufferedImage awtImage = new BufferedImage(100, 30, BufferedImage.TYPE_INT_RGB);
Graphics graphics = awtImage.getGraphics();
// fill image with red
graphics.setColor(Color.RED);
graphics.fillRect(0, 0, awtImage.getWidth(), awtImage.getHeight());
// draw on it in white
graphics.setColor(Color.WHITE);
graphics.drawString("Hello World!", 5, 15);
// conversion AWT image to JIU image
RGB24Image jiuImage = ImageCreator.convertImageToRGB24Image(awtImage);
// saving JIU image to file
PNGCodec codec = new PNGCodec();
codec.setImage(jiuImage);
codec.appendComment("Hello World! as a text comment; " +
"see http://schmidt.devlib.org/jiu/introduction.html");
codec.setFile("jiu-hello-world.png", CodecMode.SAVE);
codec.process();
}
}
The Hello World! example was a bit unusual in the sense that it created an image from scratch. A typical processing operation involves loading an image first from an existing file, modifying it and then saving it back to a file.
In this second example, we'll do just that. Load a color image from a JPEG file, convert it to grayscale, apply a blur filter to it and then save it back to a file.
A minor inconvenience with JIU is its lack of direct support for the popular JPEG file format. That's why the Java runtime's JPEG loading and saving capabilities will be used for those parts. Obviously, other formats could be used. However, JPEG is very popular because it can reduce photo-realistic (also known as continuous tone) images very well. It does that by throwing away some information, though, so be careful not to do too many edit - save as JPEG editing cycles with an image. Instead, save as something else (like PNG or BMP), and use JPEG saving only as the very last step in an edit cycle.
The two operations actually modifying the loaded image will be very simple to use. The image's contrast is first increased by 20 percent, then a blur filter is applied to it. Finally, we convert the resulting image to a format that the runtime library understands (BufferedImage) and save it to a JPEG file with ImageIO.
public class JiuBlur
{
public static void main(String[] args) throws Exception
{
PixelImage image = ToolkitLoader.loadAsRgb24Image("resources/images/image1.jpg");
image = Contrast.adjust(image, 20);
image = ConvolutionKernelFilter.filter(image, ConvolutionKernelFilter.TYPE_BLUR);
BufferedImage awtImage = ImageCreator.convertToAwtBufferedImage(image);
ImageIO.write(awtImage, "jpg", new File("out-image1.jpg"));
}
}
First the input image:
Then the output image, which has higher contrast and less sharpness and detail:
Another area of imaging is image analysis, which doesn't change the image; instead, information is extracted from it. As a very simple example for that group of operations, let's count the colors of the three images we dealt with so far, the white-on-red Hello World, and the original and blurred version of the freeway photo. JIU can create histograms, data structures which store for each color how often it occurs in a particular image. Histogram3DCreator does histogram creation for images with three channels. It also has a convenience method which creates a histogram, returns its number of entries (the colors) and throws away the histogram. We just call that method:
public class JiuCountColors
{
public static void main(String[] args) throws Exception
{
String[] FILE_NAMES = {"jiu-hello-world.png", "resources/images/image1.jpg", "out-image1.jpg"};
for (int i = 0; i < FILE_NAMES.length; i++)
{
RGB24Image image = ToolkitLoader.loadAsRgb24Image(FILE_NAMES[i]);
System.out.println(FILE_NAMES[i] + "\t" + Histogram3DCreator.count(image));
}
}
}
The program prints the following lines to standard output:
jiu-hello-world.png 2 resources/images/image1.jpg 85066 out-image1.jpg 86928
As expected, the white-on-red image we generated ourselves only uses two colors. If some fancy anti-aliasing took place when drawing text, that number would probably go up. The original freeway image uses 85,066 different colors and its blurred, higher contrast counterpart about two thousand more. My explanation for the increase: the contrast operation spreads the colors over a larger interval, leading to more unique individual colors.
JIU has a lot to offer when it comes to color reduction algorithms. This small program takes the freeway photo from above and applies several reduction methods to it, saving each reduced image to a different file.
public class JiuColorReduction
{
public static void main(String[] args) throws Exception
{
// load input image
RGB24Image rgb24Image = ToolkitLoader.loadAsRgb24Image("resources/images/image1.jpg");
// create a 256 color paletted image using ordered dithering
OrderedDither od = new OrderedDither();
od.setInputImage(rgb24Image);
od.setRgbBits(3, 3, 2);
od.process();
// save paletted image
PNGCodec codec = new PNGCodec();
codec.setImage(od.getOutputImage());
codec.setFile("image1-pal256-ord.png", CodecMode.SAVE);
codec.process();
// create an image with 256 shades of gray
PixelImage gray8Image = RGBToGrayConversion.convert(rgb24Image);
codec.setImage(gray8Image);
codec.setFile("image1-gray256.png", CodecMode.SAVE);
codec.process();
// create a bilevel image using Floyd-Steinberg dithering
ErrorDiffusionDithering ed = new ErrorDiffusionDithering();
ed.setTemplateType(ErrorDiffusionDithering.TYPE_FLOYD_STEINBERG);
ed.setGrayscaleOutputBits(1);
ed.setInputImage(gray8Image);
ed.process();
codec.setImage(ed.getOutputImage());
codec.setFile("image1-floyd2.png", CodecMode.SAVE);
codec.process();
}
}
After the image has been loaded, ordered dithering with a fixed-sized palette of 256 entries is applied to it. The palette contains entries uniformly distributed over the RGB color space. A better result could be produced with Median Cut color quantization, which comes up with a palette especially suitable for each image it reduces. However, that takes more computing time. Median Cut quantization is also supported by JIU, but not shown in this small example program. Ordered dithering is much faster and looks nice from a distance:
However, zooming into the image reveals an ugly dithering pattern:
Next, a grayscale version of the original freeway image is created with the RGBToGrayConversion class.
Finally, that grayscale image is reduced to a bilevel (two color) black and white image using a more sophisticated dithering algorithm. It is called error diffusion dithering and can be used with different templates for distributing the error of color reduction to neighboring pixels. The template used in the example was created by Floyd and Steinberg. The resulting image is quite a good representation of the original given that it only uses two colors: