Programming in Java Advanced Imaging
C H A P T E R 6 |
|
Image Manipulation |
THIS chapter describes the basics of manipulating images to prepare them for further processing.
6.1
The JAI image manipulation objects and methods are used to enhance and geometrically modify images and to extract information from images. Image manipulation includes:Introduction
- Region of interest (ROI) control
- Relational operators
- Logical operators
- Arithmetic operators
- Dithering
- Clamping pixel values
- Band copy
6.2
Typically, any image enhancement operation takes place over the entire image. While the image enhancement operation may improve portions of an image, other portions of the image may lose detail. You usually want some way of limiting the enhancement operation to specific regions of the image.Region of Interest Control
To restrict the image enhancement operations to specific regions of an image, a region-of-interest mask is created. A region of interest (ROI) is conceptually a mask of true or false values. The ROI mask controls which source image pixels are to be processed and which destination pixels are to be recorded.
JAI supports two different types of ROI mask: a Boolean mask and a threshold value. The
ROIShapeclass uses a Boolean mask, which allows operations to be performed quickly and with compact storage. TheROIclass allows the specification of a threshold value; pixel values greater than or equal to the threshold value are included in the ROI. Pixel values less than the threshold are excluded.The region of interest is usually defined using a
ROIShape, which stores its area using thejava.awt.Shapeclasses. These classes define an area as a geometrical description of its outline. TheROIclass stores an area as a single-banded image.An ROI can be attached to an image as a property. See Chapter 11, "Image Properties."
6.2.1
TheThe ROI Class
ROIclass stores an area as a grayscale (single-banded) image. This class represents region information in image form, and can thus be used as a fallback where aShaperepresentation is unavailable. Inclusion and exclusion of pixels is defined by a threshold value. Source pixel values greater than or equal to the threshold value indicate inclusion in the ROI and are processed. Pixel values less than the threshold value are excluded from processing.Where possible, subclasses such as
ROIShapeare used since they provide a more compact means of storage for large regions.The
getAsShape()method may be called optimistically on any instance ofROI. However, it may return null to indicate that aShaperepresentation of theROIis not available. In this case,getAsImage()should be called as a fallback.
API:javax.media.jai.ROI
- ROI(RenderedImage im)
- constructs an
ROIobject from aRenderedImage. The inclusion threshold is taken to be halfway between the minimum and maximum sample values specified by the image'sSampleModel.
Parameters: imA single-banded RenderedImage.
- ROI(RenderedImage im, int threshold)
- constructs an
ROIobject from aRenderedImage. The inclusionthresholdis specified explicitly.
Parameters: imA single-banded RenderedImage.
thresholdThe inclusion/exclusion threshold of the ROI.
- Shape getAsShape()
- returns a
Shaperepresentation of theROI, if possible. If none is available, null is returned. A proper instance ofROI(one that is not an instance of any subclass ofROI) will always return null.
- PlanarImage getAsImage()
- returns a
PlanarImagerepresentation of theROI. This method will always succeed.
- int getThreshold()
- returns the inclusion/exclusion threshold value.
- void setThreshold(int threshold)
- sets the inclusion/exclusion threshold value.
6.2.1.1
TheDetermining the ROI Bounds
getBoundsmethods in theROIclass read the bounds of theROI, as either aRectangleor aRectangle2D.
API:javax.media.jai.ROI
- Rectangle getBounds()
- returns the bounds of the
ROIas aRectangle.
- Rectangle2D getBounds2D()
- returns the bounds of the
ROIas aRectangle2D.6.2.1.2
TheDetermining if an Area Lies Within or Intersects the ROI
containsmethods in theROIclass test whether a given point or rectangular region lie within theROI. Theintersectsmethods test whether a given rectangular region intersect with theROI.
API:javax.media.jai.ROI
- boolean contains(Point p)
- returns true if the
Pointlies within theROI.
Parameters: pA Pointidentifying the pixel to be queried.
- boolean contains(Point2D p)
- returns true if the
Point2Dlies within the ROI.
Parameters: pA Point2Didentifying the pixel to be queried.
- boolean contains(int x, int y)
- returns true if the point lies within the ROI.
Parameters: xAn int specifying the x coordinate of the pixel to be queried.
yAn int specifying the y coordinate of the pixel to be queried.
- boolean contains(double x, double y)
- returns true if the point lies within the ROI.
Parameters: xA double specifying the x coordinate of the pixel to be queried.
yA double specifying the y coordinate of the pixel to be queried.
- boolean contains(Rectangle rect)
- returns true if the
Rectanglelies within the ROI.
Parameters: rectA Rectanglespecifying the region to be tested for inclusion.
- boolean contains(Rectangle2D r)
- returns true if the
Rectangle2Dlies within the ROI.
Parameters: rA Rectangle2Dspecifying the region to be tested for inclusion.
- boolean contains(int x, int y, int w, int h)
- returns true if the rectangle lies within the ROI.
Parameters: xThe int x coordinate of the upper left corner of the region.
yThe int y coordinate of the upper left corner of the region.
wThe int width of the region.
hThe int height of the region.
- boolean contains(double x, double y, double w, double h)
- returns true if the rectangle lies within the ROI.
Parameters: xThe double x coordinate of the upper left corner of the region.
yThe double y coordinate of the upper left corner of the region.
wThe double width of the region.
hThe double height of the region.
- boolean intersects(Rectangle rect)
- returns true if the
Rectangleintersects the ROI.
Parameters: rectA Rectanglespecifying the region to be tested for inclusion.
- boolean intersects(Rectangle2D r)
- returns true if the
Rectangle2Dintersects the ROI.
Parameters: rA Rectangle2Dspecifying the region to be tested for inclusion.
- boolean intersects(int x, int y, int w, int h)
- returns true if the rectangle intersects the ROI.
Parameters: xThe int x coordinate of the upper left corner of the region.
yThe int y coordinate of the upper left corner of the region.
wThe int width of the region.
hThe int height of the region.
- boolean intersects(double x, double y, double w, double h)
- returns true if the rectangle intersects the
ROI.
Parameters: xThe double x coordinate of the upper left corner of the region.
yThe double y coordinate of the upper left corner of the region.
wThe double width of the region.
hThe double height of the region.
6.2.1.3
Several methods allow the creation of a newCreating a New ROI from an Existing ROI
ROIfrom an existingROI. Theaddmethod adds another ROI to an existing one, creating a new ROI.
API:javax.media.jai.ROI
- ROI add(ROI im)
- adds another
ROIto this one and returns the result as a newROI. The addition is performed by an "AddROIs" RIF to be specified. The suppliedROIwill be converted to a rendered form if necessary.
Parameters: imAn ROI.
- ROI subtract(ROI im)
- subtracts another
ROIto this one and returns the result as a newROI. The subtraction is performed by a "SubtractROIs" RIF to be specified. The suppliedROIwill be converted to a rendered form if necessary.
Parameters: imAn ROI.
- ROI intersect(ROI im)
- intersects the
ROIwith anotherROIand returns the result as a newROI. The intersection is performed by a "IntersectROIs" RIF to be specified. The suppliedROIwill be converted to a rendered form if necessary.
Parameters: imAn ROI.
- ROI exclusiveOr(ROI im)
- exclusive-ORs the
ROIwith anotherROIand returns the result as a newROI. The intersection is performed by an "XorROIs" RIF to be specified. The suppliedROIwill be converted to a rendered form if necessary.
Parameters: imAn ROI.
- ROI transform(AffineTransform at)
- performs an affine transformation and returns the result as a new
ROI. The transformation is performed by an "Affine" RIF.
Parameters: atAn AffineTransformspecifying the transformation.
- ROI performImageOp(RenderedImageFactory RIF, ParameterBlock paramBlock, int sourceIndex, Hashtable renderHints, Hashtable renderHintsObserved)
- transforms an ROI using an imaging operation. The operation is specified by a
RenderedImageFactory. The operation'sParameterBlock, minus the image source itself is supplied, along with an index indicating where to insert theROIimage. The usualrenderHintsandrenderHintsObservedarguments allow rendering hints to be passed in and information on which hints were followed to be passed out.
Parameters: RIFA RenderedImageFactorythat will be used to create the op.
paramBlockA ParameterBlockcontaining all sources and parameters for the operation except for theROIitself.
sourceIndexThe index of the ParameterBlock's sources where theROIis to be inserted.
renderHintsA Hashtable of rendering hints.
renderHints-ObservedA Hashtable of observed rendering hints.
- ROI performImageOp(RenderedImageFactory RIF, ParameterBlock paramBlock, int sourceIndex)
- transforms an
ROIusing an imaging operation. The operation is specified by aRenderedImageFactory. The operation'sParameterBlock, minus the image source itself is supplied, along with an index indicating where to insert theROIimage. Rendering hints are taken to be null.
Parameters: RIFA RenderedImageFactorythat will be used to create the op.
paramBlockA ParameterBlockcontaining all sources and parameters for the operation except for theROIitself.
sourceIndexThe index of the ParameterBlock's sources where theROIis to be inserted.
- ROI performImageOp(String name, ParameterBlock paramBlock, int sourceIndex, Hashtable renderHints, Hashtable renderHintsObserved)
- transforms an
ROIusing an imaging operation. The operation is specified by name; the default JAI registry is used to resolve this into a RIF. The operation'sParameterBlock, minus the image source itself is supplied, along with an index indicating where to insert theROIimage. The usualrenderHintsandrenderHintsObservedarguments allow rendering hints to be passed in and information on which hints were followed to be passed out.
Parameters: nameThe name of the operation to be performed.
paramBlockA ParameterBlockcontaining all sources and parameters for the operation except for theROIitself.
sourceIndexThe index of the ParameterBlock's sources where theROIis to be inserted.
renderHintsA Hashtable of rendering hints.
renderHints-ObservedA Hashtable of observed rendering hints.
- ROI performImageOp(String name, ParameterBlock paramBlock, int sourceIndex)
- transforms an
ROIusing an imaging operation. The operation is specified by name; the default JAI registry is used to resolve this into a RIF. The operation'sParameterBlock, minus the image source itself is supplied, along with an index indicating where to insert the ROI image. Rendering hints are taken to be null.
Parameters: nameThe name of the operation to be performed.
paramBlockA ParameterBlockcontaining all sources and parameters for the operation except for theROIitself.
sourceIndexThe index of the ParameterBlock's sources where theROIis to be inserted.
- Shape getAsShape()
- returns a Shape representation of the
ROI, if possible. If none is available, null is returned. A proper instance ofROI(one that is not an instance of any subclass ofROI) will always return null.
- PlanarImage getAsImage()
- returns a
PlanarImagerepresentation of theROI. This method will always succeed.6.2.2
TheThe ROIShape Class
ROIShapeclass is used to store a region of interest within an image as an instance of ajava.awt.Shape. Such regions are binary by definition. Using aShaperepresentation allows Boolean operations to be performed quickly and with compact storage. If aPropertyGeneratorresponsible for generating theROIproperty of a particularOperationDescriptor(such as awarp) cannot reasonably produce anROIShaperepresenting the region, it should call thegetAsImage()method on its sources and produce its outputROIin image form.
API:javax.media.jai.ROIShape
- ROIShape(Shape s)
- constructs an
ROIShapefrom aShape.
Parameters: sA Shape.
- ROIShape(Area a)
- constructs an
ROIShapefrom anArea.
Parameters: aAn Area.
6.2.2.1
The following methods in theDetermining the ROI Bounds
ROIShapeclass read the bounds of theROI.
API:javax.media.jai.ROIShape
- Rectangle getBounds()
- returns the bounds of the ROI as a
Rectangle.
- Rectangle2D getBounds2D()
- returns the bounds of the ROI as a
Rectangle2D.6.2.2.2
TheDetermining if an Area Lies Within or Intersects the ROIShape
ROIShape.containsmethod is used to determine if a given pixel lies within the region of interest. TheROIShape.intersectsmethod is used to determine if a rectangular region of the image intersects the ROI.
API:javax.media.jai.ROIShape
- boolean contains(Point p)
- returns true if the pixel lies within the
ROI.
Parameters: pThe coordinates of the pixel to be queried.
- boolean contains(Point2D p)
- returns true if the pixel lies within the
ROI.
Parameters: pThe coordinates of the pixel to be queried.
- boolean contains(int x, int y)
- returns true if the pixel lies within the
ROI.
Parameters: xThe x coordinate of the pixel to be queried.
yThe y coordinate of the pixel to be queried.
- boolean contains(double x, double y)
- returns true if the pixel lies within the
ROI.
Parameters: xThe x coordinate of the pixel to be queried.
yThe y coordinate of the pixel to be queried.
- boolean contains(Rectangle rect)
- returns true if the rectangular region is entirely contained within the
ROI.
Parameters: rectThe region to be tested for inclusion.
- boolean contains(Rectangle2D r)
- returns true if the rectangular region is entirely contained within the
ROI.
Parameters: rThe region to be tested for inclusion.
- boolean contains(int x, int y, int w, int h)
- returns true if the rectangular region is entirely contained within the
ROI.
Parameters: xThe x coordinate of the pixel to be queried.
yThe y coordinate of the pixel to be queried.
wThe width of the region.
hThe height of the region.
- boolean contains(double x, double y, double w, double h)
- returns true if the rectangular region is entirely contained within the
ROI.
Parameters: xThe x coordinate of the pixel to be queried.
yThe y coordinate of the pixel to be queried.
wThe width of the region.
hThe height of the region.
- boolean intersects(Rectangle rect)
- returns true if the rectangular region intersects the
ROI.
Parameters: rectThe region to be tested for inclusion.
- boolean intersects(Rectangle2D r)
- returns true if the rectangular region intersects the
ROI.
Parameters: rectThe region to be tested for inclusion.
- boolean intersects(int x, int y, int w, int h)
- returns true if the rectangular region intersects the
ROI.
Parameters: xThe x coordinate of the upper left corner of the region.
yThe y coordinate of the upper left corner of the region.
wThe width of the region.
hThe height of the region.
- boolean intersects(double x, double y, double w, double h)
- returns true if the rectangular region intersects the
ROI.
Parameters: xThe x coordinate of the upper left corner of the region.
yThe y coordinate of the upper left corner of the region.
wThe width of the region.
hThe height of the region.
6.2.2.3
Several methods allow the creation of a newCreating a New ROIShape from an Old ROIShape
ROIShapefrom the oldROIShape.
API:javax.media.jai.ROIShape
- ROI add(ROI im)
- adds another mask to this one. This operation may force this mask to be rendered.
Parameters: imAn ROI.
- ROI subtract(ROI im)
- subtracts another mask from this one. This operation may force this mask to be rendered.
Parameters: imAn ROI.
- ROI intersect(ROI im)
- sets the mask to its intersection with another mask. This operation may force this mask to be rendered.
Parameters: imAn ROI.
- ROI exclusiveOr(ROI im)
- sets the mask to its exclusive-OR with another mask. This operation may force this mask to be rendered.
Parameters: imAn ROI.
- ROI transform(AffineTransform at)
- performs an affine transformation and returns the result as a new ROI. The transformation is performed by an "Affine" RIF.
Parameters: atThe affine transform.
- Shape getAsShape()
- returns the internal
Shaperepresentation or null if not possible. Since we have a shape available, we simply return it.
- PlanarImage getAsImage()
- returns the shape as a
PlanarImage. This requires performing an antialiased rendering of the internalShape. We use an eight-bit, single channel image with aComponentColorModeland aColorSpace.TYPE_GRAYcolor space.6.3
Given two source images and a destination image, the JAI relational operators allow you to:Relational Operators
- Find the larger of the pixels in the two source images and store the results in the destination (
Max).The relational operators require that both source images and the destination image have the same data type and number of bands. The sizes of the two images (height and width), however, need not be the same.
- Find the smaller of the pixels in the two source images and store the results in the destination (
Min).When determining the maximum and minimum pixels in the two images, JAI performs a band-by-band comparison.
Note: Don't confuse the relational Min and Max operators with the Extrema operation (see Section 9.3, "Finding the Extrema of an Image"), which finds the image-wise minimum and maximum pixel values for each band of an image.
6.3.1
TheFinding the Maximum Values of Two Images
maxoperation takes two rendered images, and for every pair of pixels, one from each source image of the corresponding position and band, finds the maximum pixel value.The two source images may have different numbers of bands and data types. By default, the destination image bound is the intersection of the two source image bounds. If the two source images don't intersect, the destination will have a width and a height of 0. The number of bands of the destination image is the same as the least number of bands of the source images, and the data type is the biggest data type of the source images.
The pixel values of the destination image are defined by the following pseudocode:
if (srcs[0][x][y][b] > srcs[1][x][y][b]) { dst[x][y][b] = srcs[0][x][y][b]; } else { dst[x][y][b] = srcs[1][x][y][b]; }Themaxoperation does not take any parameters. TheJAI.createmethod takes only the handles for the two images. Listing 6-1 shows a partial code sample of computing the pixelwise maximum value of two images in the rendered mode.
Listing 6-1 Finding the Maximum Value of Two Images
// Create two constant images RenderedOp im0 = JAI.create("constant", param1); RenderedOp im1 = JAI.create("constant", param2);// Find the maximum value of the two images RenderedOp im2 = JAI.create("max", im0, im1);
6.3.2
TheFinding the Minimum Values of Two Images
minoperation takes two rendered images, and for every pair of pixels, one from each source image of the corresponding position and band, finds the minimum pixel value.The two source images may have different numbers of bands and data types. By default, the destination image bound is the intersection of the two source image bounds. If the two source images don't intersect, the destination will have a width and a height of 0. The number of bands of the destination image is the same as the least number of bands of the source images, and the data type is the biggest data type of the source images.
The pixel values of the destination image are defined by the following pseudocode:
if (srcs[0][x][y][b] < srcs[1][x][y][b]) { dst[x][y][b] = srcs[0][x][y][b]; } else { dst[x][y][b] = srcs[1][x][y][b]; }Theminoperation does not require any parameters. TheJAI.createmethod takes only the handles for the two images. Listing 6-2 shows a partial code sample of computing the pixelwise minimum value of two images in the renderable mode.
Listing 6-2 Finding the Minimum Value of Two Images
// Set up the parameter block and add the two source images to it ParameterBlock pb = new ParameterBlock(); pb.add(im0); pb.add(im1);// Find the maximum value of the two images RenderableOp im2 = JAI.createRenderable("min", pb, hints);
6.4
JAI supports monadic, dyadic, and unary logical operators. The monadic logical operations include pixel-by-pixel AND, OR, and XOR operations between a source image and a constant to produce a destination image. The dyadic logical operations include pixel-by-pixel AND, OR, and XOR operations between two source images to produce a destination image. The unary logical operation is a NOT operation (complement image) on each pixel of a source image on a per-band basis.Logical Operators
JAI supports the following logical operations:
- Take the bitwise AND of the two source images and store the results in the destination (
And)
- Take the bitwise AND of a source image and one of a set of per-band constants (
AndConst)
- Take the bitwise OR of the two source images and store the results in the destination (
Or)
- Take the bitwise OR of a source image and one of a set of per-band constants (
OrConst)
- Take the bitwise XOR (exclusiveOR) of the two source images and store the results in the destination (
Xor)
- Take the bitwise XOR of a source image and one of a set of per-band constants (
XorConst)As with the relational operators, the logical operations require that both source images and the destination image have the same data type and number of bands. The sizes of the two images (height and width), however, need not be the same.
- Take the bitwise NOT of a source image on each pixel on a per-band basis (
Not)
6.4.1
TheANDing Two Images
Andoperation takes two source images, and performs a bit-wise logical AND on every pair of pixels, one from each source image, of the corresponding position and band.Both source images must have integral data types. The two data types may be different.
Unless altered by an
ImageLayouthint, the destination image bound is the intersection of the two source image bounds. If the two sources don't intersect, the destination will have a width and height of 0. The number of bands of the destination image is equal to the lesser number of bands of the source images, and the data type is the smallest data type with sufficient range to cover the range of both source data types.The following matrix defines the logical
Andoperation.
src0 src1 Result 0 0 0
0 1 0
1 0 0
1 1 1
The destination pixel values are defined by the following pseudocode:
dst[x][y][b] = srcs[0][x][y][b] & srcs[1][x][y][b];TheAndoperation takes no parameters.Listing 6-3 shows a partial code sample of using the
Andoperation to AND two images together.
Listing 6-3 ANDing Two Images
// Set up the parameter block and add the two source images to it. ParameterBlock pb = new ParameterBlock(); pb.add(im0); // The first image pb.add(im1); // The second image// AND the two images together. RenderableOp op = JAI.createRenderable("and", pb, hints);
6.4.2
TheANDing an Image with a Constant
AndConstoperation takes one rendered or renderable image and an array of integer constants, and performs a bit-wise logical AND between every pixel in the same band of the source and the constant from the corresponding array entry. If the number of constants supplied is less than the number of bands of the destination, then the constant from entry 0 is applied to all the bands. Otherwise, a constant from a different entry is applied to each band.The source image must have an integral data type. By default, the destination image bound, data type, and number of bands are the same as the source image.
The following matrix defines the logical
AndConstoperation:
src const Result 0 0 0
0 1 0
1 0 0
1 1 1
The destination pixel values are defined by the following pseudocode:
if (constants.length < dstNumBands) { dst[x][y][b] = srcs[x][y][b] & constants[0]; } else { dst[x][y][b] = srcs[x][y][b] & constants[b]; }TheAndConstoperation takes one parameter:
Parameter Type Description constants int The per-band constants to logically AND with.
Listing 6-4 shows a partial code sample of using the
AndConstoperation to AND a source image with a defined constant of value 1.2.
Listing 6-4 ANDing an Image with a Constant
// Set up the parameter block with the source and a constant // value. ParameterBlock pb = new ParameterBlock(); pb.add(im); // im as the source image pb.add(1.2f); // The constant // AND the image with the constant. RenderableOp op = JAI.createRenderable("andconst", pb, hints);
6.4.3
TheORing Two Images
Oroperation takes two rendered or renderable images, and performs a bit-wise logical OR on every pair of pixels, one from each source image of the corresponding position and band.Both source images must have integral data types. The two data types may be different.
Unless altered by an
ImageLayouthint, the destination image bound is the intersection of the two source image bounds. If the two sources don't intersect, the destination will have a width and height of 0. The number of bands of the destination image is equal to the lesser number of bands of the source images, and the data type is the smallest data type with sufficient range to cover the range of both source data types.The following matrix defines the logical
ORoperation:
src0 src1 Result 0 0 0
0 1 1
1 0 1
1 1 1
The destination pixel values are defined by the following pseudocode:
dst[x][y][b] = srcs[0][x][y][b] | srcs[1][x][y][b];TheOroperation takes no parameters.Listing 6-5 shows a partial code sample of using the
oroperation to OR two images.
Listing 6-5 ORing Two Images
// Read the first image. pb = new ParameterBlock(); pb.add(file1); RenderedOp src1 = JAI.create("stream", pb);// Read the second image. pb = new ParameterBlock(); pb.add(file2); RenderedImage src2 = JAI.create("stream", pb);// OR the two images. RenderedOp dst = JAI.create("or", src1, src2);
6.4.4
TheORing an Image with a Constant
OrConstoperation takes one rendered or renderable image and an array of integer constants, and performs a bit-wise logical OR between every pixel in the same band of the source image and the constant from the corresponding array entry. If the number of constants supplied is less than the number of bands of the destination, the constant from entry 0 is applied to all the bands. Otherwise, a constant from a different entry is applied to each band.The source image must have an integral data type. By default, the destination image bound, data type, and number of bands are the same as the source image.
The following matrix defines the logical
OrConstoperation:
src const Result 0 0 0
0 1 1
1 0 1
1 1 1
The destination pixel values are defined by the following pseudocode:
if (constants.length < dstNumBands) { dst[x][y][b] = src[x][y][b] | constants[0]; } else { dst[x][y][b] = src[x][y][b] | constants[b]; }TheOrConstoperation takes one parameter:
Parameter Type Description constants int The per-band constants to logically OR with.
6.4.5
TheXORing Two Images
Xoroperation takes two rendered or renderable images, and performs a bit-wise logical XOR on every pair of pixels, one from each source image of the corresponding position and band.Both source images must have integral data types. The two data types may be different.
Unless altered by an
ImageLayouthint, the destination image bound is the intersection of the two source image bounds. If the two source images don't intersect, the destination will have a width and height of 0. The number of bands of the destination image is equal to the lesser number of bands of the source images, and the data type is the smallest data type with sufficient range to cover the range of both source data types.The following matrix defines the
Xoroperation:
src0 src1 Result 0 0 0
0 1 1
1 0 1
1 1 1
The destination pixel values are defined by the following pseudocode:
dst[x][y][b] = srcs[0][x][y][b] ^ srcs[0][x][y][b];TheXoroperation takes no parameters.
6.4.6
TheXORing an Image with a Constant
XorConstoperation takes one rendered or renderable image and an array of integer constants, and performs a bit-wise logical OR between every pixel in the same band of the source and the constant from the corresponding array entry. If the number of constants supplied is less than the number of bands of the destination, the constant from entry 0 is applied to all the bands. Otherwise, a constant from a different entry is applied to each band.The source image must have an integral data type. By default, the destination image bound, data type, and number of bands are the same as the source image.
The following matrix defines the logical
XorConstoperation:
src const Result 0 0 0
0 1 1
1 0 1
1 1 0
The destination pixel values are defined by the following pseudocode:
if (constants.length < dstNumBands) { dst[x][y][b] = src[x][y][b] ^ constants[0]; } else { dst[x][y][b] = src[x][y][b] ^ constants[b]; }TheXorConstoperation takes one parameter:
Parameter Type Description constant int The constant to logically XOR with.
6.4.7
TheTaking the Bitwise NOT of an Image
Notoperation takes one rendered or renderable image, and performs a bit-wise logical NOT on every pixel from every band of the source image. This operation, also known as a complement operation, creates an image that is somewhat like a photographic negative.The
Notoperation looks at the values in the source image as binary values and changes all the 1's in those values to 0's, and all the 0's to 1's. The operation then writes the one's complement version of the source image to the destination.The source image must have an integral data type. By default, the destination image bound, data type, and number of bands are the same as the source image.
The following matrix defines the logical NOT operation.
src Result 1 0
0 1
The pixel values of the destination image are defined by the following pseudocode:
dst[x][y][b] = ~(src[x][y][b])TheNotoperation takes no parameters.Listing 6-6 shows a partial code sample of using the
Notoperation.
Listing 6-6 Taking the NOT of an Image
// Read the source image. pb = new ParameterBlock(); pb.addSource(file); RenderedOp src = JAI.create("stream", pb); // Create the Not operation. RenderedOp dst = JAI.create("Not", src);
6.5
JAI supports both monadic and dyadic arithmetic operators. The monadic arithmetic operations include per-band addition, subtraction, division, and multiplication operations between a source image and a constant to produce a destination image. The dyadic arithmetic operations include per-band addition, subtraction, division, and multiplication operations between two source images to produce a destination image.Arithmetic Operators
The JAI arithmetic operators allow you to:
- Add two source images and store the results in a destination image (
Add)
- Add a constant value to the pixels in a source image and store the results in a destination image (
AddConst)
- Add a collection of images and store the results in a destination image (
AddCollection)
- Add a an array of double constants to a collection of rendered images (
AddConstToCollection)
- Subtract one source image from an other and store the results in a destination image (
Subtract)
- Subtract a constant value from the pixels in a source image and store the results in a destination image (
SubtractConst)
- Divide one source image into an other and store the results in a destination image (
Divide)
- Divide two source images of complex data and store the results in a destination image (
DivideComplex)
- Divide a source image by a constant value (
DivideByConst)
- Divide a source image into a constant value (
DivideIntoConst)
- Multiply two source images and store the results in a destination image (
Multiply)
- Multiply a source image by a constant value (
MultiplyConst)
- Multiply two images representing complex data (
MultiplyComplex)
- Find the absolute value of pixels in a source image and store the results in a destination image (
Absolute)As with the relational and logical operators, the arithmetic operations require that both source images and the destination image have the same data type and number of bands. The sizes of the two images (height and width), however, need not be the same.
- Take the exponent of an image and store the results in a destination image (
Exp)When JAI adds two images, it takes the value at location 0,0 in one source image, adds it to the value at location 0,0 in the second source image, and writes the sum at location 0,0 in the destination image. It then does the same for all other points in the images. Subtraction, multiplication, and division are handled similarly.
Arithmetic operations on multi-band images are performed on corresponding bands in the source images. That is, band 0 of the first image is added to band 0 of the second image, and so on.
6.5.1
TheAdding Two Source Images
Addoperation takes two rendered or renderable source images, and adds every pair of pixels, one from each source image of the corresponding position and band. The two source images may have different numbers of bands and data types. By default, the destination image bounds are the intersection of the two source image bounds. If the sources don't intersect, the destination will have a width and height of 0.The default number of bands of the destination image is equal to the smallest number of bands of the sources, and the data type is the smallest data type with sufficient range to cover the range of both source data types (not necessarily the range of their sums).
As a special case, if one of the source images has N bands (where N is greater than one), the other source has one band, and an
ImageLayouthint is provided containing a destinationSampleModelwith K bands (1 < KN), then the single band of the one1-banded source is added to each of the first K bands of the N-band source.
The destination pixel values are defined by the following pseudocode:
dst[x][y][dstBand] = clamp(srcs[0][x][y][src0Band] + srcs[1][x][y][src1Band]);If the result of the addition underflows or overflows the minimum or maximum value supported by the destination image, the value will be clamped to the minimum or maximum value, respectively.The
Addoperation does not require any parameters.Listing 6-7 shows a partial code sample of using the
Addoperation to add two images.
Listing 6-7 Adding Two Images
// Read the two images. pb = new ParameterBlock(); pb.add(s1); RenderedImage src1 = (RenderedImage)JAI.create("stream", pb);pb = new ParameterBlock(); pb.add(s2); RenderedImage src2 = (RenderedImage)JAI.create("stream", pb);// Create the ParameterBlock for the operation pb = new ParameterBlock(); pb.addSource(src1); pb.addSource(src2);// Create the Add operation. RenderedImage dst = (RenderedImage)JAI.create("add", pb);
6.5.2
TheAdding a Constant Value to an Image
AddConstoperation adds one of a set of constant values to every pixel value of a source image on a per-band basis:if (constants.length < dstNumBands) { dst[x][y][b] = src[x][y][b] + constants[0]; else { dst[x][y][b] = src[x][y][b] + constants[b]TheAddConstoperation takes one parameter:
Parameter Type Description constants double The per-band constants to be added.
The set of
constantsmust contain one entry for each band of the source image. If the number of constants supplied is less than the number of bands of the destination image, the constant from entry 0 is applied to all the bands. Otherwise, a constant from a different entry is applied to each band.By default, the destination image bound, data type, and number of bands are the same as the source image.
If the result of the addition underflows or overflows the minimum or maximum value supported by the destination image, the value will be clamped to the minimum or maximum value, respectively.
Listing 6-8 shows a partial code sample of using the
AddConstoperation.
Listing 6-8 Adding a Constant to an Image
// Create the constant values. RenderedImage im1, im2; ParameterBlock pb; double k0, k1, k2;pb = new ParameterBlock(); pb.addSource(im1); double[] constants = new double[3]; // or however many bands // in im1 constants[0] = k0; constants[1] = k1; constants[2] = k2; pb.add(constants);// Construct the AddConst operation. RenderedImage addConstImage = JAI.create("addconst", pb, null);
6.5.3
TheAdding a Collection of Images
AddCollectionoperation takes a collection of rendered images and adds every set of pixels, one from each source image of the corresponding position and band.There's no restriction on the actual class type used to represent the source collection, but each element of the collection must be of the class
RenderedImages. The number of images in the collection may vary from two to n, and is only limited by memory size. The source images may have different number of bands and data types.By default, the destination image bound is the intersection of all the source image bounds. If any of the two sources don't intersect, the destination will have a width and a height of 0. The number of bands of the destination image is the same as the least number of bands of all the sources, and the data type is the biggest data type of all the sources.
The destination pixel values are calculated as:
dst[x][y][b] = 0; for (int i = 0; i < numSources; i++) { dst[x][y][b] += srcs[i][x][y][b]; }If the result of the operation underflows or overflows the minimum or maximum value supported by the destination data type, the value will be clamped to the minimum or maximum value, respectively.The
AddCollectionoperation does not require any parameters.
6.5.4
TheAdding Constants to a Collection of Rendered Images
AddConstToCollectionoperation takes a collection of rendered images and an array of double constants, and for each rendered image in the collection adds a constant to every pixel of its corresponding band.The operation will attempt to store the result images in the same collection class as that of the source images. If a new instance of the source collection class can not be created, the operation will store the result images in a
java.util.Vector. The output collection will contain the same number of images as in the source collection.The
AddConstToCollectionoperation takes one parameter.
Parameter Type Description constants double The constants to be added.
If the number of constants supplied is less than the number of bands of the source image, the same constant from entry 0 is applied to all the bands. Otherwise, a constant from a different entry is applied to each band.
6.5.5
TheSubtracting Two Source Images
Subtractoperation takes two rendered images, and for every pair of pixels, one from each source image of the corresponding position and band, subtracts the pixel from the second source from the pixel from the first source.The two source images may have different numbers of bands and data types. By default, the destination image bounds are the intersection of the two source image bounds. If the sources don't intersect, the destination will have a width and height of 0.
The default number of bands of the destination image is equal to the smallest number of bands of the source images, and the data type is the smallest data type with sufficient range to cover the range of both source data types (not necessarily the range of their sums).
As a special case, if one of the source images has N bands (where N is greater than one), the other source has one band, and an
ImageLayouthint is provided containing a destinationSampleModelwith K bands (1 < KN), then the single band of the one-banded source is subtracted from or into each of the first K bands of the N-band source.
The destination pixel values are defined by the following pseudocode:
dst[x][y][dstBand] = clamp(srcs[0][x][y][src0Band] - srcs[1][x][y][src1Band]);If the result of the subtraction underflows or overflows the minimum or maximum value supported by the destination image, the value will be clamped to the minimum or maximum value respectively.The
Subtractoperation does not require any parameters.
6.5.6
TheSubtracting a Constant from an Image
SubtractConst operation takes one rendered or renderable image and an array of double constants, and subtracts every pixel of the same band of the source from the constant from the corresponding array entry. If the number of constants supplied is less than the number of bands of the destination, the constant from entry 0 is applied to all the bands. Otherwise, a constant from a different entry is applied to each band.By default, the destination image bound, data type, and number of bands are the same as the source image.
The destination pixel values are defined by the following pseudocode:
if (constants.length < dstNumBands) { dst[x][y][b] = constants[0] - src[x][y][b]; } else { dst[x][y][b] = constants[b] - src[x][y][b]; }TheSubtractConst operation takes one parameter:
Parameter Type Description constants double The per-band constants to be subtracted.
If the result of the subtraction underflows or overflows the minimum or maximum value supported by the destination image, the value will be clamped to the minimum or maximum value respectively.
6.5.7
TheSubtracting an Image from a Constant
SubtractFromConstoperation takes one rendered or renderable source image and an array of double constants, and subtracts a constant from every pixel of its corresponding band of the source image. If the number of constants supplied is less than the number of bands of the destination, the constant from entry 0 is applied to all the bands. Otherwise, a constant from a different entry is applied to each band. By default, the destination image bounds, data type, and number of bands are the same as the source image.The destination pixel values are defined by the following pseudocode:
if (constants.length < dstNumBands) { dst[x][y][b] = src[x][y][b] - constants[0]; } else { dst[x][y][b] = src[x][y][b] - constants[b]; }TheSubtractFromConst operation takes one parameter:
Parameter Type Description constants double The constants to be subtracted.
If the result of the subtraction underflows or overflows the minimum or maximum value supported by the destination image, the value will be clamped to the minimum or maximum value respectively.
6.5.8
TheDividing One Image by Another Image
Divideoperation takes two rendered or renderable images, and for every pair of pixels, one from each source image of the corresponding position and band, divides the pixel from the first source by the pixel from the second source.In case of division by 0, if the numerator is 0, the result is set to 0; otherwise, the result is set to the maximum value supported by the destination data type.
The
Divideoperation does not require any parameters.The two source images may have different number of bands and data types. By default, the destination image bound is the intersection of the two source image bounds. If the two sources don't intersect, the destination will have a width and a height of 0. The default number of bands of the destination image is the same as the least number of bands of the source images, and the data type is the biggest data type of the sources.
As a special case, if one of the source images has N bands (where N is greater than one), the other source has one band, and an
ImageLayouthint is provided containing a destinationSampleModelwith K bands (1 < KN), then the single band of the one-banded source will be divided by or into to each of the first K bands of the N-band source.
If the result of the operation underflows or overflows the minimum or maximum value supported by the destination data type, it will be clamped to the minimum or maximum value respectively.
The
Divideoperation does not require any parameters.
6.5.9
TheDividing an Image by a Constant
DivideByConstoperation takes one rendered or renderable source image and an array of double constants, and divides every pixel of the same band of the source by the constant from the corresponding array entry. If the number of constants supplied is less than the number of bands of the destination, the constant from entry 0 is applied to all the bands. Otherwise, a constant from a different entry is applied to each band.In case of division by 0, if the numerator is 0, the result is set to 0. Otherwise, the result is set to the maximum value supported by the destination data type. By default, the destination image bound, data type, and number of bands are the same as the source image.
The destination pixel values are defined by the following pseudocode:
if (constants.length < dstNumBands) { dst[x][y][b] = srcs[x][y][b]/constants[0]; } else { dst[x][y][b] = srcs[x][y][b]/constants[b]; }TheDivideByConstoperation takes one parameter:
Parameter Type Description constants double The per-band constants to divide by.
If the result of the division underflows or overflows the minimum or maximum value supported by the destination image, the value will be clamped to the minimum or maximum value, respectively.
6.5.10
TheDividing an Image into a Constant
DivideIntoConstoperation takes one rendered or renderable image and an array of double constants, and divides every pixel of the same band of the source into the constant from the corresponding array entry. If the number of constants supplied is less than the number of bands of the destination, the constant from entry 0 is applied to all the bands. Otherwise, a constant from a different entry is applied to each band.In case of division by 0, if the numerator is 0, the result is set to 0. Otherwise, the result is set to the maximum value supported by the destination data type.
By default, the destination image bound, data type, and number of bands are the same as the source image.
The destination pixel values are defined by the following pseudocode:
if (constants.length < dstNumBands) { dst[x][y][b] = constants[0]/src[x][y][b]; } else { dst[x][y][b] = constants[b]/src[x][y][b]; }TheDivideIntoConstoperation takes one parameter:
Parameter Type Description constants double The per-band constants to be divided into.
If the result of the division underflows or overflows the minimum or maximum value supported by the destination image, the value will be clamped to the minimum or maximum value, respectively.
6.5.11
TheDividing Complex Images
DivideComplexoperation divides two images representing complex data. The source images must each contain an even number of bands with the even-indexed bands (0, 2, etc.) representing the real and the odd-indexed bands (1, 3, etc.) the imaginary parts of each pixel. The destination image similarly contains an even number of bands with the same interpretation and with contents defined by:a = src0[x][y][2k]; b = src0[x][y][2k + 1]; c = src1[x][y][2k]; d = src1[x][y][2k + 1]; dst[x][y][2k] = (a*c + b*d)/(c2 + d2) dst[x][y][2k + 1] = (b*c - a*d)/(c2 + d2)With one exception, the number of bands of the destination image is the same as the minimum of the number of bands of the two sources, and the data type is the biggest data type of the sources. The exception occurs when one of the source images has two bands, the other source image has N = 2K bands where K is greater than one, and an
- where
![]()
ImageLayouthint is provided containing a destinationSampleModelthat specifies M = 2L bands for the destination image where L is greater than one and LK. In this special case if the first source has two bands, its single complex component will be divided by each of the first L complex components of the second source. If the second source has two bands, its single complex component will divide each of the L complex components of the first source.
If the result of the operation underflows or overflows the minimum or /maximum value supported by the destination data type, it will be clamped to the minimum or maximum value, respectively.
The
DivideComplexoperation does not require any parameters.
6.5.12
TheMultiplying Two Images
Multiplyoperation takes two rendered images, and multiplies every pair of pixels, one from each source image of the corresponding position and band.The two source images may have different number of bands and data types. By default, the destination image bound is the intersection of the two source image bounds. If the two source images don't intersect, the destination will have a width and a height of 0.
The default number of bands of the destination image is the same as the least number of bands of the source images, and the data type is the biggest data type of the source images. A special case may occur if one of the source images has N bands where N is greater than one, the other source has one band, and an
ImageLayouthint is provided containing a destinationSampleModel. If theSampleModelhint specifies K bands for the destination image where K is greater than one and KN, each of the first K bands of the N-band source is multiplied by the single band of the one-band source.
In the default case the destination pixel values are calculated as:
for (int h = 0; h < dstHeight; h++) { for (int w = 0; w < dstWidth; w++) { for (int b = 0; b < dstNumBands; b++) { dst[h][w][b] = src1[h][w][b] * src2[h][w][b]; } } }TheMultiplyoperation does not require any parameters.If the result of the multiplication underflows or overflows the minimum or maximum value supported by the destination image, the value will be clamped to the minimum or maximum value, respectively.
6.5.13
TheMultiplying an Image by a Constant
MultiplyConstoperation takes one rendered or renderable image and an array of double constants, and multiplies every pixel of the same band of the source by the constant from the corresponding array entry. If the number of constants supplied is less than the number of bands of the destination, the constant from entry 0 is applied to all the bands. Otherwise, a constant from a different entry is applied to each band. By default, the destination image bound, data type, and number of bands are the same as the source image.The destination pixel values are calculated as:
if (constants.length < dstNumBands) { dst[x][y][b] = srcs[x][y][b]*constants[0]; } else { dst[x][y][b] = srcs[x][y][b]*constants[b]; }TheMultiplyConstoperation takes a single parameter:
Parameter Type Description constants double The per-band constants to multiply by.
If the result of the multiplication underflows or overflows the minimum or maximum value supported by the destination image, the value will be clamped to the minimum or maximum value respectively.
6.5.14
TheMultiplying Two Complex Images
MultiplyComplexoperation multiplies two images representing complex data. The source images must each contain an even number of bands, with the with the even-indexed bands (0, 2, etc.) representing the real and the odd-indexed bands (1, 3, etc.) the imaginary parts of each pixel. The destination image similarly contains an even number of bands with the same interpretation and with contents defined by:a = src0[x][y][2k]; b = src0[x][y][2k + 1]; c = src1[x][y][2k]; d = src1[x][y][2k + 1]; dst[x][y][2k] = a*c - b*d; dst[x][y][2k + 1] = a*d + b*c;With one exception, the number of bands of the destination image is the same as the minimum of the number of bands of the two source images, and the data type is the biggest data type of the sources. The exception occurs when one of the source images has two bands, the other source image has N = 2K bands where K is greater than one, and an
- where
![]()
ImageLayouthint is provided containing a destinationSampleModelthat specifies M = 2L bands for the destination image where L is greater than one and LK. In this special case each of the first L complex components in the N-band source will be multiplied by the single complex component in the one-band source.
If the result of the operation underflows or overflows the minimum or maximum value supported by the destination data type, it will be clamped to the minimum or maximum value, respectively.
The
MultiplyComplexoperation does not require any parameters.
6.5.15
Images with signed integer pixels have an asymmetrical range of values fromFinding the Absolute Value of Pixels
-32,768 to 32,767, which is not very useful for many imaging operations. TheAbsoluteoperation takes a single rendered or renderable source image, and computes the mathematical absolute value of each pixel:if (src[x][y][b] < 0) { dst[x][y][b] = -src[x][y][b]; } else { dst[x][y][b] = src[x][y][b]; }For signed integral data types, the smallest value of the data type does not have a positive counterpart; such values will be left unchanged. This behavior parallels that of the Java unary minus operator.The
Absoluteoperation does not require any parameters
6.5.16
TheTaking the Exponent of an Image
Expoperation takes the exponential of the pixel values of an image. The pixel values of the destination image are defined by the following pseudocode:dst[x][y][b] = java.lang.Math.exp(src[x][y][b])TheExpoperation does not require any parameters. For integral image datatypes, the result will be rounded and clamped as needed.Listing 6-9 shows a partial code sample of using the
Expoperation to take the exponent of an image.
Listing 6-9 Taking the Exponent of an Image
// Create a ParameterBlock with the source image. pb = new ParameterBlock(); pb.addSource(src);// Perform the Exp operation RenderedImage dst = JAI.create("exp", pb);
6.6
The display of a 24-bit color image on an 8-bit frame buffer requires an operation known as dithering. The dithering operation compresses the three bands of an RGB image to a single-banded byte image.Dithering an Image
The dithering operation uses a lookup table through which the source image is passed to produce the destination image. The most-common use for the dithering operation is to convert true-color (three-band byte) images to pseudo-color (single-band byte) images.
JAI offers two operations for dithering an image: ordered dither and error-diffusion dither. The choice of dithering operation depends on desired speed and image quality, as shown in Table 6-1.
Table 6-1 Dithering Choices
Dither Type Relative Speed Relative Quality Ordered Medium Medium
Error diffusion Slowest Best
6.6.1
Ordered Dither
The ordered dithering operation is somewhat faster than the error-diffusion dither and produces a somewhat better destination image quality than the error-diffusion dither. TheOrderedDitheroperation also differs from error-diffusion dither in that it (OrderedDither) uses a color cube rather than a general lookup table.The
OrderedDitheroperation performs color quantization by finding the nearest color to each pixel in a supplied color cube lookup table and "shifting" the resulting index value by a pseudo-random amount determined by the values of a supplied dither mask.The
OrderedDitheroperation takes two parameters:
Parameter Type Description colorMap ColorCube The color cube. See Section 6.6.1.1, "Color Map Parameter."
ditherMask KernelJAI[] The dither mask. See Section 6.6.1.2, "Dither Mask Parameter."
6.6.1.1
TheColor Map Parameter
colorMapparameter can be either one of the predefinedColorCubes, or a custom color map can be created as aColorCubeobject. To create a custom color map, see Section 7.6.1.3, "Creating a Color-cube Lookup Table."The predefined color maps are:
6.6.1.2
The dither mask is a three-dimensional array of floating point values, the depth of which equals the number of bands in the image. The dither mask is supplied as an array ofDither Mask Parameter
KernelJAIobjects. Each element of the array is aKernelJAIobject that represents the dither mask matrix for the corresponding band. AllKernelJAIobjects in the array must have the same dimensions and contain floating point values greater than or equal to 0.0 and less than or equal to 1.0.The
ditherMaskparameter may either be one of the predefined dither masks or a custom mask may be created. To create a custom dither mask, see Section 6.9, "Constructing a Kernel."The predefined dither masks are (see Figure 6-1):
Figure 6-1 Ordered Dither Masks
6.6.1.3
Listing 6-10 shows a partial code sample of using theOrderedDither Example
OrderedDitheroperation.
Listing 6-10 Ordered Dither Example
// Create the color cube. ColorCube colorMap = srcRescale.getSampleModel().getTransferType() == DataBuffer.TYPE_BYTE ? ColorCube.BYTE_496 : ColorCube.createColorCube(dataType, 38, new int[] {4, 9, 6});// Set the dither mask to the pre-defined 4x4x3 mask. KernelJAI[] ditherMask = KernelJAI.DITHER_MASK_443;// Create a new ParameterBlock. ParameterBlock pb = new ParameterBlock(); pb.addSource(srcRescale).add(colorMap).add(ditherMask);// Create a gray scale color model. ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY); int bits[] = new int[] {8}; ColorModel cm = new ComponentColorModel(cs, bits, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);// Create a tiled layout with the requested ColorModel. layout = new ImageLayout(); layout.setTileWidth(TILE_WIDTH).setTileHeight(TILE_HEIGHT); layout.setColorModel(cm);// Create RenderingHints for the ImageLayout. rh = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout); // Create theordereddither OpImage. PlanarImage image = (PlanarImage)JAI.create("ordereddither", pb, rh);
6.6.2
The error-diffusion dithering operation produces the most accurate destination image, but is more complex and thus takes longer than the ordered dither.Error-diffusion Dither
The
ErrorDiffusionoperation performs color quantization by finding the nearest color to each pixel in a supplied lookup table, called a color map, and "diffusing" the color quantization error below and to the right of the pixel.The source image and the color map must have the same data type and number of bands. Also, the color map must have the same offset in all bands. The resulting image is single-banded.
The
ErrorDiffusionoperation takes two parameters:
6.6.2.1
TheError Filter Kernel
errorKernelparameter can be one of three predefined error filters or you can create your own. To create your own, see Section 6.9, "Constructing a Kernel."The predefined kernels are (see Figure 6-2):
The error filter kernel, also known as the error distribution filter, diffuses the color quantization error below and to the right of the pixel. The elements of the error filter kernel that are in the same row and to the right of the key element or are in a row below that of the key element must be between 0.0 and 1.0 and must sum to approximately 1.0. The other elements of the error filter kernel are ignored.
In operation, the filter is laid on top of the source image so that its origin aligns with the pixel to be passed through the lookup table. Figure 6-3 shows an example using the Floyd-Steinberg filter. The diffusion operation then:
- Sets the pixel at 0,2 to 214 + (5 x [7/16])
- Sets the pixel at 1,0 to 128 + (5 x [3/16])
- Sets the pixel at 1,1 to 255 + (5 x [5/16])
The filter is then moved to the next pixel and the process is repeated. The result of this process is an averaging that produces a smoother dithered image with little or no contouring.
- Sets the pixel at 1,2 to 104 + (5 x [1/16])
Figure 6-2 Error Diffusion Dither Filters
Figure 6-3 Error Diffusion Operation
6.6.2.2
Listing 6-11 shows a partial code sample of using theErrorDiffusion Example
ErrorDiffusionoperation.
Listing 6-11 Error Diffusion Example
// Create a color map with the 4-9-6 color cube and the // Floyd-Steinberg error kernel. ParameterBlock pb; pb.addSource(src); pb.add(ColorCube.BYTE_496); pb.add(KernelJAI.ERROR_FILTER_FLOYD_STEINBERG); // Perform the error diffusion operation. dst = (PlanarImage)JAI.create("errordiffusion", pb, null);
6.7
TheClamping Pixel Values
clampoperation restricts the range of pixel values for a source image by constraining the range of pixels to defined "low" and "high" values. The operation takes one rendered or renderable source image, and sets all the pixels whose value is below a low value to that low value and all the pixels whose value is above a high value to that high value. The pixels whose value is between the low value and the high value are left unchanged.A different set of low and high values may be applied to each band of the source image, or the same set of low and high values may be applied to all bands of the source. If the number of low and high values supplied is less than the number of bands of the source, the values from entry 0 are applied to all the bands. Each low value must be less than or equal to its corresponding high value.
The pixel values of the destination image are defined by the following pseudocode:
lowVal = (low.length < dstNumBands) ? low[0] : low[b]; highVal = (high.length < dstNumBands) ? high[0] : high[b]; if (src[x][y][b] < lowVal) { dst[x][y][b] = lowVal; } else if (src[x][y][b] > highVal) { dst[x][y][b] = highVal; } else { dst[x][y][b] = src[x][y][b]; }Theclampoperation takes two parameters:
Parameter Type Description low Double The lower boundary for each band.
high Double The upper boundary for each band.
Listing 6-12 shows a partial code sample of using the
Clampoperation to clamp pixels values to between 5 and 250.
Listing 6-12 Clamp Operation
// Get the source image width, height, and SampleModel. int w = src.getWidth(); int h = src.getHeight(); int b = src.getSampleModel().getNumBands();// Set the low and high clamp values. double[] low, high;low = new double[b]; high = new double[b];for (int i=0; i<b; i++) { low[i] = 5; // The low clamp value high[i] = 250; // The high clamp value }// Create the ParameterBlock with the source and parameters. pb = new ParameterBlock(); pb.addSource(src); pb.add(low); pb.add(high);// Perform the operation. RenderedImage dst = JAI.create("clamp", pb);
6.8
TheBand Copying
BandSelectoperation chooses N bands from a rendered or renderable source image and copies the pixel data of these bands to the destination image in the order specified. ThebandIndicesparameter specifies the source band indices, and its size (bandIndices.length) determines the number of bands of the destination image. The destination image may have ay number of bands, and a particular band of the source image may be repeated in the destination image by specifying it multiple times in thebandIndicesparameter.Each of the
bandIndicesvalue should be a valid band index number of the source image. For example, if the source only has two bands, 1 is a valid band index, but 3 is not. The first band is numbered 0.The destination pixel values are defined by the following pseudocode:
dst[x][y][b] = src[x][y][bandIndices[b]];Thebandselectoperation takes one parameter:
Parameter Type Description bandIndices int[] The indices of the selected bands of the image.
Listing 6-13 shows a partial code sample of using the
BandSelectoperation.
Listing 6-13 BandSelect Operation
// Set the indices of three bands of the image. int[] bandIndices; bandIndices = new int[3]; bandIndices[0] = 0; bandIndices[1] = 2; bandIndices[2] = 2;// Construct the ParameterBlock. pb = new ParameterBlock(); pb.addSource(src); pb.add(bandIndices);// Perform the operation RenderedImage dst = (RenderedImage)JAI.create("bandSelect", pb);
6.9
TheConstructing a Kernel
KernelJAIclass is an auxiliary class used with the convolve, ordered dither, error diffusion dither, dilate, and erode operations. AKernelJAIis characterized by its width, height, and key element (origin) position. The key element is the element that is placed over the current source pixel to perform convolution or error diffusion.For the
OrderedDitheroperation (see Section 6.6.1, "Ordered Dither"), an array ofKernelJAIobjects is actually required with there being oneKernelJAIper band of the image to be dithered. The location of the key element is in fact irrelevant to theOrderedDitheroperation.There are four constructors for creating a
KernelJAI. The following constructor constructs aKernelJAIobject with the given parameters.KernelJAI(int width, int height, float[] data)Thewidthandheightparameters determine the kernel size. Thedataparameter is a pointer to the floating point values stored in a data array. The key element is set toThe following constructor constructs a
![]()
KernelJAIobject with the given parameters.KernelJAI(int width, int height, int xOrigin, int yOrigin,The
float[] data)xOriginandyOriginparameters determine the key element's origin.The following constructor constructs a separable
KernelJAIobject from two float arrays.KernelJAI(int width, int height, int xOrigin, int yOrigin,The
float[] dataH, float[] dataV)dataHanddataVparameters specify the float data for the horizontal and vertical directions, respectively.The following constructor constructs a
KernelJAIobject from ajava.awt.image.Kernelobject.KernelJAI(java.awt.image.Kernel k)Listing 6-14 shows a partial code sample for creating a simple 3 x 3 kernel with the key element located at coordinates 1,1, as shown in Figure 6-4.
Listing 6-14 Constructing a KernelJAI
kernel = new KernelJAI; float[] kernelData = { 0.0F, 1.0F, 0.0F, 1.0F, 1.0F, 1.0F, 0.0F, 1.0F, 0.0F }; kernel = new KernelJAI(3, 3, 1, 1, kernelData);
The Java Advanced Imaging API provides a shorthand method for creating several commonly-used kernels, listed in Table 6-2, which can simply be called by name. These kernels and their use are described in more detail in Section 6.6.1, "Ordered Dither," Section 6.6.2, "Error-diffusion Dither," and Section 9.5, "Edge Detection."
The following code sample shows the format for creating a named kernel:
KernelJAI kernel = KernelJAI.ERROR_FILTER_FLOYD_STEINBERG;
API:javax.media.jai.KernelJAI
- public KernelJAI(int width, int height, int xOrigin, int yOrigin, float[] data)
- constructs a
KernelJAIwith the given parameters. The data array is copied.
Parameters: widthThe width of the kernel.
heightThe height of the kernel
xOriginThe x coordinate of the key kernel element.
yOriginThe y coordinate of the key kernel element.
dataThe float data in row-major format.
- public KernelJAI(int width, int height, int xOrigin, int yOrigin, float[] dataH, float[] dataV)
- constructs a separable
KernelJAIfrom two float arrays. The data arrays are copied.
Parameters: dataHThe float data for the horizontal direction.
dataVThe float data for the vertical direction.
- public KernelJAI(int width, int height, float[] data)
- constructs a
KernelJAIwith the given parameters. The data array is copied. The key element is set to (trunc(width/2), trunc(height/2)).
Parameters: dataThe float data in row-major format.
- public KernelJAI(Kernel k)
- constructs a
KernelJAIfrom ajava.awt.image.Kernelobject.
![]()
![]()
![]()
Programming in Java Advanced Imaging
Copyright © 1999, Sun Microsystems, Inc. All rights reserved.
Casa de Bender