View Javadoc
1   package com.github.celldynamics.quimp.plugin.protanalysis;
2   
3   import java.awt.event.ActionEvent;
4   import java.awt.geom.Point2D;
5   import java.util.ArrayList;
6   import java.util.HashMap;
7   import java.util.List;
8   import java.util.Map;
9   
10  import com.github.celldynamics.quimp.QParamsQconf;
11  import com.github.celldynamics.quimp.filesystem.QconfLoader;
12  import com.github.celldynamics.quimp.geom.MapCoordConverter;
13  import com.github.celldynamics.quimp.plugin.qanalysis.STmap;
14  
15  import ij.ImagePlus;
16  import ij.WindowManager;
17  import ij.gui.Overlay;
18  import ij.plugin.ZProjector;
19  
20  /**
21   * Generalisation of action for tracking points.
22   * 
23   * <p>Read:
24   * <ol>
25   * <li>{@link ProtAnalysisOptions#chbNewImage}
26   * <li>{@link ProtAnalysisOptions#plotStaticDynamic}
27   * <li>{@link ProtAnalysisOptions#chbFlattenStaticTrackImage}
28   * <li>{@link ProtAnalysisOptions#chbShowTrackMotility}
29   * <li>{@link ProtAnalysisOptions#chbShowPoint}
30   * <li>{@link ProtAnalysisOptions#chbShowTrack}
31   * <li>{@link ProtAnalysisOptions#circleRadius}
32   * </ol>
33   * 
34   * @author p.baniukiewicz
35   *
36   */
37  @SuppressWarnings("serial")
38  public class ActionTrackPoints extends ProtAnalysisAbstractAction {
39  
40    protected ImagePlus image; // image used for plotting overlay
41  
42    /**
43     * Action creator.
44     * 
45     * @param name name
46     * @param desc description
47     * @param ui reference to outer class.
48     */
49    public ActionTrackPoints(String name, String desc, ProtAnalysisUi ui) {
50      super(name, desc, ui);
51    }
52  
53    /**
54     * Initialise this action as ActionListener without properties.
55     * 
56     * @param ui reference to outer class.
57     */
58    public ActionTrackPoints(ProtAnalysisUi ui) {
59      super(ui);
60    }
61  
62    /*
63     * (non-Javadoc)
64     * 
65     * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
66     */
67    @Override
68    public void actionPerformed(ActionEvent e) {
69      if (options.chbNewImage.getValue()) { // if true = new image
70        image = ui.getImagePlus().duplicate();
71        Overlay overlay = image.getOverlay();
72        if (overlay != null) {
73          overlay.clear();
74        }
75      } else {
76        image = ui.getImagePlus();
77      }
78      QconfLoader qconfLoader = ui.getModel().getQconfLoader();
79      switch (options.plotStaticDynamic.shortValue()) {
80        case 0:
81          if (options.chbFlattenStaticTrackImage.booleanValue()) {
82            image = trackStaticFlat(qconfLoader); // static flat image, return new copy of image
83          } else {
84            trackStatic(qconfLoader); // static but we have slices
85          }
86          break;
87        case 1:
88          trackDynamic(qconfLoader); // dynamic
89          break;
90        default:
91          throw new IllegalArgumentException("Type of plot not supported.");
92      }
93      if (options.chbShowTrackMotility.booleanValue()) {
94        plotOnMap(qconfLoader);
95      }
96      if (options.chbNewImage.getValue()) {
97        image.setTitle(WindowManager.makeUniqueName(image.getTitle() + " - tracking"));
98        image.show(); // show new image
99      } else { // clear user selection at the end if no new image
100       new ActionClearPoints(ui).clear();
101     }
102   }
103 
104   /**
105    * Plot tracking lines on map.
106    *
107    * @param qconfLoader qconfLoader
108    * @see ProtAnalysisOptions#chbShowTrackMotility
109    */
110   void plotOnMap(QconfLoader qconfLoader) {
111     STmap[] stMap = ((QParamsQconf) qconfLoader.getQp()).getLoadedDataContainer().getQState();
112     HashMap<Integer, List<Point2D>> tmpSelected = extractPoints(stMap);
113     // plot - iterate over cells (keys) and plot all points
114     for (Map.Entry<Integer, List<Point2D>> entry : tmpSelected.entrySet()) {
115       Integer cellNo = entry.getKey(); // cell number
116       List<Point2D> points = entry.getValue(); // users points
117 
118       // time - y
119       // outline - x
120       ImagePlus mm = ActionPlotMap.getUnscaledMap(stMap[cellNo], stMap[cellNo].getMotMap(),
121               WindowManager.makeUniqueName("motility_map_cell_" + cellNo));
122 
123       // This is for unscaled map
124       // TrackVisualisation.Map visSingle = new TrackVisualisation.Map(
125       // WindowManager.makeUniqueName("motility_map_cell_" + cellNo),
126       // QuimPArrayUtils.double2dfloat(stMap[cellNo].getMotMap()));
127       TrackVisualisation.Map visSingle = new TrackVisualisation.Map(mm, true,
128               (double) stMap[cellNo].getRes() / stMap[cellNo].getT(), 1.0);
129 
130       MaximaFinderquimp/plugin/protanalysis/MaximaFinder.html#MaximaFinder">MaximaFinder mf = new MaximaFinder(ui.getImagePlus().getProcessor());
131       mf.setMaxima(points);
132       TrackCollection trackCollection = getTracks(stMap, cellNo, mf);
133       visSingle.addMaximaToImage(mf);
134       visSingle.addTrackingLinesToImage(trackCollection);
135       visSingle.getOriginalImage().show();
136     }
137   }
138 
139   /**
140    * Dynamic plot.
141    * 
142    * @param qconfLoader qconfLoader
143    */
144   void trackDynamic(QconfLoader qconfLoader) {
145     STmap[] stMap = ((QParamsQconf) qconfLoader.getQp()).getLoadedDataContainer().getQState();
146     // wrap original image into TrackVisualisation object. All changes will modify the image
147     // Note that TrackVisualisation keeps only reference and adds overlay object to it.
148     TrackVisualisation.Stack visStackDynamic = new TrackVisualisation.Stack(image);
149     visStackDynamic.circleRadius = options.circleRadius;
150 
151     HashMap<Integer, List<Point2D>> tmpSelected = extractPoints(stMap);
152     // plot - iterate over cells (keys) and plot all points
153     for (Map.Entry<Integer, List<Point2D>> entry : tmpSelected.entrySet()) {
154       Integer cellNo = entry.getKey(); // cell number
155       List<Point2D> points = entry.getValue(); // users points
156       MaximaFinderquimp/plugin/protanalysis/MaximaFinder.html#MaximaFinder">MaximaFinder mf = new MaximaFinder(ui.getImagePlus().getProcessor());
157       mf.setMaxima(points);
158 
159       TrackCollection trackCollection = getTracks(stMap, cellNo, mf);
160       if (options.chbShowPoint.booleanValue()) {
161         visStackDynamic.addMaximaToImage(stMap[cellNo], mf);
162         visStackDynamic.addTrackingMaximaToImage(stMap[cellNo], trackCollection);
163       }
164       // tracking lines
165       if (options.chbShowTrack.booleanValue()) {
166         visStackDynamic.addTrackingLinesToImage(stMap[cellNo], trackCollection);
167       }
168 
169     }
170     addOutline(stMap, visStackDynamic);
171   }
172 
173   /**
174    * Order data by cell numbers. For each key (cell number) collect all users points.
175    * 
176    * @param stMap maps
177    * @return Map(CellNo, ListOfPoints for this cellNo). Point has coordinate of Map [frame,index]
178    */
179   HashMap<Integer, List<Point2D>> extractPoints(STmap[] stMap) {
180     HashMap<Integer, List<Point2D>> tmpSelected = new HashMap<>();
181     for (PointCoords p : ui.getModel().selected) {
182       int tmpIndex = MapCoordConverter.findPointIndex(stMap[p.cellNo].getxMap()[p.frame],
183               stMap[p.cellNo].getyMap()[p.frame], p.point.getX(), p.point.getY(), Double.MAX_VALUE);
184       if (tmpIndex >= 0) {
185         // if no cell in HashMap - create
186         if (tmpSelected.get(p.cellNo) == null) {
187           tmpSelected.put(p.cellNo, new ArrayList<Point2D>());
188         }
189         // add point to the cell
190         tmpSelected.get(p.cellNo).add(new Point2D.Double(p.frame, tmpIndex));
191       }
192     }
193     logger.trace("Added " + tmpSelected.size() + " points");
194     return tmpSelected;
195   }
196 
197   /**
198    * Combination of static and dynamic.
199    * 
200    * @param qconfLoader qconfLoader
201    */
202   void trackStatic(QconfLoader qconfLoader) {
203     STmap[] stMap = ((QParamsQconf) qconfLoader.getQp()).getLoadedDataContainer().getQState();
204     // wrap original image into TrackVisualisation object. All changes will modify the image
205     // Note that TrackVisualisation keeps only reference and adds overlay object to it.
206     TrackVisualisation.Stack visStackStatic = new TrackVisualisation.Stack(image);
207     visStackStatic.circleRadius = options.circleRadius;
208 
209     HashMap<Integer, List<Point2D>> tmpSelected = extractPoints(stMap);
210     // plot - iterate over cells (keys) and plot all points
211     for (Map.Entry<Integer, List<Point2D>> entry : tmpSelected.entrySet()) {
212       Integer cellNo = entry.getKey(); // cell number
213       List<Point2D> points = entry.getValue(); // users points
214       MaximaFinderquimp/plugin/protanalysis/MaximaFinder.html#MaximaFinder">MaximaFinder mf = new MaximaFinder(ui.getImagePlus().getProcessor());
215       mf.setMaxima(points);
216 
217       TrackCollection trackCollection = getTracks(stMap, cellNo, mf);
218       if (options.chbShowPoint.booleanValue()) {
219         visStackStatic.addTrackingMaximaToImage(stMap[cellNo], trackCollection);
220       }
221 
222       // tracking lines
223       TrackVisualisation.Image vis = new TrackVisualisation.Image(image);
224       if (options.chbShowTrack.booleanValue()) {
225         vis.addElementsToImage(stMap[cellNo], trackCollection, mf);
226       }
227     }
228     // add outline always even if no points
229     addOutline(stMap, visStackStatic);
230   }
231 
232   /**
233    * Get tracks.
234    * 
235    * @param stMap stMap
236    * @param cellNo cellNo
237    * @param mf mf
238    * @return trackCollection
239    */
240   protected TrackCollection getTracks(STmap[] stMap, Integer cellNo, MaximaFinder mf) {
241     TrackMapAnalyserp/plugin/protanalysis/TrackMapAnalyser.html#TrackMapAnalyser">TrackMapAnalyser pt = new TrackMapAnalyser();
242     pt.trackMaxima(stMap[cellNo], -1.0, mf); // TODO Add as parameter
243     TrackCollection trackCollection = pt.getTrackCollection();
244     return trackCollection;
245   }
246 
247   /**
248    * Plot outlines.
249    * 
250    * @param stMap stMap
251    * @param visStackStatic visStackStatic
252    */
253   private void addOutline(STmap[] stMap, TrackVisualisation.Stack visStackStatic) {
254     for (STmap mapCell : stMap) {
255       // color outlines
256       visStackStatic.addOutlinesToImage(mapCell, options);
257     }
258   }
259 
260   /**
261    * Fully static.
262    * 
263    * @param qconfLoader qconfLoader
264    * @return flattened image
265    */
266   ImagePlus trackStaticFlat(QconfLoader qconfLoader) {
267     STmap[] stMap = ((QParamsQconf) qconfLoader.getQp()).getLoadedDataContainer().getQState();
268     // wrap original image into TrackVisualisation object. All changes will modify the image
269     // Note that TrackVisualisation keeps only reference and adds overlay object to it.
270     TrackVisualisation.Image visStackStatic = new TrackVisualisation.Image(image);
271     visStackStatic.circleRadius = options.circleRadius;
272     visStackStatic.flatten(ZProjector.AVG_METHOD, false);
273 
274     // order data by cell numbers. For each key (cell number) collect all users points
275     HashMap<Integer, List<Point2D>> tmpSelected = new HashMap<>();
276     for (PointCoords p : ui.getModel().selected) {
277       int tmpIndex = MapCoordConverter.findPointIndex(stMap[p.cellNo].getxMap()[p.frame],
278               stMap[p.cellNo].getyMap()[p.frame], p.point.getX(), p.point.getY(), Double.MAX_VALUE);
279       if (tmpIndex >= 0) {
280         // if no cell in HashMap - create
281         if (tmpSelected.get(p.cellNo) == null) {
282           tmpSelected.put(p.cellNo, new ArrayList<Point2D>());
283         }
284         // add point to the cell
285         tmpSelected.get(p.cellNo).add(new Point2D.Double(p.frame, tmpIndex));
286       }
287     }
288     logger.trace("Added " + tmpSelected.size() + " points");
289     // plot - iterate over cells (keys) and plot all points
290     for (Map.Entry<Integer, List<Point2D>> entry : tmpSelected.entrySet()) {
291       Integer cellNo = entry.getKey(); // cell number
292       List<Point2D> points = entry.getValue(); // users points
293       MaximaFinderquimp/plugin/protanalysis/MaximaFinder.html#MaximaFinder">MaximaFinder mf = new MaximaFinder(ui.getImagePlus().getProcessor());
294       mf.setMaxima(points);
295 
296       TrackCollection trackCollection = getTracks(stMap, cellNo, mf);
297       // tracking lines
298       MaximaFinder mfTmp = null;
299       TrackCollection tcTmp = null;
300       if (options.chbShowTrack.booleanValue()) {
301         tcTmp = trackCollection;
302       }
303       if (options.chbShowPoint.booleanValue()) {
304         mfTmp = mf;
305       }
306       if (options.chbShowTrack.booleanValue()) {
307         visStackStatic.addElementsToImage(stMap[cellNo], tcTmp, mfTmp);
308       }
309 
310     }
311     return visStackStatic.getOriginalImage();
312   }
313 
314 }