@(#)Collection.txt 1.10 Proposal: Image Collection API Modifications ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Brian Burkhalter Created: 6 July 2000 Revised: 12/18/00 CHANGES DURING COMMUNITY REVIEW ------------------------------- . Added sink management methods to CollectionImage; remove from CollectionOp. . CollectionOp.createRendering() returns a Collection instead of a CollectionImage. . Implicit change: do not internally cache an "instance" of a CollectionOp. . Add a method CollectionOp.resetProperties() to reset the property environment of a CollectionOp node. . Added specification of sink chaining in CollectionOp nodes. . Changed types of old and new values of a CollectionChangeEvent from CollectionImage to Collection. PROPOSAL -------- 1. Motivation This proposal arises in response to a request to improve the ability of JAI to handle operations which produce multiple destination images. Examples of such operations include: a) Classification A classification procedure, e.g., supervised classification such as the minimum distance classifier, produces a classmap image as well as an error image representing the classification error at each pixel location. b) Registration A registration procedure accepts a set of images and some parameters and warps each image individually to produce a separate output for each image. The registration could be either multitemporal in which case all images are approximately co-located or could be distributed in which case the overlapped images collectively cover some larger geographical area. Investigation conducted with respect to this request has concluded that the deficiencies of using JAI for such operations could perhaps best be minimized by improving the existing JAI CollectionImage API. In this context it was also observed that many of the features implemented for the rendered and renderable imaging modes were lacking in the collection processing framework. These are in particular the lack of property management as present in JAI 1.0.2 and the lack of chain editing and event propagation as proposed for JAI 1.1. Also, the implementation of renderable collections in JAI 1.0.2 is lacking particularly in that a given collection OperationDescriptor cannot specify an operation which supports both the rendered and renderable modes. Background information for some concepts referred to in this proposal is available in the previously published proposals "Support for Editing Rendered Op Chains" and "Improved Property Management". 2. Incidental Changes The following class revisions are not essential to this proposal but are recommended. 2.1 Define Two New Classes 2.1.1 AttributedImage package javax.media.jai; /** * A class which associates a PlanarImage with an attribute * of unspecified type. The class is itself a PlanarImage * equivalent to the one which it wraps. * * @since 1.1 */ public class AttributedImage extends RenderedImageAdapter { /** The attribute associated with the image. */ protected Object attribute; /** * Constructs an AttributedImage. The attribute parameter * may be null * * @throws IllegalArgumentException if theImage is * null. */ public AttributedImage(PlanarImage image, Object attribute) {} /** Retrieves the wrapped image. */ public PlanarImage getImage() {} /** Stores the attribute. */ public void setAttribute(Object attribute) {} /** Retrieves the attribute. */ public Object getAttribute() {} /** * Tests for equality. The parameter Object must be * an AttributedImage the image and attribute of which * are equal those of this object according to the equals() * methods of the image and attribute of this image, respectively. * Attributes are also considered equal if they are both null. */ public boolean equals(Object o) {} } 2.1.2 AttributedImageCollection /** * Class representing a CollectionImage wherein all image elements are * AttributedImage instances. All Collection methods will be overridden * such that contained images are forced to be AttributedImages. * *

Note that the methods getAll(attribute) and * removeAll(attribute) use the equals() method * of the attribute parameter rather that that of the * AttributedImages in the Collection. This * permits "filtering" if the attribute of the AttributedImages * contains more than one type of value. For example, if the attribute * contained both position and time, then the parameter attribute * could be an instance of a class which compared only the position if it * were desired to obtain or remove all images at a given position * irrespective of the time stamp. * * @since 1.1 */ public class AttributedImageCollection extends CollectionImage { protected AttributedImageCollection() {} /** * Constructs an AttributedImageCollection with contents * set to the contents of the supplied Collection. Only * elements in the Collection which are instances of * AttributedImage will be added. * * @throws IllegalArgumentException if images is * null */ public AttributedImageCollection(Collection images) {} /** * Returns a Set of all AttributedImages the attribute of which is * equal to the parameter object according to the equals() method of * the parameter object. If no match is found null will be returned. * If the parameter is null a Set view of all AttributedImages in the * Collection will be returned. */ public Set getAll(Object attribute) {} /** * Returns a Set of all AttributedImages the image of which is equal * to the parameter image. If no match is found null will be returned. * If the parameter is null a Set view of all AttributedImages in the * Collection will be returned. */ public Set getAll(PlanarImage image) {} /** * Removes all AttributedImages the attribute of which is * equal to the parameter object according to the equals() method of the * parameter object. The returned value contains all AttributedImages * which were removed from the underlying Collection or null if no * match was found. */ public Set removeAll(Object attribute) {} /** * Removes all AttributedImages the image of which is equal to the * parameter image. The returned value contains all AttributedImages * which were removed from the underlying Collection or null if no * match was found. */ public Set removeAll(PlanarImage image) {} /* -- CollectionImage methods: ensure elements are AttributedImages. -- */ /** * Adds the specified object to this Collection. This * method overrides the superclass method in order to perform a * type check on the object being added. * * @throws IllegalArgumentException if o is null * or is not an AttributedImage. * * @return true if and only if the parameter is added to the * Collection. */ public boolean add(Object o) {} /** * Adds to this Collection all elements in the specified * Collection which are AttributedImages. * * @return true if this Collection changed * as a result of the call. */ public boolean addAll(Collection c) {} /** * Returns the first attributed image found in the collection * that contains the planar image argument. */ public AttributedImage getAttributedImage(PlanarImage image) {} /** * Returns the first attributed image found in the collection * that contains the attribute. */ public AttributedImage getAttributedImage(Object attribute) {} 2.2 Deprecate Some Extant Classes The classes listed in this section will be deprecated as they may easily be replaced by use of the new classes described in the previous section. A description of how this may be effected should be included in the updated comments of each of the deprecated classes. Suggestions as to the nature of this documentation are included for each respective class. 2.2.1 CoordinateImage This is equivalent to an AttributedImage wherein the attribute is a coordinate. 2.2.2 SequentialImage This is equivalent to an AttributedImage wherein the attribute encompasses both time and camera position. For example, the attribute could be defined to be an instance of public class SequentialAttribute { protected Object position; protected Float timeStamp; public SequentialAttribute(Object position, float timeStamp); public Object getPosition(); public float getTimeStamp(); public boolean equals(Object o) { if(o instanceof SequentialAttribute) { SequentialAttribute sa = (SequentialAttribute)o; return sa.getPosition().equals(position) && sa.getTimeStamp().equals(timeStamp); } return false; } } 2.2.3 ImageStack This is equivalent to an AttributedImageCollection containing AttributedImages the attribute of which is an Object which represents a coordinate. 2.2.4 ImageSequence This is equivalent to an AttributedImageCollection containing AttributedImages the attribute of which is an Object containing time and position such as an instance of the SequentialAttribute class defined above. If one wanted to retrieve all images at a specific position and time, an instance of SequentialAttribute would be passed to getAll(Object). If one wanted to retrieve all images at a given position ignoring the time stamp one could for example define a class such as public class Position { protected Object position; public Position(Object position); public Object getPosition(); public boolean equals(Object o) { if(o instanceof Position) { return ((Position)o).getPosition().equals(position); } else if(o instance of SequentialAttribute) { return ((SequentialAttribute)o).getPosition().equals(position); } return false; } } and invoke the getAll(Object) method with the appropriate Position. Since it is the equals() method of the parameter that is used to test for equality, the time stamp of the attribute would be ignored. 3. Fundamental Modifications 3.1 Renderable Collections The current architecture of the Collection framework for images in JAI does not permit a Collection operation to operate in both the rendered and renderable modes with only a single OperationDescriptor. This section specifies the image factory changes suggested to improve support for renderable collections. Related modifications to other classes will be included in subsequent sections. 3.1.1 CollectionImageFactory Modify the specification to indicate that image factories which implement the create() method defined by this interface are expected to operate in rendered mode. /** * The CollectionImageFactory (CIF) interface is intended * to be implemented by classes that wish to act as factories to produce * different collection image operators. In JAI, the create() * method will be invoked in a chain of CollectionOps when the * operation is being executed in rendered mode. */ public interface CollectionImageFactory { /** * Creates a CollectionImage that represents the * result of an operation (or chain of operations) for a given * ParameterBlock and RenderingHints. * If the operation is unable to handle the input arguments, this * method should return null. * *

Generally this method is expected to be invoked by an operation * being executed in rendered mode. * * @param args Input arguments to the operation, including * sources and/or parameters. * @param hints The rendering hints. * * @return A CollectionImage containing the desired output. */ CollectionImage create(ParameterBlock args, RenderingHints hints); } 3.1.2 RenderableCollectionImageFactory Define a new image factory for CollectionImages in renderable mode as: /** * The RenderableCollectionImageFactory (RCIF) interface * is intended to be implemented by classes that wish to act as factories * to produce different collection image operators. In JAI, the * create() method defined by this sub-interface will be * invoked in a chain of CollectionOps when the operation is * being executed in renderable mode. The images contained in the * generated CollectionImage would be expected to be * RenderableImages. * * @since 1.1 */ public interface RenderableCollectionImageFactory { /** * Creates a CollectionImage that represents the * result of an operation (or chain of operations) for a given * ParameterBlock. * If the operation is unable to handle the input arguments, this * method should return null. * *

Generally this method is expected to be invoked by an operation * being executed in renderable mode. Therefore the images contained * in the generated CollectionImage would be expected to * be RenderableImages. * * @param args Input arguments to the operation, including * sources and/or parameters. * * @return A CollectionImage containing the desired output. */ CollectionImage create(ParameterBlock parameters); } 3.2 OperationRegistry Add methods to handle the new RenderableCollectionImageFactory interface and the property environment of CollectionOp nodes. Discussion of this topic has been moved to the JAI OperationRegistry Modification Proposal. 3.3 CollectionImage 3.3.1 Complete ImageJAI Implementation * Implement all WritablePropertySource methods conformant to the specification. Currently getPropertyNames() always returns null and getProperty() always returns java.awt.Image.UndefinedProperty. * An instance variable of class WritablePropertySourceImpl may be used as a utility to implement the mechanics of these methods. 3.3.2 Implement PropertyChangeEmitter Add a mechanism identical to that described for PlanarImage in "Improved Property Management". An instance variable of class PropertyChangeSupport may be used to implement the mechanics of these methods. 3.3.3 Add Sink Methods Add methods to manage sinks: addSink(), removeSink(), removeSinks(), getSinks(). 3.4 CollectionOp 3.4.1 Implement OperationNode Interface Implement the OperationNode interface defined in the "Improved Property Management" proposal. In particular this is important in order to add support for dynamic property management equivalent to that supplied in the rendered and renderable image layers. In addition to internal changes to CollectionOp this implies adding methods to modify the local property environment of the node from that inherited from the global environment maintained by the OperationRegistry. Instance variables which have types PropertyChangeSupport, WritablePropertySourceImpl, and OperationNodeSupport may be used as utilities to implement the requisite methods. Note again that here we are referring to the properties of the Collection as a whole and not to the properties of the elements of the Collection. Consistency of the graph sink list should be maintained. The constructor should add the node as a sink of any PlanarImage or CollectionImage sources and all methods which modify the source list should be updated to guarantee consistency of the sources' sink list. 3.4.2 Add Support for Renderable Mode 3.4.2.1 Add an instance variable /** * Flag indicating whether the operation is being instantiated in * renderable mode. */ protected boolean isRenderable = false; 3.4.2.2 Add a constructor /** * Constructs a CollectionOp that will be used to * instantiate a particular Collection operation from a given * operation registry, an operation name, a ParameterBlock, * and a set of rendering hints. * *

If isRenderable is true, the * createRenderableCollection() method of the * OperationRegistry will be used to construct the * Collection rendering of the node; otherwise * createCollection() will be used. * * @param registry The OperationRegistry to be used for * instantiation. if null, the default registry * is used. Saved by reference. * @param opName The operation name. Saved by reference. * @param pb The sources and other parameters. If null, * it is assumed that this node has no sources and parameters. * This parameter is cloned. * @param hints The rendering hints. If null, it is assumed * that no hints are associated with the rendering. * This parameter is cloned. * @param isRenderable Whether the operation is being executed in * renderable mode. * * @throws NullPointerException if opName * is null. */ public CollectionOp(OperationRegistry registry, String opName, ParameterBlock pb, RenderingHints hints, boolean isRenderable) {} 3.4.2.3 Add an Accessor /** * Returns whether the operation is being instantiated in renderable mode. */ public boolean isRenderable() {} 3.4.2.4 Add a Rendering Method /** * Returns the Collection rendering associated with this * operation with any contained RenderableImages rendered * using the supplied RenderContext parameter. If the * operation is being executed in rendered mode * (isRenderable() returns false), invoking * this method is equivalent to invoking getCollection(), * i.e., the parameter is ignored. If the operation is being * executed in renderable mode, the Collection will differ * from that returned by getCollection() due to any contained * RenderableImages having been rendered. If the * Collection contains any nested Collections, * these will be unwrapped recursively such that a rendering is created * for all RenderableImages encountered. */ public Collection createRendering(RenderContext renderContext) {} 3.4.3 Add a Method to Reset Property Environment /** * Resets the PropertySource. If the parameter is * true then the property environment is completely * reset; if false then only cached properties are * cleared, i.e., those which were derived from the property * environment and are now stored in the local cache. */ protected synchronized void resetProperties(boolean resetPropertySource) {} This method existed but had private access. 3.5 CollectionOp Chain Editing and Event Propagation 3.5.1 CollectionImage Modify to keep a record of the CollectionImageFactory which created the CollectionImage. When a given CollectionImageFactory creates a CollectionImage it should invoke setImageFactory() with itself as parameter before returning from create(). 3.5.1.1 Add imageFactory Variable /** * The CollectionImageFactory which created this * CollectionImage; may be null which * implies that the CollectionImage was not created * by a CollectionImageFactory. */ protected CollectionImageFactory imageFactory; 3.5.1.2 Add a Mutator /** * Sets the imageFactory instance variable to the supplied * value. The parameter may be null. It is recommended * that this method be invoked as soon as the CollectionImage * is constructed. * * @param The creating CollectionImageFactory or * null * @throws IllegalStateException if the corresponding instance variable * was already set. */ public void setImageFactory(CollectionImageFactory imageFactory) {} 3.5.1.3 Add an Accessor /** * If this CollectionImage was created by a * CollectionImageFactory then return a reference to * that factory; otherwise return null. */ public CollectionImageFactory getImageFactory() {} 3.5.2 CollectionImageFactory Add specification of an update() Method /** * Attempts to modify a rendered CollectionImage previously * created by this CollectionImageFactory as a function * of how the sources, parameters and hints of the operation have * changed. The CollectionImage passed in should not be * modified in place but some or or all of its contents may be copied * by reference into the CollectionImage returned, if any. * If none of the contents of the old CollectionImage can * be re-used, then null should be returned. * * @throws IllegalArgumentException if the name of the operation * associated with the CollectionOp does not * match that expected by this CollectionImageFactory. * * @return A CollectionImage modified according to the * new values of the ParameterBlock and * RenderingHints or null if it * is impracticable to perform the update. */ CollectionImage update(ParameterBlock oldParamBlock, RenderingHints oldHints, ParameterBlock newParamBlock, RenderingHints newHints, CollectionImage oldRendering, CollectionOp op); 3.5.3 CollectionOp Modifications similar to those described for rendered operation chains in the proposal "Support for Editing Rendered Op Chains" will be made for CollectionOp chains. * CollectionOp should implement java.util.PropertyChangeListener. * Modifications analogous to those described for RenderedOp in section 2.2 of "Support for Editing Rendered Op Chains" should be made for CollectionOp chains. Differences between CollectionOp and RenderedOp editing would be primarily that CollectionOp would generate a CollectionChangeEvent instead of a RenderingChangeEvent. The name of the CollectionChangeEvent would be "Collection" and its old and new values would be the previous and current versions of the CollectionImage generated by the CollectionImageFactory associated by the OperationRegistry with the operation. 3.5.3.1 CollectionChangeEvent The CollectionChangeEvent class is defined as import java.beans.PropertyChangeEvent; /** * Class representing an event generated by a CollectionOp * when the wrapped CollectionImage is regenerated. */ public class CollectionChangeEvent extends PropertyChangeEventJAI { public CollectionChangeEvent(CollectionOp source, Collection oldValue, Collection newValue) { super(source, "Collection", oldValue, newValue); } } 3.5.3.2 Collection Regeneration Algorithm If a Collection is requested via getCollection(), the operation name, ParameterBlock or RenderingHints are subsequently edited, and another Collection is requested via getCollection(), then the following should occur: a) If the operation name has changed, a new CollectionImage will be generated by the OperationRegistry for the new operation. If the operation name has not changed, an attempt will be made to re-use some elements of the previously generated CollectionImage by invoking update() on the CollectionImageFactory which generated it. If this attempt fails, a new CollectionImage for this operation will be requested from the OperationRegistry. b) A CollectionChangeEvent will be fired to all listeners with new and old values set to the previous and current CollectionImages associated with this node. A node will also respond to a RenderingChangeEvent or a CollectionChangeEvent emitted by a RenderedOp or CollectionOp source, respectively. These would be managed similarly to the source list being edited. 4. Convergence of CollectionImage and RenderedImage A utility class RenderedImageList would be defined as follows: import java.util.List; package javax.media.jai; /** * A CollectionImage which is also a * RenderedImage. The underlying Collection * in this case is required to be a List containing only * RenderedImages. * *

Instances of this class may be returned from either a * RenderedImageFactory or from a * CollectionImageFactory. This class would be * particularly useful for implementing operations the result of which * includes a single primary image and one or more secondary or * dependant images the data of which are less likely to be requested. * *

Invocations of RenderedImage methods on an instance * of this class will be forwarded to the first RenderedImage * in the underlying List, i.e., the image at index zero. * This should be the index assigned to the primary image in the case * alluded to above. If there are no images in the List * when a RenderedImage method is invoked an * IllegalStateException will be thrown. * *

One example of the use of this class is in generating a classmap * image using a classification algorithm. A by-product image of such an * operation is often an error image wherein the value of each pixel is * some measure of the classification error at that pixel. In this case * the classmap image would be stored at index zero in the internal list * and the error image at the unity index. The error image would be * an OpImage that has the classmap image as its source. * The result is that a reference to the error image is always available * with the classmap image but the computation of the error image pixel * values may be deferred until such time as the data are needed, if ever. * *

Methods defined in the RenderedImage and List * interfaces are not all commented in detail. The List methods * merely forward the call in most cases directly to the underlying * List; as previously stated, RenderedImage method * invocations are forwarded to the RenderedImage at position * zero in the List. * * @since Java Advanced Imaging 1.1 * @see CollectionImage * @see java.awt.image.RenderedImage * @see java.util.List */ public class RenderedImageList extends CollectionImage implements List, RenderedImage, Serializable { /** * Creates an empty RenderedImageList. */ protected RenderedImageList() {} /** * Creates a RenderedImageList from the supplied * List. * * @throws IllegalArgumentException if any objects in the * List are not RenderedImages. * @throws IllegalArgumentException if the List * is empty. * @throws IllegalArgumentException if the List * parameter is null. */ public RenderedImageList(List renderedImageList) {} /// --- RenderedImage methods. --- /** * Returns the first image in the underlying list of * RenderedImages. */ public RenderedImage getPrimaryImage() {} /** * Returns the X coordinate of the leftmost column of the * primary image. */ public int getMinX() {} /** Returns the X coordinate of the uppermost row of the primary image. */ public int getMinY() {} /** Returns the width of the primary image. */ public int getWidth() {} /** Returns the height of the primary image. */ public int getHeight() {} /** Returns the width of a tile of the primary image. */ public int getTileWidth() {} /** Returns the height of a tile of the primary image. */ public int getTileHeight() {} /** * Returns the X coordinate of the upper-left pixel of tile (0, 0) * of the primary image. */ public int getTileGridXOffset() {} /** * Returns the Y coordinate of the upper-left pixel of tile (0, 0) * of the primary image. */ public int getTileGridYOffset() {} /** * Returns the horizontal index of the leftmost column of tiles * of the primary image. */ public int getMinTileX() {} /** * Returns the number of tiles of the primary image along the * tile grid in the horizontal direction. */ public int getNumXTiles() {} /** * Returns the vertical index of the uppermost row of tiles * of the primary image. */ public int getMinTileY() {} /** * Returns the number of tiles of the primary image along the * tile grid in the vertical direction. */ public int getNumYTiles() {} /** Returns the SampleModel of the primary image. */ public SampleModel getSampleModel() {} /** Returns the ColorModel of the primary image. */ public ColorModel getColorModel() {} /** * Gets a property from the property set of this image. If the * property name is not recognized, * java.awt.Image.UndefinedProperty will be returned. * * @param name the name of the property to get, as a * String. * @return a reference to the property * Object, or the value * java.awt.Image.UndefinedProperty. * @throws IllegalArgumentException if name is * null. */ public Object getProperty(String name) {} /** * Returns a list of the properties recognized by this image. If * no properties are available, null will be * returned. * * @return an array of Strings representing valid * property names. */ public String[] getPropertyNames() {} /** * Returns a Vector containing the image sources. */ public Vector getSources() {} /** * Returns tile (tileX, tileY) of the * primary image as a Raster. Note that tileX * and tileY are indices into the tile array, not pixel * locations. * * @param tileX The X index of the requested tile in the tile array. * @param tileY The Y index of the requested tile in the tile array. */ public Raster getTile(int tileX, int tileY) {} /** * Returns the entire primary image in a single Raster. * For images with multiple tiles this will require making a copy. * The returned Raster is semantically a copy. * * @return a Raster containing a copy of this image's data. */ public Raster getData() {} /** * Returns an arbitrary rectangular region of the primary image * in a Raster. The rectangle of interest will be clipped * against the image bounds. The returned Raster is * semantically a copy. * * @param bounds the region of the RenderedImage to be * returned. */ public Raster getData(Rectangle bounds) {} /** * Copies an arbitrary rectangular region of the primary image * into a caller-supplied WritableRaster. The region to be * computed is determined by clipping the bounds of the supplied * WritableRaster against the bounds of the image. The supplied * WritableRaster must have a SampleModel that is compatible with * that of the image. * *

If the raster argument is null, the entire image will * be copied into a newly-created WritableRaster with a SampleModel * that is compatible with that of the image. * * @param dest a WritableRaster to hold the returned portion of * the image. * @return a reference to the supplied WritableRaster, or to a * new WritableRaster if the supplied one was null. */ public WritableRaster copyData(WritableRaster dest) {} /// --- List methods. --- /** * Inserts the specified element at the specified position in this list. * * @throws IllegalArgumentException if the specified element * is not a RenderedImage. * @throws IndexOutOfBoundsException if the index is out of range * (index < 0 || index > size()). */ public void add(int index, Object element) {} /** * Inserts into this List at the indicated position * all elements in the specified Collection which are * RenderedImages. * * @return true if this List changed * as a result of the call. */ public boolean addAll(int index, Collection c) {} public Object get(int index) {} public int indexOf(Object o) {} public int lastIndexOf(Object o) {} public ListIterator listIterator() {} public ListIterator listIterator(int index) {} public Object remove(int index) {} public Object set(int index, Object element) {} public List subList(int fromIndex, int toIndex) {} // --- Collection methods: overridden to require RenderedImages. --- /** * Adds the specified object to this List. * * @throws IllegalArgumentException if o is null * or is not an RenderedImage. * * @return true if and only if the parameter is added to the * List. */ public boolean add(Object o) {} /** * Adds to this List all elements in the specified * Collection which are RenderedImages. * * @return true if this List changed * as a result of the call. */ public boolean addAll(Collection c) {} } As stated in the class comments, invocations of RenderedImage methods on an instance of this class would be forwarded to the image at index zero in the list. Invocation of any accessors or mutators of the underlying List would be intercepted if it were appropriate to do so in order to verify that any supplied arguments are RenderedImages. 5. Sample Algorithms The algorithms in this section sketch how the architecture may be used to handle the examples mentioned in the first section. 5.1 Classification (minimum distance) Input: a RenderedImage and a set of class exemplars. Classes: * MinDistanceOpImage extends PointOpImage. * ClassificationErrorOpImage extends PointOpImage RIF or CIF: 1) create MinDistanceOpImage from source and exemplars 2) create ClassificationErrorOpImage from source, image (1), and exemplars 3) create RenderedImageList 4) add (1) and (2) to (3) 5) return (4) 5.2 Registration Input: a Collection of RenderedImages and some parameters. CIF: 1) compute warp coefficients from sources and parameters 2) warp each source image independently using different coefficients 3) create a CollectionImage 4) add each warped image in (2) to (3) 5) return (4)