001: import java.awt.*;
002: import java.awt.image.*;
003: import java.net.URL;
004: import JSci.maths.*;
005: import JSci.maths.matrices.DoubleMatrix;
006: import JSci.maths.matrices.DoubleSparseMatrix;
007: import JSci.maths.vectors.DoubleSparseVector;
008: import JSci.maths.wavelet.*;
009: import JSci.util.ArrayCaster;
010:
011: /**
012: * This is a simple implementation of the PixelGrabber class
013: * to allow for easier image processing.
014: * Basically, it reads a graphic file and allows you to get
015: * integer arrays from it for convenient processing.
016: * The name of the class comes from the fact that the image
017: * is stored as an internal int[][] array.
018: *
019: * This might seem inefficient since the JDK stores images
020: * as an int[] array and that we must go back and forth
021: * between the two formats.
022: * It is indeed slower, but as far as image processing is concerned,
023: * it is much simpler to work with an int[][] array.
024: * Moreover, if the processing is moderatly involved,
025: * it won't make much difference.
026: * @author Daniel Lemire
027: */
028: public final class PixelArray implements ImageObserver {
029: private int width = -1;
030: private int height = -1;
031: /********************************************
032: * The RGB model is assumed
033: *********************************************/
034: ColorModel cm = ColorModel.getRGBdefault();
035: private boolean loaded = false;
036: private int[][] array;
037:
038: private PixelArray() {
039: }
040:
041: /****************************************************
042: * Constructor
043: * @param filename file containing the image
044: * @exception IllegalArgumentException if the file
045: * can't be open. Either the format is wrong or the
046: * file cannot be found.
047: *****************************************************/
048: public PixelArray(String filename) {
049: waitForImage(Toolkit.getDefaultToolkit().getImage(filename));
050: }
051:
052: /****************************************************
053: * Constructor
054: * @param filename file containing the image
055: * @exception IllegalArgumentException if the file
056: * can't be open. Either the format is wrong or the
057: * file cannot be found.
058: *****************************************************/
059: public PixelArray(URL url) {
060: waitForImage(Toolkit.getDefaultToolkit().getImage(url));
061: }
062:
063: public PixelArray(int[][] I) {
064: width = I[0].length;
065: height = I.length;
066: array = ArrayMath.copy(I);
067: }
068:
069: public PixelArray(double[][] D) {
070: width = D[0].length;
071: height = D.length;
072: array = ArrayMath.copy(ArrayCaster.toInt(D));
073: }
074:
075: public Object clone() {
076: PixelArray pa = new PixelArray();
077: pa.loaded = true;
078: pa.array = new int[this .height][this .width];
079: for (int k = 0; k < width; k++) {
080: for (int l = 0; l < height; l++) {
081: pa.array[l][k] = this .array[l][k];
082: }
083: }
084: pa.cm = this .cm;
085: pa.height = this .height;
086: pa.width = this .width;
087: return (pa);
088: }
089:
090: public int getWidth() {
091: return (width);
092: }
093:
094: public int getHeight() {
095: return (height);
096: }
097:
098: private void computeArray(int[] p) {
099: array = new int[height][width];
100: for (int k = 0; k < width; k++) {
101: for (int l = 0; l < height; l++) {
102: array[l][k] = p[k + l * width];
103: }
104: }
105: }
106:
107: public void setRedArray(int[][] I) {
108: int g, b, a;
109: width = I[0].length;
110: height = I.length;
111: array = new int[height][width];
112: for (int k = 0; k < width; k++) {
113: for (int l = 0; l < height; l++) {
114: g = getGreen(l, k);
115: b = getBlue(l, k);
116: a = getAlpha(l, k);
117: array[l][k] = RGBtoInt(I[l][k], g, b, a);
118: }
119: }
120: }
121:
122: public void setGreenArray(int[][] I) {
123: int r, b, a;
124: width = I[0].length;
125: height = I.length;
126: array = new int[height][width];
127: for (int k = 0; k < width; k++) {
128: for (int l = 0; l < height; l++) {
129: r = getRed(l, k);
130: b = getBlue(l, k);
131: a = getAlpha(l, k);
132: array[l][k] = RGBtoInt(r, I[l][k], b, a);
133: }
134: }
135: }
136:
137: public void setBlueArray(int[][] I) {
138: int r, g, a;
139: width = I[0].length;
140: height = I.length;
141: array = new int[height][width];
142: for (int k = 0; k < width; k++) {
143: for (int l = 0; l < height; l++) {
144: r = getGreen(l, k);
145: g = getBlue(l, k);
146: a = getAlpha(l, k);
147: array[l][k] = RGBtoInt(r, g, I[l][k], a);
148: }
149: }
150: }
151:
152: public void setAlphaArray(int[][] I) {
153: int r, g, a;
154: width = I[0].length;
155: height = I.length;
156: array = new int[height][width];
157: for (int k = 0; k < width; k++) {
158: for (int l = 0; l < height; l++) {
159: r = getGreen(l, k);
160: g = getBlue(l, k);
161: a = getAlpha(l, k);
162: array[l][k] = RGBtoInt(r, g, I[l][k], a);
163: }
164: }
165: }
166:
167: private void setUniformGrey(int[][] I) {
168: width = I[0].length;
169: height = I.length;
170: array = new int[height][width];
171: for (int k = 0; k < width; k++) {
172: for (int l = 0; l < height; l++) {
173: array[l][k] = RGBtoInt(I[l][k], I[l][k], I[l][k], 255);
174: }
175: }
176: }
177:
178: /*************************************
179: * Part of the interface ImageObserver
180: **************************************/
181: public boolean imageUpdate(Image img1, int parm2, int parm3,
182: int parm4, int parm5, int parm6) {
183: if ((parm3 < 0) || (parm4 < 0)) {
184: throw new IllegalArgumentException("Could not load image.");
185: }
186: width = img1.getWidth(this );
187: height = img1.getHeight(this );
188: loaded = true;
189: return (false);
190: }
191:
192: public synchronized void waitForImage(Image img) {
193: while (loaded == false) {
194: try {
195: width = img.getWidth(this );
196: wait(100);
197: } catch (InterruptedException e) {
198: }
199: }
200: int[] p = new int[width * height];
201: PixelGrabber pg = new PixelGrabber(img, 0, 0, width, height, p,
202: 0, width);
203: try {
204: pg.grabPixels();
205: } catch (InterruptedException e) {
206: System.err.println("interrupted waiting for pixels!");
207: return;
208: }
209: if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
210: System.err.println("image fetch aborted or errored");
211: return;
212: }
213: computeArray(p);
214:
215: }
216:
217: public static int RGBtoInt(int r, int g, int b, int a) {
218: return ((a << 24) | (r << 16) | (g << 8) | b);
219: }
220:
221: public void invert() {
222: int r, g, b, a;
223: for (int y = 0; y < getWidth(); y++)
224: for (int x = 0; x < getHeight(); x++) {
225: r = getRed(x, y);
226: g = getGreen(x, y);
227: b = getBlue(x, y);
228: a = getAlpha(x, y);
229: setPixel(x, y, 255 - r, 255 - g, 255 - b, a);
230: }
231: }
232:
233: public void makeGrayFromRed() {
234: int r, a;
235: for (int y = 0; y < getWidth(); y++)
236: for (int x = 0; x < getHeight(); x++) {
237: r = getRed(x, y);
238: a = getAlpha(x, y);
239: setPixel(x, y, r, r, r, a);
240: }
241: }
242:
243: public void makeGrayFromGreen() {
244: int g, a;
245: for (int y = 0; y < getWidth(); y++)
246: for (int x = 0; x < getHeight(); x++) {
247: g = getGreen(x, y);
248: a = getAlpha(x, y);
249: setPixel(x, y, g, g, g, a);
250: }
251: }
252:
253: public void makeGrayFromBlue() {
254: int b, a;
255: for (int y = 0; y < getWidth(); y++)
256: for (int x = 0; x < getHeight(); x++) {
257: b = getBlue(x, y);
258: a = getAlpha(x, y);
259: setPixel(x, y, b, b, b, a);
260: }
261: }
262:
263: public void makeRed() {
264: int r, a;
265: for (int y = 0; y < getWidth(); y++)
266: for (int x = 0; x < getHeight(); x++) {
267: r = getRed(x, y);
268: a = getAlpha(x, y);
269: setPixel(x, y, r, 0, 0, a);
270: }
271: }
272:
273: public void makeGreen() {
274: int g, a;
275: for (int y = 0; y < getWidth(); y++)
276: for (int x = 0; x < getHeight(); x++) {
277: g = getGreen(x, y);
278: a = getAlpha(x, y);
279: setPixel(x, y, 0, g, 0, a);
280: }
281: }
282:
283: public void makeBlue() {
284: int b, a;
285: for (int y = 0; y < getWidth(); y++)
286: for (int x = 0; x < getHeight(); x++) {
287: b = getBlue(x, y);
288: a = getAlpha(x, y);
289: setPixel(x, y, 0, 0, b, a);
290: }
291: }
292:
293: public void setPixel(int x, int y, int r, int g, int b, int a) {
294: array[x][y] = RGBtoInt(r, g, b, a);
295: }
296:
297: /*********************************
298: * Allow to change the array
299: * representing the image
300: * @exception IllegalArgumentException if array doesn't make a matrix
301: **********************************/
302: public void setArray(int[][] s) {
303: array = new int[s.length][s[0].length];
304: width = s[0].length;
305: height = s.length;
306: for (int k = 0; k < width; k++) {
307: if (s[k].length != s[0].length) {
308: throw new IllegalArgumentException(
309: "Array doesn't make a matrix.");
310: }
311: for (int l = 0; l < height; l++) {
312: array[k][l] = s[k][l];
313: }
314: }
315: }
316:
317: public int getRed(int x, int y) {
318: return cm.getRed(array[x][y]);
319: }
320:
321: public int getGreen(int x, int y) {
322: return cm.getGreen(array[x][y]);
323: }
324:
325: public int getBlue(int x, int y) {
326: return cm.getBlue(array[x][y]);
327: }
328:
329: public int getAlpha(int x, int y) {
330: return cm.getAlpha(array[x][y]);
331: }
332:
333: public int[][] getRedArray() {
334: int[][] ans = new int[width][height];
335: for (int k = 0; k < width; k++) {
336: for (int l = 0; l < height; l++)
337: ans[k][l] = cm.getRed(array[l][k]);
338: }
339: return (ans);
340: }
341:
342: public int[][] getGreenArray() {
343: int[][] ans = new int[width][height];
344: for (int k = 0; k < width; k++) {
345: for (int l = 0; l < height; l++)
346: ans[k][l] = cm.getGreen(array[l][k]);
347: }
348: return (ans);
349: }
350:
351: public int[][] getBlueArray() {
352: int[][] ans = new int[width][height];
353: for (int k = 0; k < width; k++) {
354: for (int l = 0; l < height; l++)
355: ans[k][l] = cm.getBlue(array[l][k]);
356: }
357: return (ans);
358: }
359:
360: public int[][] getAlphaArray(int x, int y) {
361: int[][] ans = new int[width][height];
362: for (int k = 0; k < width; k++) {
363: for (int l = 0; l < height; l++)
364: ans[k][l] = cm.getAlpha(array[l][k]);
365: }
366: return (ans);
367: }
368:
369: public int[][] getArray(int x, int y) {
370: int[][] ans = new int[width][height];
371: for (int k = 0; k < width; k++) {
372: for (int l = 0; l < height; l++) {
373: ans[k][l] = array[k][l];
374: }
375: }
376: return (ans);
377: }
378:
379: /**********************************************
380: * Fast Wavelet Transform
381: * This method assumes a dyadic multiresolution.
382: * This implementation is temporary, expect it
383: * to be slow. It is meant to be easily
384: * understood.
385: * One good thing about this method is that
386: * it will handle the boundary automatically
387: * (as long as the chosen Multiresolution
388: * handles them).
389: * Also, it will work with any Multiresolution object.
390: * Only the red component is treated.
391: ***********************************************/
392: public PixelArray[][] redFWT(Multiresolution m) {
393: double[][] doublearray = ArrayCaster.toDouble(getRedArray());
394: return (FWT(m, doublearray));
395: }
396:
397: /**********************************************
398: * Fast Wavelet Transform
399: * This method assumes a dyadic multiresolution.
400: * This implementation is temporary, expect it
401: * to be slow. It is meant to be easily
402: * understood.
403: * One good thing about this method is that
404: * it will handle the boundary automatically
405: * (as long as the chosen Multiresolution
406: * handles them).
407: * Also, it will work with any Multiresolution object.
408: * Only the green component is treated.
409: ***********************************************/
410: public PixelArray[][] greenFWT(Multiresolution m) {
411: double[][] doublearray = ArrayCaster.toDouble(getGreenArray());
412: return (FWT(m, doublearray));
413: }
414:
415: /**********************************************
416: * Fast Wavelet Transform
417: * This method assumes a dyadic multiresolution.
418: * This implementation is temporary, expect it
419: * to be slow. It is meant to be easily
420: * understood.
421: * One good thing about this method is that
422: * it will handle the boundary automatically
423: * (as long as the chosen Multiresolution
424: * handles them).
425: * Also, it will work with any Multiresolution object.
426: * Only the blue component is treated.
427: ***********************************************/
428: public PixelArray[][] blueFWT(Multiresolution m) {
429: double[][] doublearray = ArrayCaster.toDouble(getBlueArray());
430: return (FWT(m, doublearray));
431: }
432:
433: private PixelArray[][] FWT(Multiresolution m, double[][] doublearray) {
434: final int pwidth = m.previousDimension(width);
435: final int wavewidth = width - pwidth;
436: final int pheight = m.previousDimension(height);
437: final int waveheight = height - pheight;
438: DoubleSparseVector[] wcachelow = new DoubleSparseVector[pwidth];
439: DoubleSparseVector[] hcachelow = new DoubleSparseVector[pheight];
440: DoubleSparseVector[] wcachehigh = new DoubleSparseVector[wavewidth];
441: DoubleSparseVector[] hcachehigh = new DoubleSparseVector[waveheight];
442: for (int k = 0; k < pwidth; k++) {
443: wcachelow[k] = new DoubleSparseVector(m.primaryScaling(
444: pwidth, k).evaluate(1));
445: }
446: for (int k = 0; k < pheight; k++) {
447: hcachelow[k] = new DoubleSparseVector(m.primaryScaling(
448: pheight, k).evaluate(1));
449: }
450: for (int k = 0; k < wavewidth; k++) {
451: wcachehigh[k] = new DoubleSparseVector(m.primaryWavelet(
452: pwidth, k).evaluate(0));
453: }
454: for (int k = 0; k < waveheight; k++) {
455: hcachehigh[k] = new DoubleSparseVector(m.primaryWavelet(
456: pheight, k).evaluate(0));
457: }
458:
459: DoubleSparseMatrix temp;
460: DoubleMatrix imgMat = new DoubleMatrix(doublearray);
461: double[][] lowpass = new double[pwidth][pheight];
462: for (int k = 0; k < pwidth; k++) {
463: for (int l = 0; l < pheight; l++) {
464: temp = wcachelow[k].tensorProduct(hcachelow[l]);
465: lowpass[k][l] = temp.scalarProduct(imgMat);
466: }
467: }
468: double[][] highlowpass = new double[wavewidth][pheight];
469: for (int k = 0; k < wavewidth; k++) {
470: for (int l = 0; l < pheight; l++) {
471: temp = wcachehigh[k].tensorProduct(hcachelow[l]);
472: highlowpass[k][l] = temp.scalarProduct(imgMat);
473: }
474: }
475: double[][] lowhighpass = new double[pwidth][waveheight];
476: for (int k = 0; k < pwidth; k++) {
477: for (int l = 0; l < waveheight; l++) {
478: temp = wcachelow[k].tensorProduct(hcachehigh[l]);
479: lowhighpass[k][l] = temp.scalarProduct(imgMat);
480: }
481: }
482: double[][] highhighpass = new double[wavewidth][waveheight];
483: for (int k = 0; k < wavewidth; k++) {
484: for (int l = 0; l < waveheight; l++) {
485: temp = wcachehigh[k].tensorProduct(hcachehigh[l]);
486: highhighpass[k][l] = temp.scalarProduct(imgMat);
487: }
488: }
489: PixelArray[][] ans = new PixelArray[2][2];
490: ans[0][0] = new PixelArray();
491: ans[0][0].setUniformGrey(toIntegerArray0_255(lowpass));
492: ans[1][0] = new PixelArray();
493: ans[1][0].setUniformGrey(toIntegerArray0_255(highlowpass));
494: ans[0][1] = new PixelArray();
495: ans[0][1].setUniformGrey(toIntegerArray0_255(lowhighpass));
496: ans[1][1] = new PixelArray();
497: ans[1][1].setUniformGrey(toIntegerArray0_255(highhighpass));
498: return (ans);
499: }
500:
501: private int[][] toIntegerArray0_255(double[][] v) {
502: double max = ArrayMath.max(v);
503: double min = ArrayMath.min(v);
504: if (max == min) {
505: max = min + Double.MIN_VALUE;
506: }
507: int[][] ans = new int[v.length][v[0].length];
508: for (int k = 0; k < v.length; k++) {
509: for (int l = 0; l < v[0].length; l++) {
510: ans[k][l] = (int) Math.round((v[k][l] - min)
511: / (max - min) * 255);
512: }
513: }
514: return (ans);
515: }
516:
517: /***********************************
518: * Get the image back
519: ************************************/
520: public Image rebuildImage() {
521: int[] p = new int[height * width];
522: for (int k = 0; k < width; k++) {
523: for (int l = 0; l < height; l++) {
524: p[l * width + k] = array[l][k];
525: }
526: }
527: MemoryImageSource source = new MemoryImageSource(width, height,
528: cm, p, 0, width);
529: return (Toolkit.getDefaultToolkit().createImage(source));
530: }
531:
532: public static Image buildImage(int[][] a) {
533: int h = a.length;
534: int w = a[0].length;
535: int[] p = new int[h * w];
536: for (int k = 0; k < w; k++) {
537: for (int l = 0; l < h; l++) {
538: p[l * w + k] = a[l][k];
539: }
540: }
541: MemoryImageSource source = new MemoryImageSource(w, h,
542: ColorModel.getRGBdefault(), p, 0, w);
543: return (Toolkit.getDefaultToolkit().createImage(source));
544: }
545: }
|