Einführung in die Java Advanced Imaging Klassen

Werbung
Einführung in die
Java Advanced Imaging
Klassen
verfasst
von
Beat Trachsler
EMPA St. Gallen
Gruppe Medientechnik
6. April 2002
1
Einleitung
Dieser Text gibt eine kurze Einführung in die Java Advanced Imaging Klassen (JAI).
Diese Klassen bieten Methoden zur Bildverarbeitung an, so z.B. geometrische Transformationen (Skalierung, Drehung u.ä.) und Filter (Weichzeichner, Scharfzeichner, etc.).
Daneben werden auch komplexere Bildtransformationen wie die Diskrete Fourier Transformation (DFT) und die Diskrete Cosinus Transformation (DCT) zur Verfügung gestellt.
Zudem bietet JAI Methoden zur Bildanalyse, wie z.B. den Mittelwert, die Extremalwerte
und das Histogramm.
Viele der oben erwähnten Transformationen sind in den Java 2D Klassen bereits
enthalten. Der grosse Vorteil von JAI ist der einfache Umgang mit den gängigen Dateiformaten für Bilder. Java 2 unterstützt in diesem Bereich nämlich nach wie vor nur
das JPEG Format. Dieses genügt jedoch nicht den Anforderungen der professionellen
Bildverarbeitung, was die Farbechtheit betrifft. Mit JAI hingegen können ohne grossen
Aufwand auch TIFF Dateien gelesen und geschrieben werden.
Im Abschnitt 2 werden einige Datenstrukturen, welche man zur Darstellung von Bildern mit Java und JAI benötigt, kurz vorgestellt. Wir konzentrieren uns dabei auf das
Sample Modell, welches für den Zugriff auf die Pixels entscheidend ist. Im Abschnitt 3
wird das Operatormodell von JAI vorgestellt. Wir gehen dort auf die create-Methoden
ein, welche als Interface für sämtliche JAI-Operatoren dienen. Zudem geben wir drei
Beispiele für die Anwendung von JAI-Operatoren. Im Abschnitt 4 schliesslich wird ein
kurzes Beispielprogramm vorgestellt.
2
2.1
Datenstrukturen
Samples und Pixels
Das Graphik Modell von Java ist in den AWT Klassen definiert. Wir halten uns im
folgenden an die Methoden und Objekte der Java 2D Klassen, welche seit Java 2 zum
Kern von Java gehören. Die kleinste Einheit im Graphik Modell von Java ist das Sample.
Jedes Pixel eines Bildes wird durch eine bestimmte Anzahl Samples beschrieben. Bei
einem RGB-Bild braucht es z.B. drei Samples pro Pixel, ein Rot-, ein Grün und ein
Blau-Sample. Alle Samples der gleichen Art bilden zusammen einen Farbkanal. Demnach
braucht man für ein Graustufenbild nur ein Sample pro Pixel und für ein CMYK-Bild
vier Samples pro Pixel.
Die Samples werden in Java in einem Objekt namens Raster verwaltet. Dieses Objekt
bietet Methoden an, mit denen man die Samples als eindimensionales Array exportieren
kann, z.B.
samplemap = pixelraster.getPixels(0,0,width,height,samplemap);
Dabei ist pixelraster eine Instanz des Objekts Raster. Es enthält die Samples eines
Bildes gemäss der jeweiligen Anzahl Farbkanäle und gemäss den Dimensionen width und
height des Bildes. samplemap ist ein eindimensionales Array der Länge width · height ·
1
numbands, wobei numbands für die Anzahl der Farbkanäle des Bildes steht. Der Datentyp
des Arrays ist entweder int, float oder double.
Die abgeleitete Klasse WritableRaster erlaubt es zudem, die Samples wieder ins
Raster zurück zu schreiben. Das geschieht mit der Methode setPixels:
pixelraster.setPixels(0,0,width,height,samplemap);
Die Methode setPixels funktioniert aber erst, nachdem das Raster initialisiert wurde.
Dies geschieht am einfachsten über ein Objekt namens ColorModel. Dieses Objekt enthält
Daten über den Farbraum des Bildes. Zudem wird der Datentyp des Rasters verwaltet
(in der Regel byte). Mit der folgenden Methode kann ein zum ColorModel kompatibles
Raster erzeugt werden:
WritableRaster pixelraster =
colormodel.createCompatibleWritableRaster(width,height);
Dabei ist colormodel eine Instanz des Objekts ColorModel. Wenn Sie ausgehend von
einem Raster ein neues Bild definieren wollen, müssen Sie also zunächst ein ColorModel
definieren. Für den vordefinierten sRGB-Farbraum geschieht das wie folgt:
// Definition des ColorModel (sRGB)
int[] bits = {8, 8, 8}; // Farbtiefe 8 bit pro Farbkanal
ColorModel colormodel = new ComponentColorModel(
ColorSpace.getInstance(ColorSpace.CS_sRGB),
bits, false, false,
Transparency.OPAQUE, // keine Transparenz
DataBuffer.TYPE_BYTE); // Datentyp byte (default)
Dabei enthält das Array bits die Farbtiefe für jeden der drei Farbkanäle.
2.2
Das Planar Image
Die JAI Klassen bieten eine ganze Reihe von Objekten an, mit denen ein zweidimensionales Bild repräsentiert werden kann. Die Klasse, welche diesen Varianten zugrunde
liegt, heisst PlanarImage. Es handelt sich dabei um die JAI Implementation des Interface
RenderedImage von Java 2D. Ein PlanarImage enthält neben dem Raster des Bildes
auch dessen ColorModel. Für den Zugriff auf das Raster bietet die PlanarImage-Klasse
die Methode copyData an:
WritableRaster pixelraster = image.copyData();
Dabei ist image eine Instanz des Objekts PlanarImage.
Will man umgekehrt ein PlanarImage aus einem Raster konstruieren, so beginnt die
Konstruktion wie im Abschnitt 2.1 beschrieben mit dem ColorModel. Damit lässt sich
ein SampleModel definieren, das mit dem ColorModel kompatibel ist:
SampleModel samplemodel =
colormodel.createCompatibleSampleModel(width,height);
2
Das SampleModel ist diejenige Komponente des Raster-Objekts, welche beschreibt, wie
die Samples im Raster verwaltet werden. Mit dem ColorModel und dem SampleModel
kann jetzt ein JAI Bild definiert werden. Da die Klasse PlanarImage eine abstrakte
Klasse ist, müssen wir dazu auf eine Subklasse namens TiledImage zurückgreifen:
image =
new TiledImage(0,0,width,height,0,0,samplemodel,colormodel);
Nun kann man mit der Methode setData ein Raster in das PlanarImage einfügen:
image.setData(pixelraster);
Dabei ist zu beachten, dass das Raster zum ColorModel kompatibel sein muss.
3
3.1
Das Operatormodell
Die create-Methoden
Die Operatoren werden in JAI nicht direkt auf das Bild angewendet. Ein Operator wird
indirekt mit Hilfe der create-Methode aufgerufen. Dabei gibt es eine ganze Menge von
syntaktischen Varianten für die create-Methode, die den Umgang mit dem Konstrukt
in der Praxis erleichtern sollen. Im folgenden zunächst diejenige Version, welche den
anderen, abgeleiteten Varianten zugrunde liegt:
JAI.create(opname, parameterblock, hints);
Dabei bezeichnet opname einen String, welcher den Namen des Operators enthält, also
z.B. ’scale’ für den Skalierungsoperator. Die Variable parameterblock ist eine Instanz
der AWT Klasse ParameterBlock. Dieses Objekt enthält Parameterwerte für den JAI
Operator und die Datenquelle(n), auf die der Operator angewendet werden soll, also in
der Regel das Ausgangsbild. Die Parameterwerte werden dabei sequentiell mit der Methode add eingelesen. Die Datenquelle wird mit der Methode addSource spezifiziert. Die
folgenden Abschnitte enthalten Beispiele für die Anwendung dieser Klasse. Die hints
schliesslich sind vom Typ RenderingHints. Sie dienen zur Einstellung spezieller Rendering Optionen, wie z.B. Antialiasing, etc. In diesem Text werden wir nicht näher auf diese
Optionen eingehen. Wir verweisen dazu auf den JAI Guide von Sun.
Neben der oben beschriebenen create-Methode existieren eine Menge von Abkürzungen. So ist es z.B. möglich, die hints wegzulassen. Zudem kann der parameterblock
ersetzt werden durch die direkte Angabe der Datenquelle gefolgt von den Parameterwerten:
JAI.create(opname, image, param1, param2, ...);
Für eine vollständige Beschreibung aller Varianten verweisen wir auf den JAI Guide von
Sun.
3
3.2
Der fileload- und der filestore-Operator
Eine sehr einfache und grundlegende Anwendung von JAI Operatoren ergibt sich beim
Lesen und Schreiben von Bilddateien. Die folgende Methode liest ein Bild mit bekanntem
Format1 ein. Beachten Sie, dass das Format des Bildes nicht explizit angegeben werden
muss.
PlanarImage image = JAI.create("fileload", path);
Dabei bezeichnet die String-Variable path den Pfad des entsprechenden Files.
Will man umgekehrt ein JAI Bild in ein File hinaus schreiben, so bietet sich der
Operator filestore an. Hier muss natürlich das gewünschte Ausgabeformat als String
spezifiziert werden. Für die Ausgabe eines Bildes in ein TIFF File ergibt sich damit:
JAI.create("filestore", image, path, "TIFF");
3.3
Der addconst-Operator
Der Operator addconst ist ein sogenannter Punktoperator. Dabei wird pixelweise zu
jedem Samplewert eine bestimmte Konstante addiert. Wird dabei der Wertebereich des
Samples verlassen, setzt JAI einfach die obere oder untere Schranke des Wertebereichs,
also z.B. 0 oder 255. Die nachfolgende Codesequenz zeigt, wie man bei einem Bild mit
drei Farbkanälen, pixelweise zu jedem Sample den Wert 10 addieren kann. Beachten Sie
dabei, dass die Konstante für jeden Farbkanal einzeln angegeben werden muss und dass
das entsprechende Parameterarray vom Datentyp double sein muss.
double[] constants = {10., 10., 10.};
ParameterBlock paramblock = new ParameterBlock();
paramblock.addSource(image);
paramblock.add(constants);
image = JAI.create("addconst", paramblock);
3.4
Der mean-Operator
Der mean-Operator ist ein sogenannter statistischer Operator. Er dient zur Berechnung
des arithmetischen Mittelwerts für jeden Farbkanal. Das spezielle an diesem Operator
ist, dass der Ausgabewert eigentlich kein Bild sondern ein Zahlenarray sein müsste, das
für jeden Farbkanal den mittleren Farbwert angibt. JAI-Operatoren liefern aber immer
ein Bild als Resultat. Die berechneten Mittelwerte werden als Property im PlanarImage
gespeichert und müssen hinterher mit der Methode getProperty exportiert werden.
// compute the channelwise mean of the image
ParameterBlock pb = new ParameterBlock();
pb.addSource(image);
// The source image
1
Zum Zeitpunkt, als dieser Text verfasst wurde, wurden die folgenden Dateiformate von JAI unterstützt: TIFF, JPEG, GIF, BMP, FPX, PNG und PNM.
4
pb.add(null);
// The region to scan, ’null’ = whole image
pb.add(1);
// The horizontal sampling rate
pb.add(1);
// The vertical sampling rate
image = JAI.create("mean", pb);
double[] mean = (double[])image.getProperty("mean");
4
Beispielprogramm
In diesem Abschnitt wird das Beispielprogramm ’TestGraphics’ vorgestellt. Es zeigt, wie
man mit JAI auf sehr einfache Art und Weise eine Java 2D Graphik in einem TIFF
Dokument abspeichern kann.
/*
* @(#)TestGraphics.java 1.0 01/11/20
*/
import
import
import
import
java.awt.*;
java.awt.color.*;
java.awt.image.*;
javax.media.jai.*;
class TestGraphics {
// Dimensionen des Bildes:
private static final int WIDTH = 500;
private static final int HEIGHT = 350;
public static void main(String args[]) {
System.out.println("Starting TestGraphics...");
// Definition des ColorModel (sRGB)
int[] bits = {8, 8, 8}; // Farbtiefe 8 bit
ColorModel colormodel = new ComponentColorModel(
ColorSpace.getInstance(ColorSpace.CS_sRGB), // Farbraum sRGB
bits, false, false,
Transparency.OPAQUE, // keine Transparenz
DataBuffer.TYPE_BYTE); // Datentyp Byte (default)
// Erzeugen des SampleModel
SampleModel samplemodel =
colormodel.createCompatibleSampleModel(WIDTH,HEIGHT);
// Erzeugen des Bildes
5
TiledImage image =
new TiledImage(0,0,WIDTH,HEIGHT,0,0,samplemodel,colormodel);
// Initialisierung von 2D Graphics
Graphics2D graphics = image.createGraphics();
graphics.setBackground(Color.white);
graphics.clearRect(0,0,WIDTH,HEIGHT);
// Erzeugen der Graphik
graphics.setColor(Color.red);
graphics.fillRect(50,50,100,100);
graphics.setColor(Color.green);
graphics.fillRect(200,50,100,100);
graphics.setColor(Color.blue);
graphics.fillRect(350,50,100,100);
graphics.setColor(Color.cyan);
graphics.fillRect(50,200,100,100);
graphics.setColor(Color.magenta);
graphics.fillRect(200,200,100,100);
graphics.setColor(Color.yellow);
graphics.fillRect(350,200,100,100);
// Bild in TIFF-File schreiben
JAI.create("filestore",image,"./graphik.tif","TIFF");
}
}
6
Herunterladen