001: /*
002: * $RCSfile: ImagePyramid.java,v $
003: *
004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Use is subject to license terms.
007: *
008: * $Revision: 1.1 $
009: * $Date: 2005/02/11 04:57:09 $
010: * $State: Exp $
011: */
012: package javax.media.jai;
013:
014: import java.awt.image.RenderedImage;
015: import java.util.Vector;
016:
017: /**
018: * A class implementing the "Pyramid" operation on a
019: * <code>RenderedImage</code>. Given a <code>RenderedImage</code>
020: * which represents the image at the highest resolution level,
021: * the images at lower resolution levels may be derived by
022: * performing a specific chain of operations to downsample the
023: * image at the higher resolution level repeatedly. Similarly,
024: * once an image at a lower resolution level is obtained, the images
025: * at higher resolution levels may be retrieved by performing a
026: * specific chain of operations to upsample the image at the lower
027: * resolution level repeatedly.
028: *
029: * <p> When an image is downsampled, the image at the higher resolution
030: * level is lost. However, the difference image between the original image
031: * and the image obtained by up sampling the downsampled result image is
032: * saved. This difference image, combined with the up sampling operations
033: * is used to retrieve the image at a higher resolution level from the
034: * image at a lower resolution level.
035: *
036: * <p> This is a bi-directional operation. A user may request an image
037: * at any resolution level greater than or equal to the highest
038: * resolution level, which is defined as level 0.
039: *
040: * <p> The <code>downSampler</code> is a chain of operations that is
041: * used to derive the image at the next lower resolution level from
042: * the image at the current resolution level. That is, given an image
043: * at resolution level <code>i</code>, <code>downSampler</code> is
044: * used to obtain the image at resolution level <code>i+1</code>.
045: * The chain may contain one or more operation nodes; however, each
046: * node must be a <code>RenderedOp</code>. The parameter points to the
047: * last node in the chain. The very first node in the chain must be
048: * a <code>RenderedOp</code> that takes one <code>RenderedImage</code>
049: * as its source. All other nodes may have multiple sources. When
050: * traversing back up the chain, if a node has more than one source,
051: * the first source, <code>source0</code>, is used to move up the
052: * chain. This parameter is saved by reference.
053: *
054: * <p> The <code>upSampler</code> is a chain of operations that is
055: * used to derive the image at the next higher resolution level from
056: * the image at the current resolution level. That is, given an image
057: * at resolution level <code>i</code>, <code>upSampler</code> is
058: * used to obtain the image at resolution level <code>i-1</code>.
059: * The requirement for this parameter is identical to that of the
060: * <code>downSampler</code> parameter.
061: *
062: * <p> The <code>differencer</code> is a chain of operations that is used
063: * to find the difference between an image at a particular resolution
064: * level and the image obtained by first down sampling that image
065: * then up sampling the result image of the down sampling operations.
066: * The chain may contain one or more operation nodes; however, each
067: * node must be a <code>RenderedOp</code>. The parameter points to the
068: * last node in the chain. The very first node in the chain must be
069: * a <code>RenderedOp</code> that takes two <code>RenderedImage</code>s
070: * as its sources. When traversing back up the chain, if a node has
071: * more than one source, the first source, <code>source0</code>, is
072: * used to move up the chain. This parameter is saved by reference.
073: *
074: * <p> The <code>combiner</code> is a chain of operations that is
075: * used to combine the result image of the up sampling operations
076: * and the difference image saved to retrieve an image at a higher
077: * resolution level. The requirement for this parameter is identical
078: * to that of the <code>differencer</code> parameter.
079: *
080: * <p> Reference:
081: * "The Laplacian Pyramid as a Compact Image Code"
082: * Peter J. Burt and Edward H. Adelson
083: * IEEE Transactions on Communications, Vol. COM-31, No. 4, April 1983
084: *
085: * @see ImageMIPMap
086: *
087: */
088: public class ImagePyramid extends ImageMIPMap {
089:
090: /** The operation chain used to derive the higher resolution images. */
091: protected RenderedOp upSampler;
092:
093: /** The operation chain used to differ two images. */
094: protected RenderedOp differencer;
095:
096: /** The operation chain used to combine two images. */
097: protected RenderedOp combiner;
098:
099: /** The default constructor. */
100: protected ImagePyramid() {
101: }
102:
103: /** The saved difference images. */
104: private Vector diffImages = new Vector();
105:
106: /**
107: * Constructor. The <code>RenderedOp</code> parameters point to
108: * the last operation node in each chain. The first operation in
109: * each chain must not have any source images specified; that is,
110: * its number of sources must be 0. All input parameters are saved
111: * by reference.
112: *
113: * @param image The image with the highest resolution.
114: * @param downSampler The operation chain used to derive the lower
115: * resolution images.
116: * @param upSampler The operation chain used to derive the higher
117: * resolution images.
118: * @param differencer The operation chain used to differ two images.
119: * @param combiner The operation chain used to combine two images.
120: *
121: * @throws IllegalArgumentException if <code>image</code> is <code>null</code>.
122: * @throws IllegalArgumentException if <code>downSampler</code> is
123: * <code>null</code>.
124: * @throws IllegalArgumentException if <code>upSampler</code> is
125: * <code>null</code>.
126: * @throws IllegalArgumentException if <code>differencer</code> is
127: * <code>null</code>.
128: * @throws IllegalArgumentException if <code>combiner</code> is
129: * <code>null</code>.
130: */
131: public ImagePyramid(RenderedImage image, RenderedOp downSampler,
132: RenderedOp upSampler, RenderedOp differencer,
133: RenderedOp combiner) {
134: super (image, downSampler);
135:
136: if (upSampler == null || differencer == null
137: || combiner == null) {
138: throw new IllegalArgumentException(JaiI18N
139: .getString("Generic0"));
140: }
141:
142: this .upSampler = upSampler;
143: this .differencer = differencer;
144: this .combiner = combiner;
145: }
146:
147: /**
148: * Constructor. The <code>RenderedOp</code> parameters point to
149: * the last operation node in each chain. The first operation in
150: * the <code>downSampler</code> chain must have the image with
151: * the highest resolution as its source. The first operation in
152: * all other chains must not have any source images specified;
153: * that is, its number of sources must be 0. All input parameters
154: * are saved by reference.
155: *
156: * @param downSampler The operation chain used to derive the lower
157: * resolution images.
158: * @param upSampler The operation chain used to derive the higher
159: * resolution images.
160: * @param differencer The operation chain used to differ two images.
161: * @param combiner The operation chain used to combine two images.
162: *
163: * @throws IllegalArgumentException if <code>downSampler</code> is
164: * <code>null</code>.
165: * @throws IllegalArgumentException if <code>upSampler</code> is
166: * <code>null</code>.
167: * @throws IllegalArgumentException if <code>differencer</code> is
168: * <code>null</code>.
169: * @throws IllegalArgumentException if <code>combiner</code> is
170: * <code>null</code>.
171: * @throws IllegalArgumentException if <code>downSampler</code>
172: * has no sources.
173: * @throws IllegalArgumentException if an object other than a
174: * <code>RenderedImage</code> is found in the
175: * <code>downSampler</code> chain.
176: */
177: public ImagePyramid(RenderedOp downSampler, RenderedOp upSampler,
178: RenderedOp differencer, RenderedOp combiner) {
179: super (downSampler);
180:
181: if (upSampler == null || differencer == null
182: || combiner == null) {
183: throw new IllegalArgumentException(JaiI18N
184: .getString("Generic0"));
185: }
186:
187: this .upSampler = upSampler;
188: this .differencer = differencer;
189: this .combiner = combiner;
190: }
191:
192: /**
193: * Returns the image at the specified resolution level. The
194: * requested level must be greater than or equal to 0 or
195: * <code>null</code> will be returned. The image is obtained
196: * by either down sampling or up sampling the current image.
197: *
198: * @param level The specified resolution level.
199: */
200: public RenderedImage getImage(int level) {
201: if (level < 0) {
202: return null;
203: }
204:
205: while (currentLevel < level) {
206: getDownImage();
207: }
208: while (currentLevel > level) {
209: getUpImage();
210: }
211:
212: return currentImage;
213: }
214:
215: /**
216: * Returns the image at the next lower resolution level,
217: * obtained by applying the <code>downSampler</code> on the
218: * image at the current resolution level.
219: */
220: public RenderedImage getDownImage() {
221: currentLevel++;
222:
223: // Duplicate the downSampler op chain.
224: RenderedOp downOp = duplicate(downSampler,
225: vectorize(currentImage));
226:
227: // Save the difference image.
228: RenderedOp upOp = duplicate(upSampler, vectorize(downOp
229: .getRendering()));
230: RenderedOp diffOp = duplicate(differencer, vectorize(
231: currentImage, upOp.getRendering()));
232: diffImages.add(diffOp.getRendering());
233:
234: currentImage = downOp.getRendering();
235: return currentImage;
236: }
237:
238: /**
239: * Returns the image at the previous higher resolution level,
240: * If the current image is already at level 0, then the current
241: * image will be returned without further up sampling.
242: *
243: * <p> The image is obtained by first up sampling the current
244: * image, then combine the result image with the previously saved
245: * difference image using the <code>combiner</code> op chain.
246: */
247: public RenderedImage getUpImage() {
248: if (currentLevel > 0) {
249: currentLevel--;
250:
251: // Duplicate the upSampler op chain.
252: RenderedOp upOp = duplicate(upSampler,
253: vectorize(currentImage));
254:
255: // Retrieve diff image for this level.
256: RenderedImage diffImage = (RenderedImage) diffImages
257: .elementAt(currentLevel);
258: diffImages.removeElementAt(currentLevel);
259:
260: RenderedOp combOp = duplicate(combiner, vectorize(upOp
261: .getRendering(), diffImage));
262: currentImage = combOp.getRendering();
263: }
264:
265: return currentImage;
266: }
267:
268: /**
269: * Returns the difference image between the current image and the
270: * image obtained by first down sampling the current image then up
271: * sampling the result image of down sampling. This is done using
272: * the <code>differencer</code> op chain. The current level and
273: * current image will not be changed.
274: */
275: public RenderedImage getDiffImage() {
276: // First downsample.
277: RenderedOp downOp = duplicate(downSampler,
278: vectorize(currentImage));
279:
280: // Then upsample.
281: RenderedOp upOp = duplicate(upSampler, vectorize(downOp
282: .getRendering()));
283:
284: // Find the difference image.
285: RenderedOp diffOp = duplicate(differencer, vectorize(
286: currentImage, upOp.getRendering()));
287:
288: return diffOp.getRendering();
289: }
290: }
|