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 }