View Javadoc

1   package de.desy.acop.video.analysis;
2   
3   /**
4    * 
5    * <code>AnalyzedImage</code> is a wrapper for the image, which provides
6    * statistical parameters of the supplied image. This class accepts an intensity
7    * image (given at construction) and performs statistical analysis. The
8    * calculated values can be obtained through the getter methods for each of the
9    * specific calculated parameters.
10   * 
11   * @author <a href="mailto:jaka.bobnar@cosylab.com">Jaka Bobnar</a>
12   * @deprecated not used anymore; relevant things moved to {@link ImageAnalysisEngine}.
13   */
14  @Deprecated
15  public class AnalyzedImage {
16  	
17  	private final ColorDecoder decoder;
18  	private final int[] image;
19  	private final int scan;
20  	private final int roiX;
21  	private final int roiY;
22  	private final int roiW;
23  	private final int roiH;
24  	private final double threshold;
25  
26  	private double[] sideViewX;
27  	private double[] sideViewY;
28  	private double meanX = Double.NaN;
29  	private double meanY = Double.NaN;
30  	private double stdX = Double.NaN;
31  	private double stdY = Double.NaN;
32  	private double sideViewAmplitudeX = Double.NaN;
33  	private double sideViewAmplitudeY = Double.NaN;
34  	private double sideViewConstX = Double.NaN;
35  	private double sideViewConstY = Double.NaN;
36  	private double stdA = Double.NaN;
37  	private double stdB = Double.NaN;
38  	private double angle2D = Double.NaN;
39  	private double amplitude2D = Double.NaN;
40  	private double const2D = Double.NaN;
41  	
42  
43  	/**
44  	 * Constructs a new AnalyzedImage, which accepts an image matrix. The
45  	 * provided image describes the intensity of pixels in a 2D image. This
46  	 * image will be analyzed for statistical parameters and will not be changed
47  	 * in any way during the analysis. Only the region given by the additional
48  	 * parameters will be analyzed (region of interest is always a rectangle).
49  	 * 
50  	 * @param decoder 
51  	 *            the </code>ColorDecoder</code> to be used for transforming 
52  	 *            the </code>TYPE_INT_ARGB</code> integer color code into a
53  	 *            meaningful double value
54  	 * @param image
55  	 *            the image represented by an array of intensity values.
56  	 *            Consider this array to be an intensity image matrix combined
57  	 *            into a single array one row after another
58  	 * @param scan
59  	 *            the length of a single row in the image matrix
60  	 * @param x
61  	 *            region of interest left coordinate
62  	 * @param y
63  	 *            region of interest upper coordinate
64  	 * @param w
65  	 *            the width of the region of interest
66  	 * @param h
67  	 *            the height of the region of interest
68  	 * @param threshold
69  	 *            the threshold below which all values are neglected
70  	 * @throws Exception 
71  	 */
72  	public AnalyzedImage(ColorDecoder decoder, int[] image, int scan, int x, int y, int w, int h, double threshold)
73  			throws Exception {
74  		if (decoder == null) {
75  			decoder = new IdentityColorDecoder();
76  		}
77  		if (image == null) {
78  			throw new IllegalArgumentException("Image cannot be null.");
79  		}
80  		if (image.length % scan != 0) {
81  			throw new IllegalArgumentException("The scan parameter '" + scan
82  					+ "' does not match image length '" + image.length + "'.");
83  		}
84  		int height = image.length / scan;
85  		if (x < 0 || x >= scan || x + w > scan || x + w < 0 || w < 0 || y < 0
86  				|| y >= height || y + h > height || y + h < 0 || h < 0) {
87  			throw new IllegalArgumentException("Region of interest is invalid.");
88  		}
89  		
90  		this.decoder = decoder;
91  		this.image = image;
92  		this.scan = scan;
93  		this.roiX = x;
94  		this.roiY = y;
95  		this.roiW = w;
96  		this.roiH = h;
97  		this.threshold = threshold;
98  
99  		analyzeImage();
100 	}
101 
102 	private double getValue(int x, int y) {
103 		// int position = y*scan + x;
104 		return decoder.transform(image[y * scan + x]);
105 	}
106 
107 	private void analyzeImage() throws Exception {
108 //		sideViewAnalysis();
109 //		gauss2DFit();
110 		
111 		sideViewX = new double [scan];
112 		sideViewY = new double [image.length/scan];
113 		int imageHeight = image.length/scan;
114 		
115 		double maxValue = -Double.MAX_VALUE;
116 		double minValue = Double.MAX_VALUE;
117 		double value;
118 		for (int i = 0; i < scan; ++i) {
119 			for (int j = 0; j < imageHeight; ++j) {
120 				value = getValue(i, j);
121 				if (value < threshold) continue;
122 				if (maxValue < value) maxValue = value;
123 				if (minValue > value) minValue = value;
124 				sideViewX[i] += value;
125 				sideViewY[j] += value;
126 			}
127 		}
128 		
129 		double[] roiSideViewX = new double[roiW];
130 		double[] roiSideViewY = new double[roiH];
131 		
132 		for (int i = 0; i < roiW; ++i){
133 			roiSideViewX [i]= sideViewX [i+roiX];
134 		}
135 		
136 		for (int j = 0; j < roiH; ++j){
137 			roiSideViewY [j]= sideViewY [j+roiY];
138 		}
139 		
140 		int y = 0 , x = 0;
141 		
142 		double sum = 0;
143 		double sumX = 0;
144 		double sumY = 0;
145 		double sumXX = 0;
146 		double sumYY = 0;
147 		double sumXY = 0;
148 		
149 		for(y = 0; y < roiH; y++)
150 		{
151 			for(x = 0; x < roiW; x++)
152 			{
153 				value = getValue(x+roiX, y+roiY);
154 				if(threshold < value)
155 				{
156 					sum = sum+value;
157 					sumX = sumX+value*(x+roiX);
158 					sumY = sumY+value*(y+roiY);
159 				}
160 			}
161 		}
162 		
163 		double Y = sumY/sum;
164 		double X = sumX/sum;
165 		
166 		double argx, argy;
167 		for(y = 0; y < roiH; y++)
168 		{
169 			for(x = 0; x < roiW; x++)
170 			{
171 				value = getValue(x+roiX, y+roiY);
172 				if(threshold < value)
173 				{
174 					argx = x+roiX-X;
175 					argy = y+roiY-Y;
176 					sumXX = sumXX+value*argx*argx;
177 					sumXY = sumXY+value*argx*argy;
178 					sumYY = sumYY+value*argy*argy;
179 				}
180 			}
181 		}
182 
183 		double XX = sumXX/sum;
184 		double XY = sumXY/sum;
185 		double YY = sumYY/sum;
186 
187 		double b = 0;
188 		double c = 0;
189 		double D = 0;
190 		double A = 0;
191 		double B = 0;
192 		b = -2*(XX+YY);
193 		c = (XX*YY)-(XY*XY);
194 		D = Math.sqrt((b*b)-(4*c));
195 		A = (-b-D)/2;
196 		B = (-b+D)/2;
197 		double phase = Math.atan2(2*XX, YY-XX)*(90/Math.PI);
198 		
199 		double[] xMinMax = findMinMax(sideViewX);
200 		double[] yMinMax = findMinMax(sideViewY);
201 		
202 		meanX = X;
203 		stdX = Math.sqrt(XX);
204 		sideViewAmplitudeX = xMinMax[1]-xMinMax[0];
205 		sideViewConstX = xMinMax[0];
206 		
207 		meanY = Y;
208 		stdY = Math.sqrt(YY);
209 		sideViewAmplitudeY = yMinMax[1]-yMinMax[0];
210 		sideViewConstY = yMinMax[0];
211 		
212 		stdA = Math.sqrt(A);
213 		stdB = Math.sqrt(B);
214 		angle2D = phase;
215 		amplitude2D = maxValue-minValue;
216 		const2D = minValue;
217 		
218 //		System.out.println("---RESULTS---");
219 //		System.out.println("mean X = "+meanX);
220 //		System.out.println("std X = "+stdX);
221 //		System.out.println("amplitude X = "+sideViewAmplitudeX);
222 //		System.out.println("const X = "+sideViewConstX);
223 //		System.out.println("---");
224 //		System.out.println("mean Y = "+meanY);
225 //		System.out.println("std Y = "+stdY);
226 //		System.out.println("amplitude Y = "+sideViewAmplitudeY);
227 //		System.out.println("const Y = "+sideViewConstY);
228 //		System.out.println("---");
229 //		System.out.println("std A = "+stdA);
230 //		System.out.println("std B = "+stdB);
231 //		System.out.println("theta = "+angle2D);
232 //		System.out.println("amplitude = "+amplitude2D);
233 //		System.out.println("const = "+const2D);
234 //		System.out.println("---");
235 		
236 	}
237 	
238 	private double[] findMinMax(double[] values) {
239 		double min = Double.MAX_VALUE;
240 		double max = -Double.MAX_VALUE;
241 		for (int i = 0; i < values.length; i++) {
242 			if (values[i] > max) max = values[i];
243 			if (values[i] < min) min = values[i];
244 		}
245 		return new double[] {min, max};
246 	}
247 
248 //	private void gauss2DFit() throws MathException {
249 //		double [] roi = new double [roiW*roiH]; 
250 //		int width = roiX + roiW;
251 //		int height = roiY + roiH;
252 //		
253 //		
254 //		for (int i = roiY; i < height; ++i) {
255 //			for (int j = roiX; j < width; ++j) {
256 //				roi[j - roiX + ((i-roiY) * roiW)] = getValue(i, j);
257 //			}
258 //		}
259 //		
260 //		double[][] x = new double [roi.length][2];
261 //		double[] a2D = new double[] {1,1,1,1,1,1,1};
262 //		LMfunc f = new LM2DGauss();
263 //		
264 //		for (int i =0; i < roiH; i++){
265 //			for (int j = 0; j < roiW; j++) {
266 //				x[j][0]=j;
267 //				x[j][1]=i;
268 //			}
269 //		}
270 //		
271 //		LM.solve(x, a2D, roi, f, TERM_EPSILON, MAX_ITER);
272 //		amplitude2D = a2D[0];
273 //		gauss2DFitXMean = a2D[1];
274 //		gauss2DFitYMean = a2D[2];
275 //		a = a2D[3];
276 //		b = a2D[4];
277 //		c = a2D[5];
278 //		const2D = a2D[6];
279 //		const2D = 0;
280 //		
281 //		double[]sigma = NonLinearSystem.calculate(a, b, c, stdX, stdY, 0 );
282 //		
283 //		gauss2DFitXMean = Math.sqrt(sigma[0]);
284 //		gauss2DFitYMean = Math.sqrt(sigma[1]);
285 //		angle2D = sigma[2];
286 //
287 //	}
288 //
289 //	private void sideViewAnalysis() throws Exception {
290 //		
291 //		sideViewX = new double [scan];
292 //		sideViewY = new double [image.length/scan];
293 //		int imageHeight = image.length/scan;
294 //		
295 //		for (int i = 0; i < scan; ++i) {
296 //			for (int j = 0; j < imageHeight; ++j) {
297 //				sideViewX[i] += getValue(i, j);
298 //			}
299 //		}
300 //
301 //		for (int i = 0; i < imageHeight; ++i) {
302 //			for (int j = 0; j < scan; ++j) {
303 //				sideViewY[i] += getValue(j, i);
304 //			}
305 //		}
306 //		
307 //		double[] roiSideViewX = new double[roiW];
308 //		double[] roiSideViewY = new double[roiH];
309 //		
310 //		for (int i = 0; i < roiW; ++i){
311 //			roiSideViewX [i]= sideViewX [i+roiX];
312 //		}
313 //		
314 //		for (int j = 0; j < roiH; ++j){
315 //			roiSideViewY [j]= sideViewY [j+roiY];
316 //		}
317 //		
318 //		/*
319 //		int width = roiX + roiW;
320 //		int height = roiY + roiH;
321 //
322 //		for (int i = roiX; i < width; ++i) {
323 //			for (int j = roiY; j < height; ++j) {
324 //				roiSideViewX[i - roiX] += getValue(i, j);
325 //			}
326 //		}
327 //
328 //		for (int i = roiY; i < height; ++i) {
329 //			for (int j = roiX; j < width; ++j) {
330 //				roiSideViewY[i - roiY] += getValue(j, i);
331 //			}
332 //		}
333 //		*/
334 //		
335 //		double[][]x = new double [roiW][1];
336 //		
337 //		for (int i = 0; i < roiW; i++){
338 //			x[i][0]=i;
339 //		}
340 //		
341 //		double[] aX = new double[] {1,1,1,1};
342 //		double[] aY = new double[] {1,1,1,1};
343 //		LMfunc f = new LMGauss();
344 //		
345 //		LM.solve(x, aX, roiSideViewX, f, TERM_EPSILON, MAX_ITER);
346 //		sideViewAmplitudeX = aX[0];
347 //		meanX = aX[1];
348 //		stdX = aX[2];
349 //		sideViewConstX = aX[3];
350 //		
351 //		double[][]y = new double [roiH][1];
352 //		
353 //		for (int i = 0; i < roiH; i++){
354 //			y[i][0]=i;
355 //		}
356 //		
357 //		LM.solve(y, aY, roiSideViewY, f, TERM_EPSILON, MAX_ITER);
358 //		sideViewAmplitudeY = aY[0];
359 //		meanY = aY[1];
360 //		stdY = aY[2];
361 //		sideViewConstY = aY[3];
362 //
363 //	}
364 
365 	/**
366 	 * Returns the intensity image, which was analyzed by this class. The
367 	 * statistical parameters of this AnalyzedImage match the image returned by
368 	 * this method. This is a 2D intensity matrix concatenated row-by-row into a
369 	 * 1-dimensional array.
370 	 * 
371 	 * @return the original image
372 	 */
373 	public int[] getImage() {
374 		return image;
375 	}
376 	
377 	/**
378 	 * Returns the width of the row in the image matrix (the matrix is combined
379 	 * into a single array returned by {@link #getImage()});
380 	 * 
381 	 * @return
382 	 */
383 	public int getScan() {
384 		return scan;
385 	}
386 
387 	/**
388 	 * The height of the region of interest.
389 	 * 
390 	 * @return the height
391 	 */
392 	public int getROIH() {
393 		return roiH;
394 	}
395 
396 	/**
397 	 * The width of the region of interest.
398 	 * 
399 	 * @return the width
400 	 */
401 	public int getROIW() {
402 		return roiW;
403 	}
404 
405 	/**
406 	 * Left coordinate of the region of interest.
407 	 * 
408 	 * @return the left coordinate
409 	 */
410 	public int getROIX() {
411 		return roiX;
412 	}
413 
414 	/**
415 	 * Upper coordinate of the region of interest.
416 	 * 
417 	 * @return the upper coordinate
418 	 */
419 	public int getROIY() {
420 		return roiY;
421 	}
422 
423 	/**
424 	 * Returns the standard deviation of the 2-dimensional intensity image in the
425 	 * direction of the "a" axis of the profile (assuming an elliptical profile).
426 	 * 
427 	 * @return the standard deviation in a direction
428 	 * 
429 	 * @see {@link #getImage()}
430 	 */
431 	public double getStdA() {
432 		return stdA;
433 	}
434 
435 	/**
436 	 * Returns the standard deviation of the 2-dimensional intensity image in the
437 	 * direction of the "b" axis of the profile (assuming an elliptical profile).
438 	 *  
439 	 * @return the standard deviation in b direction
440 	 * 
441 	 * @see {@link #getImage()}
442 	 */
443 	public double getStdB() {
444 		return stdB;
445 	}
446 
447 	/**
448 	 * Returns the angle of rotation of the 2-dimensional intensity profile around
449 	 * its center (the angle between x and a and y and b axes).
450 	 * 
451 	 * @return the angle of rotation of the 2-dimensional intensity profile
452 	 * 
453 	 * @see {@link #getImage()}
454 	 */
455 	public double getAngle2D() {
456 		return angle2D;
457 	}
458 	
459 	/**
460 	 * Returns the minimum pixel value (as returned by </code>ColorDecoder</code> 
461 	 * assigned to this </code>AnalyzedImage</code>) across the entire image. This
462 	 * value represents the background of the image.
463 	 * 
464 	 * @return the constant value of the intensity profile
465 	 * 
466 	 * @see {@link #getImage()}
467 	 */
468 	public double getConst2D() {
469 		return const2D;
470 	}
471 
472 	/**
473 	 * Returns the maximum pixel value (as returned by </code>ColorDecoder</code> 
474 	 * assigned to this </code>AnalyzedImage</code>) across the entire image.
475 	 * 
476 	 * @return the amplitude
477 	 * 
478 	 * @see {@link #getImage()}
479 	 */
480 	public double getAmplitude2D() {
481 		return amplitude2D;
482 	}
483 
484 	/**
485 	 * Returns the array representing the side projection of the intensity image
486 	 * on the X axis.
487 	 * 
488 	 * @return the projection on the x axis
489 	 * 
490 	 * @see {@link #getImage()}
491 	 */
492 	public double[] getSideViewX() {
493 		return sideViewX;
494 	}
495 
496 	/**
497 	 * Returns the array representing the side projection of the intensity image
498 	 * on the Y axis.
499 	 * 
500 	 * @return the projection on the y axis
501 	 * 
502 	 * @see {@link #getImage()}
503 	 */
504 	public double[] getSideViewY() {
505 		return sideViewY;
506 	}
507 
508 	/**
509 	 * Returns the standard deviation of the side view projection
510 	 * of the image on the x axis. This is calculated from the values
511 	 * returned by the {@link #getSideViewX()}.
512 	 * 
513 	 * @return the standard deviation
514 	 */
515 	public double getStdX() {
516 		return stdX;
517 	}
518 
519 	/**
520 	 * Returns the standard deviation of the side view projection
521 	 * of the image on the y axis. This is calculated from the values
522 	 * returned by the {@link #getSideViewY()}.
523 	 * 
524 	 * @return the standard deviation
525 	 */
526 	public double getStdY() {
527 		return stdY;
528 	}
529 
530 	/**
531 	 * Returns the mean value of the side view projection of the
532 	 * image on the x axis. This is calculated from the values
533 	 * returned by the {@link #getSideViewX()}.
534 	 * 
535 	 * @return the mean value
536 	 */
537 	public double getMeanX() {
538 		return meanX;
539 	}
540 
541 	/**
542 	 * Returns the mean value of the side view projection of the
543 	 * image on the y axis. This is calculated from the values
544 	 * returned by the {@link #getSideViewY()}.
545 	 * 
546 	 * @return the mean value
547 	 */
548 	public double getMeanY() {
549 		return meanY;
550 	}
551 
552 	/**
553 	 * Returns the amplitude (maximum minus minimum) value of the side view 
554 	 * projection of the image on the x axis. This is calculated from the values
555 	 * returned by the {@link #getSideViewX()}.
556 	 * 
557 	 * @return the amplitude
558 	 */
559 	public double getSideViewAmplitudeX() {
560 		return sideViewAmplitudeX;
561 	}
562 
563 	/**
564 	 * Returns the amplitude (maximum minus minimum) value of the side view 
565 	 * projection of the image on the y axis. This is calculated from the values
566 	 * returned by the {@link #getSideViewY()}.
567 	 * 
568 	 * @return the amplitude
569 	 */
570 	public double getSideViewAmplitudeY() {
571 		return sideViewAmplitudeY;
572 	}
573 	
574 	/**
575 	 * Returns the constant (minimum) value of the side view projection of
576 	 * the image on the x axis. This is calculated from the values
577 	 * returned by the {@link #getSideViewX()}.
578 	 * 
579 	 * @return the constant
580 	 */
581 	public double getSideViewConstX() {
582 		return sideViewConstX;
583 	}
584 
585 	/**
586 	 * Returns the constant (minimum) value of the side view projection of
587 	 * the image on the y axis. This is calculated from the values
588 	 * returned by the {@link #getSideViewY()}.
589 	 * 
590 	 * @return the constant
591 	 */
592 	public double getSideViewConstY() {
593 		return sideViewConstY;
594 	}
595 }