1
2
3
4 package de.desy.acop.video.analysis;
5
6
7 import java.awt.Rectangle;
8 import java.awt.image.BufferedImage;
9 import java.beans.PropertyChangeEvent;
10 import java.beans.PropertyChangeListener;
11 import java.beans.PropertyChangeSupport;
12 import java.util.concurrent.Executor;
13 import java.util.concurrent.Executors;
14 import java.util.concurrent.RejectedExecutionHandler;
15 import java.util.concurrent.ThreadFactory;
16 import java.util.concurrent.ThreadPoolExecutor;
17 import java.util.concurrent.TimeUnit;
18 import java.util.logging.Level;
19
20 import de.desy.acop.displayers.tools.DismissableBlockingQueue;
21 import de.desy.acop.displayers.tools.TINEThreadFactory;
22 import de.desy.acop.transport.ConnectionFailed;
23 import de.desy.acop.transport.ConnectionParameters;
24 import de.desy.acop.video.VideoServerConnection;
25 import de.desy.acop.video.displayer.ImageFormat;
26 import de.desy.acop.video.displayer.TineHandler;
27 import de.desy.acop.video.displayer.TineImageReceiver;
28 import de.desy.acop.video.displayer.timage.TImageConverter;
29 import de.desy.acop.video.timageio.TBufferedImage;
30 import de.desy.tine.types.IMAGE;
31 import de.desy.tine.types.IMAGE.FrameHeader;
32
33
34
35
36
37
38
39
40
41
42
43 @SuppressWarnings("deprecation")
44 public class ImageAnalysisEngine implements TineImageReceiver {
45
46 private class AnalysisExecutor implements Runnable {
47 final int iW;
48 final int iH;
49 final int rX;
50 final int rY;
51 final int rW;
52 final int rH;
53 final int r2X;
54 final int r2Y;
55 final int r2W;
56 final int r2H;
57 final double th;
58 final IMAGE im;
59 final double[] bg;
60 final boolean calculateThreshold;
61 final boolean performFit;
62 final boolean smoothing;
63 public AnalysisExecutor(IMAGE im, double[] bg, int iW, int iH, int rX, int rY, int rW, int rH,
64 int r2X, int r2Y, int r2W, int r2H, double threshold, boolean calculateThreshold, boolean performFit, boolean smoothing) {
65 this.iW = iW;
66 this.iH = iH;
67 this.rX = rX;
68 this.rY = rY;
69 this.rW = rW;
70 this.rH = rH;
71 this.th = threshold;
72 this.im = im;
73 this.bg = bg;
74 this.r2X = r2X;
75 this.r2Y = r2Y;
76 this.r2W = r2W;
77 this.r2H = r2H;
78 this.calculateThreshold = calculateThreshold;
79 this.performFit = performFit;
80 this.smoothing = smoothing;
81 }
82 public void run() {
83 analyzeImage(im, bg, iW, iH, rX, rY, rW, rH, r2X, r2Y, r2W, r2H, th, calculateThreshold, performFit,smoothing);
84 }
85 }
86
87 public static final String PROPERTY_A_IMAGE = "aImage";
88 public static final String PROPERTY_BACKGROUND = "background";
89 public static final String PROPERTY_PRECISE_BACKGROUND = "preciseBackground";
90 public static final String PROPERTY_RESET = "reset";
91
92 private PropertyChangeSupport pcSupport;
93 private Executor executor;
94 private ThreadPoolExecutor analysisExecutor;
95 private boolean localAnalysis = true;
96
97 private VideoServerConnection videoServerConnection;
98
99
100
101 private ColorDecoder decoder;
102
103 private AImage aImage;
104 private IMAGE image;
105 private IMAGE background;
106 private double[] preciseBackground;
107 private int imageWidth = -1;
108 private int imageHeight = -1;
109 private int backgroundWidth = -1;
110 private int backgroundHeight = -1;
111 private int roiX = -1;
112 private int roiY = -1;
113 private int roiW = -1;
114 private int roiH = -1;
115 private int roi2X = -1;
116 private int roi2Y = -1;
117 private int roi2W = -1;
118 private int roi2H = -1;
119 private double threshold = Double.NaN;
120 private boolean calculateThreshold = true;
121 private boolean performFit = false;
122 private boolean performSmoothing = false;
123
124
125
126 private double[] previousStartValuesX = null;
127 private double[] previousStartValuesY = null;
128
129
130
131
132
133 public ImageAnalysisEngine(ColorDecoder decoder) {
134 this.decoder = decoder;
135 }
136
137 @Override
138 public void resetForReceiving() {
139 getPcSupport().firePropertyChange(PROPERTY_RESET, false, true);
140 }
141
142 @Override
143 public synchronized void updateValue(IMAGE newImage) {
144 if (!localAnalysis) return;
145
146
147 image = newImage.clone();
148
149 FrameHeader hdr = image.getFrameHeader();
150 int w = hdr.aoiWidth == -1 ? hdr.sourceWidth : hdr.aoiWidth;
151 int h = hdr.aoiHeight == -1 ? hdr.sourceHeight : hdr.aoiHeight;
152
153 if (w != imageWidth || h != imageHeight) {
154 roiX = 0;
155 roiY = 0;
156 roiW = w;
157 roiH = h;
158 roi2X = 0;
159 roi2Y = 0;
160 roi2W = w;
161 roi2H = h;
162 }
163
164 imageWidth = w;
165 imageHeight = h;
166
167 if ((imageWidth != backgroundWidth || imageHeight != backgroundHeight) && background != null) {
168 setBackground(null);
169 }
170 else performAnalysis();
171 }
172
173
174
175
176
177
178
179
180
181
182 public synchronized void setPreciseBackground(double[] background, int width) {
183 TineHandler.logger.log(Level.INFO, "Background image set.");
184 setPreciseBackground(background,width, true);
185 }
186
187
188
189
190
191
192
193 public synchronized double[] getPreciseBackground() {
194 return preciseBackground;
195 }
196
197
198
199
200
201
202 public synchronized int getBackgroundWidth() {
203 return backgroundWidth;
204 }
205
206
207
208
209
210
211
212
213 private synchronized void setPreciseBackground(final double[] preciseBackground, final int width, boolean reanalyze) {
214 this.background = null;
215 this.preciseBackground = null;
216 backgroundWidth = -1;
217 backgroundHeight = -1;
218 if (preciseBackground != null) {
219 int w = width;
220 int h = preciseBackground.length/width;
221
222 if (w == imageWidth && h == imageHeight) {
223 backgroundWidth = w;
224 backgroundHeight = h;
225 this.preciseBackground = preciseBackground;
226 this.background = toImage(preciseBackground,width);
227 }
228 }
229
230 if (reanalyze) {
231 if (localAnalysis) performAnalysis();
232 else {
233 executeTask(new Runnable() {
234 @Override
235 public void run() {
236 try {
237 getVideoServerConnection().setBackground(background);
238 } catch (ConnectionFailed e) {
239 e.printStackTrace();
240 }
241 }
242 });
243 }
244 getPcSupport().firePropertyChange(PROPERTY_PRECISE_BACKGROUND, null, this.preciseBackground);
245 getPcSupport().firePropertyChange(PROPERTY_BACKGROUND, null, this.background);
246 }
247 }
248
249
250
251
252
253
254
255
256 public static IMAGE toImage(double[] image,int width) {
257 if (image == null || image.length == 0) return null;
258 if (width < 1 || image.length/width < 1) return null;
259 BufferedImage bi = toBufferedImage(image,width);
260
261 return TImageConverter.parseBufferedImage(bi);
262 }
263
264
265
266
267
268
269
270
271 public static BufferedImage toBufferedImage(double[] image,int width) {
272 if (image == null || image.length == 0) return null;
273 if (width < 1 || image.length/width < 1) return null;
274 int i = 0;
275 int length = image.length;
276 int[] rgb = new int[length];
277 try {
278 int alpha = (255 << 24);
279 int val;
280 for (;;) {
281 val = (int)(image[i]+0.5);
282 if (val < 0) val = 0;
283 rgb[i] = alpha + (val << 16) + (val << 8) + (val);
284 i++;
285 }
286 } catch (ArrayIndexOutOfBoundsException e) {
287
288 }
289 BufferedImage bi = new BufferedImage(width,length/width,BufferedImage.TYPE_BYTE_GRAY);
290
291 bi.setRGB(0,0,width,length/width,rgb,0,width);
292 return bi;
293 }
294
295
296
297
298
299 public synchronized IMAGE getBackground() {
300 return background;
301 }
302
303
304
305
306
307 public synchronized void setBackground(IMAGE background) {
308 TineHandler.logger.log(Level.INFO, "Background image set.");
309 setBackground(background, true);
310 this.background = background;
311 }
312
313 private synchronized void setBackground(final IMAGE background, boolean reanalyze) {
314 if (background == null) {
315 setPreciseBackground(null,-1);
316 } else {
317 int[] rgb = toBufferedImage(background).getRGB(0, 0, background.getFrameHeader().sourceWidth,
318 background.getFrameHeader().sourceHeight, null, 0, background.getFrameHeader().sourceWidth);
319 double[] data = getDecoder().transform(rgb);
320 setPreciseBackground(data,background.getFrameHeader().sourceWidth,reanalyze);
321 this.background = background;
322 }
323 }
324
325
326
327
328
329
330
331
332 public synchronized void setRoi(final int roiX, final int roiY, final int roiW, final int roiH) {
333 if (this.roiX == roiX && this.roiY == roiY && this.roiW == roiW && this.roiH == roiH) return;
334 TineHandler.logger.log(Level.INFO, "ROI: x="+roiX+", y="+roiY +", w="+roiW + ", h="+roiH);
335 setAnalysisParameters(threshold,roiX,roiY,roiW,roiH,roi2X,roi2Y,roi2W,roi2H,calculateThreshold,performFit,performSmoothing);
336 }
337
338
339
340
341
342
343
344
345 public synchronized void setRoi2(final int roi2X, final int roi2Y, final int roi2W, final int roi2H) {
346 if (this.roi2X == roi2X && this.roi2Y == roi2Y && this.roi2W == roi2W && this.roi2H == roi2H) return;
347 TineHandler.logger.log(Level.INFO, "Threshold ROI: x="+roi2X+", y="+roi2Y +", w="+roi2W + ", h="+roi2H);
348 setAnalysisParameters(threshold,roiX,roiY,roiW,roiH,roi2X,roi2Y,roi2W,roi2H,calculateThreshold,performFit,performSmoothing);
349 }
350
351
352
353
354
355 public synchronized IMAGE getImage() {
356 return image;
357 }
358
359
360
361
362
363 public synchronized AImage getAImage() {
364 return aImage;
365 }
366
367
368
369
370
371
372 public synchronized Rectangle getROI() {
373 return new Rectangle(roiX,roiY,roiW,roiH);
374 }
375
376
377
378
379
380
381
382 public synchronized Rectangle getThresholdROI() {
383 return new Rectangle(roi2X,roi2Y,roi2W,roi2H);
384 }
385
386
387
388
389
390
391
392
393 public synchronized double getThreshold() {
394 return threshold;
395 }
396
397
398
399
400
401
402
403
404
405
406
407
408
409 public synchronized void setThreshold(final double threshold) {
410 if (this.threshold == threshold) return;
411
412 TineHandler.logger.log(Level.INFO, "Threshold: " + threshold);
413 setAnalysisParameters(threshold,roiX,roiY,roiW,roiH,roi2X,roi2Y,roi2W,roi2H,calculateThreshold,performFit,performSmoothing);
414 }
415
416
417
418
419
420
421
422
423
424 public synchronized void setCalculateThreshold(final boolean calculateThreshold) {
425 if (this.calculateThreshold == calculateThreshold) return;
426 TineHandler.logger.log(Level.INFO, "Calculate threshold: " + calculateThreshold);
427 setAnalysisParameters(roiX,roiY,roiW,roiH,roi2X,roi2Y,roi2W,roi2H,calculateThreshold,performFit,performSmoothing);
428 }
429
430
431
432
433
434
435
436 public boolean isCalculateThreshold() {
437 return calculateThreshold;
438 }
439
440
441
442
443
444
445
446
447 public synchronized void setPerformFit(final boolean performFit) {
448 if (this.performFit == performFit) return;
449 TineHandler.logger.log(Level.INFO, "Perform fit: " + performFit);
450 setAnalysisParameters(roiX,roiY,roiW,roiH,roi2X,roi2Y,roi2W,roi2H,calculateThreshold,performFit,performSmoothing);
451 }
452
453
454
455
456
457
458
459 public synchronized void setPerformSmoothing(final boolean performSmoothing) {
460 if (this.performSmoothing == performSmoothing) return;
461 TineHandler.logger.log(Level.INFO, "Perform smoothing: " + performSmoothing);
462 setAnalysisParameters(roiX,roiY,roiW,roiH,roi2X,roi2Y,roi2W,roi2H,calculateThreshold,performFit,performSmoothing);
463 }
464
465
466
467
468
469
470
471 public boolean isPerformFit() {
472 return performFit;
473 }
474
475
476
477
478
479 public synchronized ColorDecoder getDecoder() {
480 return decoder;
481 }
482
483
484
485
486
487 public synchronized void setDecoder(ColorDecoder decoder) {
488 if (this.decoder == decoder) return;
489 this.decoder = decoder;
490 if (localAnalysis) performAnalysis();
491 else {
492
493 }
494 }
495
496
497
498
499
500
501
502
503
504
505
506 public void setAnalysisParameters(final int roiX, final int roiY, final int roiW, final int roiH,
507 final int roi2X, final int roi2Y, final int roi2W, final int roi2H,
508 final boolean calculateThreshold, boolean performFit, boolean performSmoothing) {
509 setAnalysisParameters(threshold, roiX, roiY, roiW, roiH, roi2X,
510 roi2Y, roi2W, roi2H, calculateThreshold,performFit, performSmoothing);
511 }
512
513
514
515
516
517
518
519
520
521 public synchronized void setAnalysisParameters(final double threshold,
522 final int roiX, final int roiY, final int roiW, final int roiH,
523 final int roi2X, final int roi2Y, final int roi2W, final int roi2H,
524 final boolean calculateThreshold, final boolean performFit,final boolean performSmoothing) {
525
526 if (!localAnalysis) {
527 executeTask(new Runnable() {
528 @Override
529 public void run() {
530 try {
531 getVideoServerConnection().setAnalysisParameters(threshold, roiX, roiY, roiW, roiH, roi2X, roi2Y, roi2W, roi2H,calculateThreshold,performFit);
532 } catch (ConnectionFailed e) {
533 e.printStackTrace();
534 }
535 }
536 });
537 } else {
538 this.calculateThreshold = calculateThreshold;
539 this.performFit = performFit;
540 this.performSmoothing = performSmoothing;
541 if (this.threshold == threshold && this.roiX == roiX && this.roiY == roiY && this.roiW == roiW && this.roiH == roiH
542 && this.roi2X == roi2X && this.roi2Y == roi2Y && this.roi2H == roi2H && this.roi2W == roi2W) return;
543 this.threshold = threshold;
544 if (roiX >= imageWidth) this.roiX = imageWidth-1;
545 else if (roiX < 0) this.roiX = 0;
546 else this.roiX = roiX;
547 if (roiY >= imageHeight) this.roiY = imageHeight-1;
548 else if (roiY < 0) this.roiY = 0;
549 else this.roiY = roiY;
550 if (this.roiX+roiW > imageWidth) this.roiW = imageWidth-this.roiX;
551 else if (roiW <= 0) this.roiW = 1;
552 else this.roiW = roiW;
553 if (this.roiY+roiH > imageHeight) this.roiH = imageHeight-this.roiY;
554 else if (roiH <= 0) this.roiH = 1;
555 else this.roiH = roiH;
556
557 if (roi2X >= imageWidth) this.roi2X = imageWidth-1;
558 else if (roi2X < 0) this.roi2X = 0;
559 else this.roi2X = roi2X;
560 if (roi2Y >= imageHeight) this.roi2Y = imageHeight-1;
561 else if (roi2Y < 0) this.roi2Y = 0;
562 else this.roi2Y = roi2Y;
563 if (this.roi2X+roi2W > imageWidth) this.roi2W = imageWidth-this.roi2X;
564 else if (roi2W <= 0) this.roi2W = 1;
565 else this.roi2W = roi2W;
566 if (this.roi2Y+roi2H > imageHeight) this.roi2H = imageHeight-this.roi2Y;
567 else if (roi2H <= 0) this.roi2H = 1;
568 else this.roi2H = roi2H;
569 performAnalysis();
570 }
571
572 }
573
574
575
576
577
578
579 public void setLocalAnalysisMode(boolean local) {
580 if (localAnalysis == local) return;
581 TineHandler.logger.log(Level.INFO, "Analysis mode: " + local);
582 localAnalysis = local;
583 if (localAnalysis) {
584 executeTask(new Runnable() {
585 @Override
586 public void run() {
587 getVideoServerConnection().disconnect();
588 }
589 });
590 setRoi(0, 0, imageWidth, imageHeight);
591 }
592 }
593
594
595
596
597
598
599 public boolean isLocalAnalysisMode() {
600 return localAnalysis;
601 }
602
603
604
605
606 public void startRemoteAnalysis() {
607 TineHandler.logger.log(Level.INFO, "Start remote analysis.");
608 try {
609 getVideoServerConnection().connect();
610 } catch (ConnectionFailed e) {
611 e.printStackTrace();
612 }
613 }
614
615
616
617
618 public void stopRemoteAnalysis() {
619 TineHandler.logger.log(Level.INFO, "Stop remote analysis.");
620 getVideoServerConnection().disconnect();
621 }
622
623
624
625
626
627 public void setAnalysisServerConnectionParameters(ConnectionParameters cp) {
628 getVideoServerConnection().setConnectionParameters(cp);
629 }
630
631 private VideoServerConnection getVideoServerConnection() {
632 if (videoServerConnection == null) {
633 videoServerConnection = new VideoServerConnection();
634 videoServerConnection.addPropertyChangeListener(new PropertyChangeListener() {
635 @Override
636 public void propertyChange(PropertyChangeEvent evt) {
637 if (localAnalysis) return;
638 if (evt.getPropertyName() == VideoServerConnection.PROPERTY_A_IMAGE) {
639 setAImage((AImage) evt.getNewValue());
640 }
641 else if (evt.getPropertyName() == VideoServerConnection.PROPERTY_BACKGROUND) {
642 setBackground((IMAGE) evt.getNewValue(), false);
643 }
644 else if (evt.getPropertyName() == VideoServerConnection.PROPERTY_PRECISE_BACKGROUND) {
645
646
647 }
648 }
649 });
650 }
651 return videoServerConnection;
652 }
653
654 private synchronized void setAImage(AImage aImage) {
655 this.aImage = aImage;
656 getPcSupport().firePropertyChange(PROPERTY_A_IMAGE, null, aImage);
657 }
658
659 private synchronized void performAnalysis() {
660 if (image == null) return;
661 executeAnalysis(new AnalysisExecutor(image,preciseBackground,imageWidth,imageHeight,roiX,roiY,roiW,roiH,
662 roi2X, roi2Y, roi2W, roi2H, threshold,calculateThreshold,performFit,performSmoothing));
663 }
664
665 private void executeAnalysis(Runnable r) {
666 if (analysisExecutor == null) {
667 ThreadFactory factory = new TINEThreadFactory("TINE-Dismissable");
668 analysisExecutor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.NANOSECONDS,
669 new DismissableBlockingQueue<Runnable>(2, true), factory){
670 @Override
671 protected void afterExecute(Runnable r, Throwable t) {
672 super.afterExecute(r, t);
673 if (t != null) {
674 t.printStackTrace();
675 }
676 }
677 };
678 analysisExecutor.setRejectedExecutionHandler(new RejectedExecutionHandler(){
679 public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
680
681 }
682 });
683 }
684
685 analysisExecutor.execute(r);
686 }
687
688 @Deprecated
689 @SuppressWarnings("unused")
690 private static int tineFormatToBufferedType(IMAGE im) {
691 int format = im.getFrameHeader().imageFormat;
692 if (format == ImageFormat.IMAGE_FORMAT_GRAY.getId()) {
693 return BufferedImage.TYPE_BYTE_GRAY;
694 } else if (format == ImageFormat.IMAGE_FORMAT_RGB.getId()) {
695 return BufferedImage.TYPE_INT_RGB;
696 }
697 return BufferedImage.TYPE_INT_RGB;
698 }
699
700 private void analyzeImage(final IMAGE im, double[] bg, int iW, int iH, int rX, int rY, int rW, int rH,
701 int r2X, int r2Y, int r2W, int r2H, double threshold, boolean calculateThreshold,
702 boolean performFit, boolean smoothing) {
703
704 IMAGE newImage = im;
705 BufferedImage bufferedImage = toBufferedImage(im);
706 int aH = im.getFrameHeader().aoiHeight;
707 int aW = im.getFrameHeader().aoiWidth;
708
709 if (aH != -1) {
710 iW = aW;
711 iH = aH;
712 }
713
714 if (bufferedImage == null) throw new IllegalStateException("No BufferedImage!");
715
716 int[] data = bufferedImage.getRGB(0, 0, iW, iH, null, 0, iW);
717 double[] values = decoder.transform(data);
718
719 int i, j;
720
721 if (bg != null) {
722 int[] newImageVals = new int[values.length];
723 try {
724 i = 0;
725
726 for (;;) {
727 values[i] -= bg[i];
728 newImageVals[i] = (int)(values[i]+0.5);
729 if (newImageVals[i] < 0) {
730 newImageVals[i] = 0;
731 }
732 i++;
733 }
734 } catch (ArrayIndexOutOfBoundsException e) {
735
736 }
737 bufferedImage = new BufferedImage(bufferedImage.getWidth(),bufferedImage.getHeight(),BufferedImage.TYPE_BYTE_GRAY);
738 bufferedImage.setRGB(0,0,iW,iH,newImageVals,0,iW);
739 newImage = TImageConverter.parseBufferedImage(bufferedImage,im.getFrameHeader().clone(),im.getSourceHeader().clone());
740 newImage.getFrameHeader().aoiWidth = im.getFrameHeader().aoiWidth;
741 newImage.getFrameHeader().aoiHeight = im.getFrameHeader().aoiHeight;
742 newImage.getFrameHeader().xStart = im.getFrameHeader().xStart;
743 newImage.getFrameHeader().yStart = im.getFrameHeader().yStart;
744 newImage.getFrameHeader().sourceHeight = im.getFrameHeader().sourceHeight;
745 newImage.getFrameHeader().sourceWidth = im.getFrameHeader().sourceWidth;
746 }
747
748
749
750
751
752
753
754
755 int thresholdPointsCount = 0;
756 if (calculateThreshold) {
757 double sum = 0;
758 int r = rX + rW, b = rY+rH;
759 boolean inside = false;
760 for(j = 0; j < r2H; j++) {
761 inside = rY <= r2Y + j && r2Y + j < b;
762 for(i = 0; i < r2W; i++) {
763
764 if (rX <= r2X + i && r2X + i < r && inside) continue;
765 sum += values[i+r2X+iW*(j+r2Y)];
766 thresholdPointsCount++;
767 }
768 }
769 if (thresholdPointsCount != 0) {
770 threshold = sum/thresholdPointsCount;
771 } else {
772 threshold = Double.NaN;
773 }
774 }
775
776 double[] sideViewX = new double [iW];
777 double[] sideViewY = new double [iH];
778
779 double maxValue = -Double.MAX_VALUE;
780 double minValue = Double.MAX_VALUE;
781 double value;
782
783
784
785 if (Double.isNaN(threshold)) {
786 for (i = 0; i < iW; ++i) {
787 for (j = 0; j < iH; ++j) {
788 value = values[i+j*iW];
789
790
791
792 if (minValue > value) minValue = value;
793 if (maxValue < value) maxValue = value;
794 sideViewX[i] += (value);
795 sideViewY[j] += (value);
796 }
797 }
798 } else {
799 for (i = 0; i < iW; ++i) {
800 for (j = 0; j < iH; ++j) {
801 value = values[i+j*iW];
802
803
804
805 if (minValue > value) minValue = value;
806 if (value < threshold) continue;
807 if (maxValue < value) maxValue = value;
808 sideViewX[i] += (value-threshold);
809 sideViewY[j] += (value-threshold);
810 }
811 }
812 }
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827 if (smoothing) {
828 sideViewX = FittingUtilities.smooth(sideViewX);
829 sideViewY = FittingUtilities.smooth(sideViewY);
830 }
831
832 double[] roiSideViewX = new double[rW];
833 double[] roiSideViewY = new double[rH];
834
835 for (i = 0; i < rW; ++i){
836 roiSideViewX[i]= sideViewX[rX+i];
837 }
838
839 for (j = 0; j < rH; ++j){
840 roiSideViewY[j]= sideViewY[rY+j];
841 }
842
843 try {
844 i = 0;
845 for (;;) {
846 if (values[i] < 0) values[i] = 0;
847 i++;
848 }
849 } catch (ArrayIndexOutOfBoundsException e) {
850
851 }
852
853 double sum = 0;
854 double sumX = 0;
855 double sumY = 0;
856 double sumXX = 0;
857 double sumYY = 0;
858 double sumXY = 0;
859
860 if (Double.isNaN(threshold)) {
861 for(j = 0; j < rH; j++)
862 {
863 for(i = 0; i < rW; i++)
864 {
865 value = values[i+rX+iW*(j+rY)];
866 sum = sum+value;
867 sumX = sumX+value*(i+rX);
868 sumY = sumY+value*(j+rY);
869 }
870 }
871 } else {
872 for(j = 0; j < rH; j++)
873 {
874 for(i = 0; i < rW; i++)
875 {
876 value = values[i+rX+iW*(j+rY)];
877 if(threshold < value)
878 {
879 value -=threshold;
880 sum = sum+value;
881 sumX = sumX+value*(i+rX);
882 sumY = sumY+value*(j+rY);
883 }
884 }
885 }
886 }
887
888 double Y = sumY/sum;
889 double X = sumX/sum;
890
891 double argx, argy;
892 if (Double.isNaN(threshold)) {
893 for(j = 0; j < rH; j++)
894 {
895 for(i = 0; i < rW; i++)
896 {
897 value = values[i+rX+iW*(j+rY)];
898 argx = i+rX-X;
899 argy = j+rY-Y;
900 sumXX = sumXX+value*argx*argx;
901 sumXY = sumXY+value*argx*argy;
902 sumYY = sumYY+value*argy*argy;
903 }
904 }
905 } else {
906 for(j = 0; j < rH; j++)
907 {
908 for(i = 0; i < rW; i++)
909 {
910 value = values[i+rX+iW*(j+rY)];
911 if(threshold < value)
912 {
913 value -=threshold;
914 argx = i+rX-X;
915 argy = j+rY-Y;
916 sumXX = sumXX+value*argx*argx;
917 sumXY = sumXY+value*argx*argy;
918 sumYY = sumYY+value*argy*argy;
919 }
920 }
921 }
922 }
923
924
925
926
927 double XX = sumXX/sum;
928 double XY = sumXY/sum;
929 double YY = sumYY/sum;
930
931 double b = 0;
932 double c = 0;
933 double D = 0;
934 double A = 0;
935 double B = 0;
936 b = -2*(XX+YY);
937 c = (XX*YY)-(XY*XY);
938 D = Math.sqrt((b*b)-(4*c));
939 A = (-b-D)/2;
940 B = (-b+D)/2;
941 double phase = Math.atan2(2*XX, YY-XX)*(90/Math.PI);
942
943 double[] xMinMax = findMinMax(roiSideViewX,0,rW);
944 double[] yMinMax = findMinMax(roiSideViewY,0,rH);
945
946
947 double xM = 0;
948 double yM = 0;
949 double suma = 0;
950 double xSTD = 0;
951 double ySTD = 0;
952 double constX = xMinMax[0];
953 double constY = yMinMax[0];
954 for (i = 0; i < rW; i++) {
955 value = roiSideViewX[i]-constX;
956 if (value < 0) value = 0;
957 xM += i*value;
958 xSTD += i*i*value;
959 suma += value;
960 }
961 xM = xM/suma;
962 xSTD = Math.sqrt(xSTD/suma-xM*xM);
963 suma = 0;
964 for (i = 0; i < rH; i++) {
965 value = roiSideViewY[i]-constY;
966 if (value < 0) value = 0;
967 yM += i*value;
968 ySTD += i*i*value;
969 suma += value;
970 }
971 yM = yM/suma;
972 ySTD = Math.sqrt(ySTD/suma-yM*yM);
973
974 double[] startValuesX = new double[]{xMinMax[1]-constX,xSTD,xM,constX,0};
975 double[] startValuesY = new double[]{yMinMax[1]-constY,ySTD,yM,constY,0};
976
977 if (performFit) {
978
979
980 int l = roiSideViewX.length;
981 double[] steps = new double[l];
982 double[] weights = new double[l];
983 for (i = 0; i < l; i++) {
984 steps[i] = i;
985 weights[i] = 1;
986 }
987
988 try {
989 startValuesX = FittingUtilities.lmLinear(steps,roiSideViewX,weights,startValuesX);
990
991 if ((startValuesX[0] > 7000000 || startValuesX[0] <= 1|| startValuesX[1] <= 1)) {
992 if (previousStartValuesX != null) {
993 startValuesX = previousStartValuesX;
994 i = 0;
995 do {
996 startValuesX = FittingUtilities.lmLinear(steps,roiSideViewX,weights,startValuesX);
997 i++;
998 } while ((startValuesX[0] > 7000000 || startValuesX[0] <= 1 || startValuesX[1] <= 1) && i < 3);
999 if ((startValuesX[0] > 7000000 || startValuesX[0] <= 1 || startValuesX[1] <= 1) ) {
1000 startValuesX = new double[]{Double.NaN,Double.NaN,Double.NaN,Double.NaN,Double.NaN};
1001 previousStartValuesX = null;
1002 } else {
1003 previousStartValuesX = startValuesX;
1004 }
1005 } else {
1006 startValuesX = new double[]{Double.NaN,Double.NaN,Double.NaN,Double.NaN,Double.NaN};
1007 previousStartValuesX = null;
1008 }
1009 } else {
1010
1011
1012 if (rW < iW) {
1013 double slope = startValuesX[4];
1014 startValuesX = new double[]{xMinMax[1]-constX,xSTD,rX+xM,constX,0};
1015 double[] roiSX = new double[iW];
1016 for (i = 0; i < iW; i++) {
1017 roiSX[i] = sideViewX[i] - slope*i;
1018 }
1019
1020 steps = new double[iW];
1021 weights = new double[iW];
1022 for (i = 0; i < iW; i++) {
1023 steps[i] = i;
1024 weights[i] = 1;
1025 }
1026 startValuesX = FittingUtilities.lmLinear(steps,roiSX,weights,startValuesX);
1027 if ((startValuesX[0] > 7000000 || startValuesX[0] <= 1|| startValuesX[1] <= 1) && previousStartValuesX != null) {
1028 startValuesX = previousStartValuesX;
1029 do {
1030 startValuesX = FittingUtilities.lmLinear(steps,roiSX,weights,startValuesX);
1031 i++;
1032 } while ((startValuesX[0] > 7000000 || startValuesX[0] <= 1|| startValuesX[1] <= 1) && i < 3);
1033 }
1034 startValuesX[4] += slope;
1035 if ((startValuesX[0] > 7000000 || startValuesX[0] <= 1|| startValuesX[1] <= 1)) {
1036 startValuesX = new double[]{Double.NaN,Double.NaN,Double.NaN,Double.NaN,Double.NaN};
1037 previousStartValuesX = null;
1038 } else {
1039 previousStartValuesX = startValuesX;
1040 }
1041 } else {
1042 previousStartValuesX = startValuesX;
1043 }
1044 }
1045 } catch (Exception e) {
1046 startValuesX = new double[]{Double.NaN,Double.NaN,Double.NaN,Double.NaN,Double.NaN};
1047 previousStartValuesX = null;
1048 }
1049
1050 try {
1051 l = roiSideViewY.length;
1052 steps = new double[l];
1053 weights = new double[l];
1054 for (i = 0; i < l; i++) {
1055 steps[i] = i;
1056 weights[i] = 1;
1057 }
1058
1059 startValuesY = FittingUtilities.lmLinear(steps,roiSideViewY,weights,startValuesY);
1060
1061 if ((startValuesY[0] > 7000000 || startValuesY[0] <= 1 || startValuesY[1] <= 1) ) {
1062 if (previousStartValuesY != null) {
1063 startValuesY = previousStartValuesY;
1064 i = 0;
1065 do {
1066 startValuesY = FittingUtilities.lmLinear(steps,roiSideViewY,weights,startValuesY);
1067 i++;
1068 } while ((startValuesY[0] > 7000000 || startValuesY[0] <= 1 || startValuesY[1] <= 1) && i < 3);
1069 if ((startValuesY[0] > 7000000 || startValuesY[0] <= 1 || startValuesY[1] <= 1) ) {
1070 startValuesY = new double[]{Double.NaN,Double.NaN,Double.NaN,Double.NaN,Double.NaN};
1071 previousStartValuesY = null;
1072 } else {
1073 previousStartValuesY = startValuesY;
1074 }
1075 } else {
1076 startValuesY = new double[]{Double.NaN,Double.NaN,Double.NaN,Double.NaN,Double.NaN};
1077 previousStartValuesY = null;
1078 }
1079 } else {
1080
1081
1082 if (rH < iH) {
1083 double slope = startValuesY[4];
1084 startValuesY = new double[]{yMinMax[1]-constY,ySTD,rY+yM,constY,0};
1085 double[] roiSY = new double[iH];
1086 for (i = 0; i < iH; i++) {
1087 roiSY[i] = sideViewY[i] - slope*i;
1088 }
1089 steps = new double[iH];
1090 weights = new double[iH];
1091 for (i = 0; i < iH; i++) {
1092 steps[i] = i;
1093 weights[i] = 1;
1094 }
1095 i = 0;
1096 startValuesY = FittingUtilities.lmLinear(steps,roiSY,weights,startValuesY);
1097 if ((startValuesY[0] > 7000000 || startValuesY[0] <= 1 || startValuesY[1] <= 1) && previousStartValuesY != null) {
1098 startValuesY = previousStartValuesY;
1099 do {
1100 startValuesY = FittingUtilities.lmLinear(steps,roiSY,weights,startValuesY);
1101 i++;
1102 } while ((startValuesY[0] > 7000000 || startValuesY[0] <= 1 || startValuesY[1] <= 1) && i < 3);
1103 }
1104
1105 startValuesY[4] += slope;
1106 if ((startValuesY[0] > 7000000 || startValuesY[0] <= 1 || startValuesY[1] <= 1) ) {
1107 startValuesY = new double[]{Double.NaN,Double.NaN,Double.NaN,Double.NaN,Double.NaN};
1108 previousStartValuesY = null;
1109 } else {
1110 previousStartValuesY = startValuesY;
1111 }
1112 } else {
1113 previousStartValuesY = startValuesY;
1114 }
1115 }
1116 } catch (Exception e) {
1117 startValuesY = new double[]{Double.NaN,Double.NaN,Double.NaN,Double.NaN,Double.NaN};
1118 previousStartValuesY = null;
1119 }
1120
1121 if (FittingUtilities.isNaN(startValuesY)) {
1122 startValuesY = new double[]{yMinMax[1]-constY,ySTD,rY+yM,constY,0};
1123 }
1124 if (FittingUtilities.isNaN(startValuesX)) {
1125 startValuesX = new double[]{xMinMax[1]-constX,ySTD,rX+xM,constX,0};
1126 }
1127 } else {
1128 startValuesX = new double[]{Double.NaN,Double.NaN,Double.NaN,Double.NaN,Double.NaN};
1129 startValuesY = new double[]{Double.NaN,Double.NaN,Double.NaN,Double.NaN,Double.NaN};
1130 previousStartValuesY = null;
1131 previousStartValuesX = null;
1132 }
1133
1134
1135 try {
1136 if (!Double.isNaN(threshold)){
1137 maxValue = maxValue - threshold;
1138 }
1139 AImage newAImage = new AImage(
1140 newImage, rX, rY, rW, rH, r2X, r2Y, r2W, r2H, threshold,
1141 sideViewX,rX+xM,xSTD,xMinMax[1]-constX,constX,
1142 startValuesX[2], startValuesX[1], startValuesX[0], startValuesX[3], startValuesX[4],
1143 sideViewY,rY+yM,ySTD,yMinMax[1]-constY,constY,
1144 startValuesY[2], startValuesY[1], startValuesY[0], startValuesY[3], startValuesY[4],
1145
1146 Math.sqrt(A), Math.sqrt(B), phase, maxValue, minValue,
1147 thresholdPointsCount,calculateThreshold,performFit,smoothing
1148 );
1149 setAImage(newAImage);
1150
1151 } catch (Exception e) {
1152 e.printStackTrace();
1153 }
1154
1155 }
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451 private double[] findMinMax(double[] values,int start, int length) {
1452 double min = Double.MAX_VALUE;
1453 double max = -Double.MAX_VALUE;
1454 for (int i = start; i < start+length; i++) {
1455 if (values[i] > max) max = values[i];
1456 if (values[i] < min) min = values[i];
1457 }
1458 return new double[] {min, max};
1459 }
1460
1461 private void executeTask(Runnable r) {
1462 if (executor == null) {
1463 executor = Executors.newSingleThreadExecutor();
1464 }
1465 executor.execute(r);
1466 }
1467
1468 private PropertyChangeSupport getPcSupport() {
1469 if (pcSupport == null) {
1470 pcSupport = new PropertyChangeSupport(this);
1471 }
1472 return pcSupport;
1473 }
1474
1475
1476
1477
1478
1479
1480 public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
1481 getPcSupport().addPropertyChangeListener(propertyName, listener);
1482 }
1483
1484
1485
1486
1487
1488
1489 public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
1490 getPcSupport().removePropertyChangeListener(propertyName, listener);
1491 }
1492
1493
1494
1495
1496
1497 public void addPropertyChangeListener(PropertyChangeListener listener) {
1498 getPcSupport().addPropertyChangeListener(listener);
1499 }
1500
1501
1502
1503
1504
1505 public void removePropertyChangeListener(PropertyChangeListener listener) {
1506 getPcSupport().removePropertyChangeListener(listener);
1507 }
1508
1509
1510
1511
1512
1513
1514
1515
1516 public static BufferedImage toBufferedImage(IMAGE image) {
1517 if (TBufferedImage.isSupported(image.getFrameHeader().imageFormat)) {
1518 return TBufferedImage.toBufferedImage(image,null,false,false);
1519 } else {
1520 return new TImageConverter().parseTineImage(image,false);
1521 }
1522 }
1523 }
1524