1 package com.github.celldynamics.quimp.plugin.protanalysis;
2
3 import java.awt.Color;
4 import java.awt.Point;
5 import java.awt.Polygon;
6 import java.util.ArrayList;
7 import java.util.Arrays;
8 import java.util.Collections;
9 import java.util.Comparator;
10 import java.util.Iterator;
11 import java.util.List;
12
13 import org.apache.commons.lang3.tuple.Pair;
14 import org.slf4j.Logger;
15 import org.slf4j.LoggerFactory;
16
17 import com.github.celldynamics.quimp.plugin.protanalysis.ProtAnalysisOptions.OutlinesToImage;
18 import com.github.celldynamics.quimp.plugin.qanalysis.STmap;
19 import com.github.celldynamics.quimp.utils.graphics.GraphicsElements;
20
21 import ij.ImagePlus;
22 import ij.ImageStack;
23 import ij.gui.Overlay;
24 import ij.gui.PointRoi;
25 import ij.gui.PolygonRoi;
26 import ij.plugin.ZProjector;
27 import ij.process.FloatProcessor;
28 import ij.process.ImageProcessor;
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 public abstract class TrackVisualisation {
46
47
48
49
50 static final Logger LOGGER = LoggerFactory.getLogger(TrackVisualisation.class.getName());
51
52
53
54 public double circleRadius = 7.;
55
56
57
58 public static Color MAXIMA_COLOR = Color.MAGENTA;
59
60
61
62
63
64
65
66
67
68
69
70 public static Color[] color = { Color.YELLOW, Color.GREEN, Color.WHITE };
71
72
73
74
75 protected ImagePlus originalImage;
76
77
78
79
80 protected Overlay overlay;
81
82
83
84
85
86
87
88
89 public TrackVisualisation(ImagePlus originalImage) {
90 this.originalImage = originalImage;
91 LOGGER.trace("Num of slices: " + originalImage.getStackSize());
92 overlay = originalImage.getOverlay();
93 if (overlay == null) {
94 overlay = new Overlay();
95 }
96
97 }
98
99
100
101
102
103
104
105 public TrackVisualisation(String name, ImageProcessor imp) {
106 this(new ImagePlus(name, imp));
107 }
108
109
110
111
112
113
114
115
116
117 public void plotCircle(double x, double y, Color color, double radius) {
118
119 PolygonRoi or = GraphicsElements.getCircle(x, y, color, radius);
120 overlay.add(or);
121 }
122
123
124
125
126
127
128 public ImagePlus getOriginalImage() {
129 return originalImage;
130 }
131
132
133
134
135
136
137
138
139
140 protected Color getColor(Track track) {
141 Color c;
142 Track.TrackType type = track.type;
143 switch (type) {
144 case FORWARD:
145 c = color[1];
146 break;
147 case BACKWARD:
148 c = color[0];
149 break;
150 case OTHER:
151 c = color[2];
152 break;
153 default:
154 throw new IllegalArgumentException("Color not supported");
155 }
156 return c;
157 }
158
159
160
161
162
163
164
165
166
167
168
169
170 public void flatten(int method, boolean preserveStack) {
171 ImageStack is = originalImage.getStack();
172 is = is.convertToFloat();
173
174 ZProjector zp = new ZProjector(new ImagePlus(originalImage.getTitle(), is));
175 zp.setStartSlice(1);
176 zp.setStopSlice(originalImage.getStackSize());
177 zp.setMethod(method);
178 zp.doProjection();
179 ImagePlus ret = zp.getProjection();
180
181 if (originalImage.getStackSize() > 1 && preserveStack) {
182 ImageStack imS = new ImageStack(ret.getWidth(), ret.getHeight());
183 for (int s = 0; s < originalImage.getStackSize(); s++) {
184 imS.addSlice(ret.getProcessor().convertToByte(true));
185 }
186 originalImage = new ImagePlus(originalImage.getTitle(), imS);
187 } else {
188 originalImage =
189 new ImagePlus(originalImage.getTitle(), ret.getProcessor().convertToByte(true));
190 }
191
192 }
193
194
195
196
197 public void clear() {
198 if (overlay != null) {
199 overlay.clear();
200 }
201 }
202
203
204
205
206
207
208
209 static class Map extends TrackVisualisation {
210
211
212
213
214
215
216
217
218
219
220
221
222
223 private boolean rotated = false;
224
225 private double ts = 1.0;
226 private double os = 1.0;
227
228
229
230
231
232
233
234
235 public Map(ImagePlus originalImage) {
236 super(originalImage);
237 }
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252 public Map(ImagePlus originalImage, boolean rotated, double ts, double os) {
253 super(originalImage);
254 this.rotated = rotated;
255 this.ts = ts;
256 this.os = os;
257 }
258
259
260
261
262
263
264
265 public Map(String name, ImageProcessor imp) {
266 super(name, imp);
267 }
268
269
270
271
272
273
274
275 public Map(String name, float[][] data) {
276 super(name, new FloatProcessor(data));
277 }
278
279
280
281
282
283
284
285
286 public void addCirclesToImage(Polygon points, Color color, double radius) {
287 Polygon polsc = scale(points);
288 int[] indexes = polsc.ypoints;
289 int[] frames = polsc.xpoints;
290 for (int n = 0; n < points.npoints; n++) {
291
292 if (frames[n] < 0 || indexes[n] < 0) {
293 continue;
294 }
295 plotCircle(frames[n], indexes[n], color, radius);
296 }
297 originalImage.setOverlay(overlay);
298 }
299
300
301
302
303
304
305 public void addMaximaToImage(MaximaFinder maxF) {
306 Polygon max = maxF.getMaxima();
307
308 Polygon polsc = scale(max);
309 PointRoi pr = GraphicsElements.getPoint(polsc, TrackVisualisation.MAXIMA_COLOR);
310 overlay.add(pr);
311 originalImage.setOverlay(overlay);
312 }
313
314 private int[] scale(int[] in, int len, double sc) {
315 int[] ret = new int[len];
316 for (int i = 0; i < len; i++) {
317 ret[i] = (int) Math.round(in[i] * sc);
318 }
319 return ret;
320 }
321
322 private Polygon scale(Polygon in) {
323 int[] xp;
324 int[] yp;
325 if (rotated) {
326 yp = scale(in.xpoints, in.npoints, ts);
327 xp = scale(in.ypoints, in.npoints, os);
328 } else {
329 xp = scale(in.xpoints, in.npoints, ts);
330 yp = scale(in.ypoints, in.npoints, os);
331 }
332 return new Polygon(xp, yp, xp.length);
333 }
334
335
336
337
338
339
340
341 public void addTrackingLinesToImage(TrackCollection trackCollection) {
342 Iterator<Pair<Track, Track>> it = trackCollection.iterator();
343 while (it.hasNext()) {
344 Pair<Track, Track> pair = it.next();
345
346 Polygon pairLeft = scale(pair.getLeft().asPolygon());
347 Polygon pairRight = scale(pair.getRight().asPolygon());
348
349 PolygonRoi pr = GraphicsElements.getLine(pairLeft, getColor(pair.getLeft()));
350
351 overlay.add(pr);
352 pr = GraphicsElements.getLine(pairRight, getColor(pair.getRight()));
353
354 overlay.add(pr);
355 }
356 originalImage.setOverlay(overlay);
357 }
358
359 }
360
361
362
363
364
365
366
367 static class Image extends TrackVisualisation {
368
369
370
371
372
373
374 public Image(ImagePlus originalImage) {
375 super(originalImage);
376 }
377
378
379
380
381
382
383
384 public Image(String name, ImageProcessor imp) {
385 super(name, imp);
386 }
387
388
389
390
391
392
393
394
395
396 public void addCirclesToImage(STmap mapCell, Polygon points, Color color, double radius) {
397 double[][] x = mapCell.getxMap();
398 double[][] y = mapCell.getyMap();
399 int[] indexes = points.ypoints;
400 int[] frames = points.xpoints;
401 for (int n = 0; n < points.npoints; n++) {
402
403 if (frames[n] < 0 || indexes[n] < 0) {
404 continue;
405 }
406 double xcoord = x[frames[n]][indexes[n]];
407 double ycoord = y[frames[n]][indexes[n]];
408 plotCircle(xcoord, ycoord, color, radius);
409 }
410 originalImage.setOverlay(overlay);
411 }
412
413
414
415
416
417
418
419
420 public void addElementsToImage(STmap mapCell, TrackCollection trackCollection,
421 MaximaFinder mf) {
422 if (mf != null) {
423 Polygon max = mf.getMaxima();
424 addCirclesToImage(mapCell, max, TrackVisualisation.MAXIMA_COLOR, circleRadius);
425 }
426 if (trackCollection != null) {
427 addTrackingLinesToImage(mapCell, trackCollection);
428 }
429 }
430
431
432
433
434
435
436
437
438 public void addTrackingLinesToImage(STmap mapCell, TrackCollection trackCollection) {
439 double[][] x = mapCell.getxMap();
440 double[][] y = mapCell.getyMap();
441
442 ArrayList<float[]> xcoorda = new ArrayList<>();
443 ArrayList<float[]> ycoorda = new ArrayList<>();
444 int al = 0;
445
446 Iterator<Track> it = trackCollection.iteratorTrack();
447 while (it.hasNext()) {
448 Track track = it.next();
449 Polygon pr = track.asPolygon();
450
451 xcoorda.add(new float[pr.npoints]);
452 ycoorda.add(new float[pr.npoints]);
453
454
455 int invalidVertex = 0;
456
457 for (int f = 0; f < pr.npoints; f++) {
458
459 if (pr.ypoints[f] < 0 || pr.xpoints[f] < 0) {
460 invalidVertex++;
461 continue;
462 }
463 xcoorda.get(al)[f] = (float) x[pr.xpoints[f]][pr.ypoints[f]];
464 ycoorda.get(al)[f] = (float) y[pr.xpoints[f]][pr.ypoints[f]];
465 }
466 PolygonRoi polyRoi = GraphicsElements.getLine(xcoorda.get(al), ycoorda.get(al),
467 pr.npoints - invalidVertex, getColor(track));
468 overlay.add(polyRoi);
469 al++;
470 }
471 originalImage.setOverlay(overlay);
472 }
473 }
474
475
476
477
478
479
480
481 static class Stack extends TrackVisualisation {
482
483
484
485
486
487
488 public Stack(ImagePlus originalImage) {
489 super(originalImage);
490 }
491
492
493
494
495
496
497
498 public Stack(String name, ImageProcessor imp) {
499 super(name, imp);
500 }
501
502
503
504
505
506
507
508
509
510 public void addCirclesToImage(STmap mapCell, Polygon points, Color color, double radius) {
511 double[][] x = mapCell.getxMap();
512 double[][] y = mapCell.getyMap();
513 int[] indexes = points.ypoints;
514 int[] frames = points.xpoints;
515
516
517
518 for (int n = 0; n < points.npoints; n++) {
519
520 if (frames[n] < 0 || indexes[n] < 0) {
521 continue;
522 }
523 double xcoord = x[frames[n]][indexes[n]];
524 double ycoord = y[frames[n]][indexes[n]];
525 plotCircle(xcoord, ycoord, frames[n] + 1, color, radius);
526 }
527 originalImage.setOverlay(overlay);
528 }
529
530
531
532
533
534
535
536
537
538
539 public void addCirclesToImage(STmap mapCell, List<Pair<Point, Point>> points, Color color,
540 double radius) {
541 int[] x = new int[points.size()];
542 int[] y = new int[points.size()];
543 int l = 0;
544 for (Pair<Point, Point> p : points) {
545 x[l] = p.getRight().x;
546 y[l] = p.getRight().y;
547 l++;
548 }
549 Polygon poly = new Polygon(x, y, points.size());
550 addCirclesToImage(mapCell, poly, color, radius);
551 }
552
553
554
555
556
557
558
559
560
561
562
563 public void addTrackingMaximaToImage(STmap mapCell, TrackCollection trackCollection) {
564 double[][] x = mapCell.getxMap();
565 double[][] y = mapCell.getyMap();
566
567 ArrayList<float[]> xcoorda = new ArrayList<>();
568 ArrayList<float[]> ycoorda = new ArrayList<>();
569 int al = 0;
570
571 Iterator<Track> it = trackCollection.iteratorTrack();
572 while (it.hasNext()) {
573 Track track = it.next();
574 Polygon polyR = track.asPolygon();
575
576
577 List<Point> plR =
578 TrackMapAnalyser.polygon2Point2i(new ArrayList<Polygon>(Arrays.asList(polyR)));
579
580 Collections.sort(plR, new ListPoint2iComparator());
581
582 Polygon plRsorted = TrackMapAnalyser.point2i2Polygon(plR);
583
584 xcoorda.add(new float[plRsorted.npoints]);
585 ycoorda.add(new float[plRsorted.npoints]);
586
587
588 int invalidVertex = 0;
589
590 for (int f = 0; f < plRsorted.npoints; f++) {
591
592 if (plRsorted.ypoints[f] < 0 || plRsorted.xpoints[f] < 0) {
593 invalidVertex++;
594 continue;
595 }
596 xcoorda.get(al)[f] = (float) x[plRsorted.xpoints[f]][plRsorted.ypoints[f]];
597 ycoorda.get(al)[f] = (float) y[plRsorted.xpoints[f]][plRsorted.ypoints[f]];
598 }
599
600
601
602
603
604 for (int f = 0; f < plRsorted.npoints - invalidVertex; f++) {
605
606
607
608
609
610 PolygonRoi polyRoi = GraphicsElements.getCircle(xcoorda.get(al)[f], ycoorda.get(al)[f],
611 color[al % 2], circleRadius);
612
613 polyRoi.setPosition((int) plRsorted.xpoints[f] + 1);
614 overlay.add(polyRoi);
615 }
616 al++;
617 }
618 originalImage.setOverlay(overlay);
619
620 }
621
622
623
624
625
626
627
628
629
630
631
632
633 public void addTrackingLinesToImage(STmap mapCell, TrackCollection trackCollection) {
634 double[][] x = mapCell.getxMap();
635 double[][] y = mapCell.getyMap();
636
637 ArrayList<float[]> xcoorda = new ArrayList<>();
638 ArrayList<float[]> ycoorda = new ArrayList<>();
639 int al = 0;
640
641 Iterator<Track> it = trackCollection.iteratorTrack();
642 while (it.hasNext()) {
643 Track track = it.next();
644 Polygon polyR = track.asPolygon();
645
646
647 List<Point> plR =
648 TrackMapAnalyser.polygon2Point2i(new ArrayList<Polygon>(Arrays.asList(polyR)));
649
650 Collections.sort(plR, new ListPoint2iComparator());
651
652 Polygon plRsorted = TrackMapAnalyser.point2i2Polygon(plR);
653
654 xcoorda.add(new float[plRsorted.npoints]);
655 ycoorda.add(new float[plRsorted.npoints]);
656
657
658 int invalidVertex = 0;
659
660 for (int f = 0; f < plRsorted.npoints; f++) {
661
662 if (plRsorted.ypoints[f] < 0 || plRsorted.xpoints[f] < 0) {
663 invalidVertex++;
664 continue;
665 }
666 xcoorda.get(al)[f] = (float) x[plRsorted.xpoints[f]][plRsorted.ypoints[f]];
667 ycoorda.get(al)[f] = (float) y[plRsorted.xpoints[f]][plRsorted.ypoints[f]];
668 }
669
670
671
672
673
674 for (int f = 0; f < plRsorted.npoints - invalidVertex; f++) {
675
676
677
678
679
680 PolygonRoi polyRoi =
681 GraphicsElements.getLine(xcoorda.get(al), ycoorda.get(al), f + 1, color[al % 2]);
682
683 polyRoi.setPosition((int) plRsorted.xpoints[f] + 1);
684 overlay.add(polyRoi);
685
686
687
688 if (al % 2 == 1) {
689 PolygonRoi polyRoi1 = GraphicsElements.getLine(xcoorda.get(al - 1), ycoorda.get(al - 1),
690 xcoorda.get(al - 1).length, color[al % 2 - 1]);
691 polyRoi1.setPosition((int) plRsorted.xpoints[f] + 1);
692 overlay.add(polyRoi1);
693 }
694 }
695 al++;
696 }
697 originalImage.setOverlay(overlay);
698 }
699
700
701
702
703
704
705
706 public void addMaximaToImage(STmap mapCell, MaximaFinder mf) {
707 Polygon max = mf.getMaxima();
708 addCirclesToImage(mapCell, max, TrackVisualisation.MAXIMA_COLOR, 7);
709 }
710
711
712
713
714
715
716
717
718
719
720 public void plotCircle(double x, double y, int frame, Color color, double radius) {
721
722 PolygonRoi or = GraphicsElements.getCircle(x, y, color, radius);
723
724 or.setPosition(frame);
725 overlay.add(or);
726 }
727
728
729
730
731
732
733
734
735
736
737
738
739 public void addOutlinesToImage(STmap mapCell, ProtAnalysisOptions config) {
740 double[][] mm = mapCell.getMotMap();
741 double[][] cm = mapCell.getConvMap();
742
743 switch (config.selOutlineColoring.plotType) {
744 case MOTILITY:
745 plotOutline(mapCell.getxMap(), mapCell.getyMap(),
746 new Color[] { config.selOutlineColoring.motColor,
747 config.selOutlineColoring.defColor },
748 new double[] { config.selOutlineColoring.motThreshold }, mapCell.getMotMap());
749 break;
750 case CONVEXITY:
751 plotOutline(mapCell.getxMap(), mapCell.getyMap(),
752 new Color[] { config.selOutlineColoring.convColor,
753 config.selOutlineColoring.defColor },
754 new double[] { config.selOutlineColoring.convThreshold }, mapCell.getConvMap());
755 break;
756 case CONVANDEXP: {
757
758 double[][] tmpMap = new double[mm.length][];
759 for (int f = 0; f < tmpMap.length; f++) {
760 tmpMap[f] = new double[mm[f].length];
761 for (int r = 0; r < mm[f].length; r++) {
762 tmpMap[f][r] = ((mm[f][r] > 0 && cm[f][r] > 0)) ? 1.0 : -1.0;
763 }
764 }
765 plotOutline(
766 mapCell.getxMap(), mapCell.getyMap(), new Color[] {
767 config.selOutlineColoring.convColor, config.selOutlineColoring.defColor },
768 new double[] { 0 }, tmpMap);
769 }
770 break;
771 case CONCANDRETR: {
772
773 double[][] tmpMap = new double[mm.length][];
774 for (int f = 0; f < tmpMap.length; f++) {
775 tmpMap[f] = new double[mm[f].length];
776 for (int r = 0; r < mm[f].length; r++) {
777 tmpMap[f][r] = (mm[f][r] < 0 && cm[f][r] < 0) ? 1.0 : -1.0;
778 }
779 }
780 plotOutline(
781 mapCell.getxMap(), mapCell.getyMap(), new Color[] {
782 config.selOutlineColoring.motColor, config.selOutlineColoring.defColor },
783 new double[] { 0 }, tmpMap);
784 }
785 break;
786 case BOTH: {
787 double[][] tmpMap = new double[mm.length][];
788 for (int f = 0; f < tmpMap.length; f++) {
789 tmpMap[f] = new double[mm[f].length];
790 for (int r = 0; r < mm[f].length; r++) {
791 tmpMap[f][r] = (mm[f][r] > 0 && cm[f][r] > 0) ? 1.0 : -1.0;
792 }
793 }
794 double[][] tmpMap1 = new double[mm.length][];
795 for (int f = 0; f < tmpMap.length; f++) {
796 tmpMap1[f] = new double[mm[f].length];
797 for (int r = 0; r < mm[f].length; r++) {
798 tmpMap1[f][r] = (mm[f][r] < 0 && cm[f][r] < 0) ? 1.0 : -1.0;
799 }
800 }
801
802 plotOutline(mapCell.getxMap(), mapCell.getyMap(),
803 new Color[] { config.selOutlineColoring.motColor,
804 config.selOutlineColoring.convColor, config.selOutlineColoring.defColor },
805 new double[] { 0, 0 }, tmpMap, tmpMap1);
806
807 }
808 break;
809 case UNIFORM: {
810
811 plotOutline(mapCell.getxMap(), mapCell.getyMap(),
812 new Color[] { config.selOutlineColoring.motColor,
813 config.selOutlineColoring.defColor },
814 new double[] { Double.MAX_VALUE }, mapCell.getMotMap());
815 }
816 break;
817 default:
818 throw new IllegalArgumentException("Plot Type not supported");
819 }
820
821 originalImage.setOverlay(overlay);
822 }
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839 private void plotOutline(double[][] x, double[][] y, Color[] color, double[] threshold,
840 double[][]... map) {
841 Polygon[] lines = new Polygon[map.length + 1];
842 for (int i = 0; i < lines.length; i++) {
843 lines[i] = new Polygon();
844 }
845
846
847 for (int f = 0; f < x.length; f++) {
848 for (int r = 0; r < x[0].length; r++) {
849 int l = 0;
850 boolean added = false;
851 for (Object item : map) {
852 double[][] tmpmap = (double[][]) item;
853
854
855 if (tmpmap[f][r] >= threshold[l]) {
856
857 lines[l].addPoint((int) Math.round(x[f][r]), (int) Math.round(y[f][r]));
858 added = true;
859
860 if (lines[lines.length - 1].npoints > 0) {
861 PolygonRoi polyR = GraphicsElements.getLine(lines[lines.length - 1],
862 color[color.length - 1], f + 1);
863 overlay.add(polyR);
864 lines[lines.length - 1] = new Polygon();
865 }
866 } else {
867 if (lines[l].npoints > 0) {
868 PolygonRoi polyR = GraphicsElements.getLine(lines[l], color[l], f + 1);
869 overlay.add(polyR);
870 lines[l] = new Polygon();
871 }
872
873 if (!added) {
874 lines[lines.length - 1].addPoint((int) Math.round(x[f][r]),
875 (int) Math.round(y[f][r]));
876 }
877 }
878 l++;
879 }
880 }
881
882 PolygonRoi polyR;
883 for (int i = 0; i < lines.length; i++) {
884 if (lines[i].npoints > 0) {
885 polyR = GraphicsElements.getLine(lines[i], color[i], f + 1);
886 overlay.add(polyR);
887 lines[i] = new Polygon();
888 }
889 }
890 }
891 }
892
893
894
895
896
897
898
899 class ListPoint2iComparator implements Comparator<Point> {
900
901
902
903
904
905
906 @Override
907 public int compare(Point o1, Point o2) {
908 if (o1.x < o2.x) {
909 return -1;
910 }
911 if (o1.x > o2.x) {
912 return 1;
913 } else {
914 return 0;
915 }
916 }
917
918 }
919 }
920 }