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 }