View Javadoc
1   package com.github.celldynamics.quimp.plugin.randomwalk;
2   
3   import java.awt.Color;
4   import java.io.File;
5   import java.util.ArrayList;
6   import java.util.Collections;
7   import java.util.Comparator;
8   import java.util.Date;
9   import java.util.Iterator;
10  import java.util.List;
11  
12  import org.apache.commons.lang3.tuple.Pair;
13  import org.slf4j.Logger;
14  import org.slf4j.LoggerFactory;
15  
16  import com.github.celldynamics.quimp.Outline;
17  import com.github.celldynamics.quimp.QuimP;
18  import com.github.celldynamics.quimp.geom.TrackOutline;
19  import com.github.celldynamics.quimp.geom.filters.OutlineProcessor;
20  import com.github.celldynamics.quimp.plugin.ana.ANAp;
21  import com.github.celldynamics.quimp.plugin.randomwalk.BinaryFilters.MorphoOperations;
22  import com.github.celldynamics.quimp.plugin.randomwalk.RandomWalkSegmentation.SeedTypes;
23  import com.github.celldynamics.quimp.utils.IJTools;
24  import com.github.celldynamics.quimp.utils.test.RoiSaver;
25  
26  import ij.ImagePlus;
27  import ij.ImageStack;
28  import ij.gui.Roi;
29  import ij.process.AutoThresholder;
30  import ij.process.Blitter;
31  import ij.process.ByteProcessor;
32  import ij.process.ImageProcessor;
33  
34  /**
35   * Generate new seeds for n+1 frame in stack using previous results of segmentation.
36   * 
37   * <p>This class supports two methods:
38   * <ol>
39   * <li>Based on morphological operations
40   * <li>Based on contour shrinking (part of QuimP Outline framework)
41   * </ol>
42   * 
43   * <p>In both cases the aim is to shrink the object (which is white) to prevent overlapping
44   * foreground
45   * and background in next frame (assuming that objects are moving). The same is for background.
46   * Finally, the new seed should have set foreground pixels to area inside the object and background
47   * pixels in remaining part of image. There should be unseeded strip of pixels around the object.
48   * 
49   * @author p.baniukiewicz
50   */
51  public abstract class PropagateSeeds {
52  
53    /** The Constant LOGGER. */
54    static final Logger LOGGER = LoggerFactory.getLogger(PropagateSeeds.class.getName());
55  
56    /**
57     * Seed propagators available in this class.
58     * 
59     * @author p.baniukiewicz
60     *
61     */
62    public enum Propagators {
63      /**
64       * Just copy input as output.
65       */
66      NONE,
67      /**
68       * Use contour shrinking.
69       * 
70       * @see Contour
71       */
72      CONTOUR,
73      /**
74       * Use morphological operations.
75       * 
76       * @see Morphological
77       */
78      MORPHOLOGICAL
79  
80    }
81  
82    /**
83     * Default setting. Better do not change.
84     */
85    public boolean darkBackground = true;
86    /**
87     * Thresholding method used for estimating true background.
88     * 
89     * <p>If null background is not modified.
90     * 
91     */
92    private AutoThresholder.Method thresholdMethod = null;
93  
94    /**
95     * Default constructor.
96     */
97    public PropagateSeeds() {
98    }
99  
100   /**
101    * Allow to store seed history that can be later presented in form of composite image.
102    * 
103    * @param storeSeeds <tt>true</tt> to store seeds.
104    * @param trueBackground if not null, selected method will be used for estimating true background
105    *        - excluding bright objects from it
106    * @see #getCompositeSeed(ImagePlus, int)
107    */
108   public PropagateSeeds(boolean storeSeeds, AutoThresholder.Method trueBackground) {
109     this.storeSeeds = storeSeeds;
110     if (storeSeeds) {
111       this.seeds = new ArrayList<>();
112     }
113     thresholdMethod = trueBackground;
114   }
115 
116   /**
117    * Default resolution used during outlining objects.
118    * 
119    * @see Contour#getOutlineAndColors(ImageProcessor, boolean)
120    */
121   public static final int STEPS = 4;
122   /**
123    * By default seed history is not stored.
124    */
125   protected boolean storeSeeds = false;
126   /**
127    * Container for FG and BG seeds pixels used for seed visualisation.
128    * 
129    * <p>Every imageProcessor in pair contains important bits set to WHITE. For example BG pixels are
130    * white here as well as FG pixels.
131    * 
132    * @see #getCompositeSeed(ImagePlus, int)
133    * @see PropagateSeeds#storeSeeds
134    */
135   protected List<Seeds> seeds;
136   /**
137    * Scale color values in composite preview.
138    * 
139    * <p>1.0 stand for opaque colors.
140    * 
141    * @see #getCompositeSeed(ImagePlus, int)
142    */
143   public static final double colorScaling = 0.5;
144 
145   /**
146    * Return demanded propagator.
147    * 
148    * @param prop propagator to create
149    * @param storeseeds true for storing seeds
150    * @param trueBackground if not null, selected method will be used for estimating true
151    *        background - excluding bright objects from it
152    * @return the propagator
153    */
154   public static PropagateSeeds getPropagator(Propagators prop, boolean storeseeds,
155           AutoThresholder.Method trueBackground) {
156     switch (prop) {
157       case NONE:
158         return new PropagateSeeds.Dummy(storeseeds);
159       case CONTOUR:
160         return new PropagateSeeds.Contour(storeseeds, trueBackground);
161       case MORPHOLOGICAL:
162         return new PropagateSeeds.Morphological(storeseeds, trueBackground);
163       default:
164         throw new IllegalArgumentException("Unknown propagator");
165     }
166   }
167 
168   /**
169    * Empty propagator. Do nothing.
170    * 
171    * @author p.baniukiewicz
172    *
173    */
174   public static class Dummy extends PropagateSeeds {
175 
176     /** The binary. */
177     PropagateSeeds binary;
178 
179     /**
180      * Default constructor without storing seed history.
181      */
182     public Dummy() {
183       binary = new PropagateSeeds.Morphological();
184     }
185 
186     /**
187      * Allow to store seed history that can be later presented in form of composite image.
188      * 
189      * @param storeSeeds <tt>true</tt> to store seeds.
190      * @see #getCompositeSeed(ImagePlus, int)
191      */
192     public Dummy(boolean storeSeeds) {
193       binary = new PropagateSeeds.Morphological(storeSeeds, null);
194     }
195 
196     /*
197      * (non-Javadoc)
198      * 
199      * @see com.github.celldynamics.quimp.plugin.randomwalk.PropagateSeeds#propagateSeed(ij.process.
200      * ImageProcessor, double, double)
201      * 
202      */
203     @Override
204     public Seeds propagateSeed(ImageProcessor previous, ImageProcessor org, double shrinkPower,
205             double expandPower) {
206       Seedsmp/plugin/randomwalk/Seeds.html#Seeds">Seeds decodedSeeds = new Seeds(2);
207       try {
208         decodedSeeds = SeedProcessor.decodeSeedsfromGrayscaleImage(previous);
209         if (decodedSeeds.get(SeedTypes.FOREGROUNDS) == null) {
210           throw new RandomWalkException("no FG maps");
211         }
212       } catch (RandomWalkException e) {
213         // this is handled to console only as return is still valid (no FG seeds in output)
214         LOGGER.debug("Empty seeds. " + e.getMessage());
215       }
216       return decodedSeeds;
217     }
218 
219     /*
220      * (non-Javadoc)
221      * 
222      * @see
223      * com.github.celldynamics.quimp.plugin.randomwalk.PropagateSeeds#getCompositeSeed(ij.ImagePlus)
224      */
225     @Override
226     public ImagePlus getCompositeSeed(ImagePlus org, int offset) throws RandomWalkException {
227       // Need override as we have different object here (binary, not this).
228       return binary.getCompositeSeed(org, offset);
229     }
230 
231   }
232 
233   /**
234    * Contain methods for propagating seeds to the next frame using contour shrinking operations.
235    * 
236    * <p>{@link Contour#scaleMagn} and <t>shrinkPower</t> specified in
237    * {@link OutlineProcessor#shrinknl(double, double, double, double, double, double, double, double)}
238    * are related to each other. Larger <t>shrinkPower</t> stands for larger number of steps during
239    * shrinking
240    * (Step size is fixed {@link Contour#stepSize}). Therefore large {@link Contour#scaleMagn} will
241    * move concave node too
242    * fast because internally {@link Contour#stepSize} is multiplied by factor returned by
243    * {@link OutlineProcessor#amplificationFactor(double, double, double)} that ranges 1.0 for
244    * positive curvatures to {@link Contour#scaleMagn} for negative with specified
245    * {@link Contour#scaleSigma}. Generally increasing <t>shrinkPower</t>,
246    * the{@link Contour#scaleMagn} should be deceased.
247    * 
248    * <p>To increase effect of shifting nodes with negative curvature, normals locally can be
249    * adjusted according to node with lowest curvature. See
250    * {@link OutlineProcessor#shrinknl(double, double, double, double, double, double, double,
251    * double)}
252    * 
253    * <p>For linear scaling set {@link Contour#scaleMagn} to 1.0 and
254    * {@link Contour#averageNormalsDist} to 0.0.
255    * 
256    * <p>PropagateSeeds_Contour<br />
257    * <img src=
258    * "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAmcAAAJBCAIAAACMJYVtAAAhEElEQVR42u3d25Kj
259    * urYAUf7/p+d5OBErdpdtELrAlDQy+qGju3yjSKeEMToOAPmICBsBAADVBMjc9vMj
260    * 7hPYrZrcBHYZAn+9z/gfhj7b+Be/YmxSTW4Cq1Wz/UFHjKYB1eQmcDHW+7Mffw79
261    * PoeEX0eIX2/+9e8nN/x06evzLBylMhMzVpObQGpv//j29Qd+/funQr8kPL9h439d
262    * vvs4CoRZqslNYBpvL8eh53dyMnQ9l6pOv5NB98m42K8eE1WTm0AudUuO2LSb+VWS
263    * QjOP06NDfd+tgAzV5CYwjcMJx7OFtyoZzzITs1STm0Bqbx8ws/zOu3928kddv3ok
264    * ryY3gbzqVh8F+jpyLLf966jz12i0/Py985dJS8wy1+QmAAA3pmIAAEA1AQBQTQAA
265    * VBMAANUEAEA1AQBQTQDeLLyhgAjcBHBl3fMr3/76d28EUE1uAhNUs/0dpOJinufP
266    * BFBNbgI3xnrlV9g6mhfO/fRkxMq3Jw+X+S0Mm1eTm9xE0mT+EvLWgu/nC+eWrxff
267    * 8l/MxDLV5CY3kd3bCjPLj+10NPPSt5NrTJ88YWYiWzW5yU0knWv+OrxT6HDLugqF
268    * Zh49Vr41nsV0c01uchNzeNt4FGiEmeWC3R3PMhPJ55rctLcgo7cdPztp+ceOB4gu
269    * 34acp4cpqslNIJe6Xc7TK1kR/vMnn1n59vI+mYls1eQmN7G44QBecYqbgGoCnOIm
270    * wHAAqgkAGd6Ey/7ASBTcBDg55IehmuAmsJqWLYNfqCa4CRjGGt6qJriJx2SuW6kA
271    * UznZPijG09XkJjcxt9LtZpZ/l/n8i9g7vUfEVHeL/vpwk5vYupold/Vrtb8tR7KT
272    * 3jnmqyY3uYmzceKlDBWL3N4S++5VK/czM5Z4CJTu8NzkJjdTe1txjebyRW4HmbnN
273    * UaAnzwtwDkKKanKTm9yczOFCM8+9+vU5x58BaeH49ETCpeWM1d8LGMdNbnJzKnWr
274    * V74tX0JhxHi2+j1ofTO7XXWEnG9Wk5vc5GZqb3uZebmY0ed4lpl93PjlYb2czHyt
275    * mtzkJje3rublE7i1mPvXhXm3NrPEvcqxLTkXryY3uYnb6p4vKnvyY41HgY7iY1BH
276    * wTK5W2tZcYyInLmryU1ucnNxwzGHlpU39NvP6xQ3uQnVRJkM0fwIQU7VxKNuFmnH
277    * TYbjeS1LT9O74ad9QDVR6eYfGYsM5SbQzcwCLe/daTDTSBQD3awfu3ITeMvMMzkN
278    * aVUTY9yMxrvmJvBWMnvIyUzV5OaQZHITyFtNcqomHnAzOj4GN5fRfrP18xYZz7bJ
279    * 6dc9QTW5OW81uUn+fxxmcquZ0VOUKjn9+qaZa3LzRTdjxCNxc6tq2lAJq/n95oa0
280    * m1XT7yJhNbmZWt2TS2GdX7WLmQuY+eVOfIKSo5rc3HxEy83s3n5dUa/kus+OAj1q
281    * 5r//HyMe0pnuCarJzdndfK+a3Hzc4UYzvy489PkDNngSM4OZWeea3FRNI9qk6n4e
282    * 8Ok+nq1+v2DmUDOjVTq/x4HV5KZqmm6m9vax8Swzs801G6TzexxVTW5y8+edcHOx
283    * apY8EDMnqObhhL3VqsnNGat5OJk2obqFR4HOx6ctS+ziXTOFM2E1ucnNi/vh5uyG
284    * Y+pq/ly0iJnpneLm8tU03VRNJDXz+x2SUzWRwE3hVE1U7egxXAtyqiba3YxnHp6b
285    * wOvVPFyRxEgUKavJTSBjMsmpmuAmwMwHzDy+nlEE1eQmN4E3zDweOgrUJufx64Rc
286    * qOYmbsbDT4Wb2Put5/3PTprNpKhqbu1m4mpyM7EwHb803eVb2LfWf1DNfnL+UbTw
287    * j2pyM6Gh3FTZ8W8HXfbvLndYscRERjPnG9K2m6ya3Bzy5LnJzT4Dz5O/f47yfl30
288    * q3B3P7/5r/v874piJ6sdXa7lq5rp5Vzc0rpVqbd1M8fx3kSnHXAz0aTw1z/++oGv
289    * F8m8Zeavm5+b2fJfy1RzDzO/Wrp4Nbn59XmmPkLLzd0moJ/7dOEgt+S9oPzml49e
290    * rV/5qkmqefaAWaSIlYzj5qWbfyqe/wgtN1eO5a9slBxIyWbm+dEhc82FzJxezrql
291    * SLZ1s33BbW5yc8j8stCNzGYWamauOe2BoEXkrDhCu7ObqsnN1NUsl6HQw1s3bzns
292    * 43PNzYa0x7wfpdRVc2c3HaHlZhZ1f52AV3J05UStk5PlKswsP/uu/Py9863hjINJ
293    * zJx1YFu9WPSebh4pVs+Ou/8f3PQVT6TYyeKFXXIGOWfy0xX1lnfToHbxSSeYeeM5
294    * RSeP+l9+ZBo/VZObSau5vZtYUc5nDwRF7RvImZBj/ZpATtXcwc145TlxE8ws+f94
295    * /qnc8qdCtvrhrWpil2oGN4FUc81oV679wEyNotnlVM3l3YzXnxA3wcyHk3lUL3k7
296    * 6PqUUbGUoGriHTffryY3wcyHzYw6c+Lez94+5yAG2qyaWK2ad9xsXSdsCTcX2ls3
297    * epeZtpr9whqXh56Ouf1caX/m5lvVjK5KRpcfnt/NsaqMWK5vp0VuJzDz19Gd9rtq
298    * 3RCTn4kwdK/jpmr2Gr/W3IOzhB42s/0O51nkdg4zv+oR90/T65LtZeScsZrcXKOa
299    * Iz7T3CicFSvf/lp1789VIkvWyL1cOHf1RW6nqWbLbh79vni9UjhHrErNTdV8Ze9f
300    * zM3bA7q7q9SWLF17efNdF7mdvprnT+Ch0/TmlLPXqtTcVM0Mu/5G4TzZ0UuuGf2/
301    * yt1dQqGjmefvR4kXuV25mo9+hbRSzkhiHDe5+Uw1J7mIWO7zgwpXvj1Ol+t7xcxj
302    * hUVuVbNrOKca2PZalZqbqplnd+9woaLkk87ylW9zmlmoWeJFbpet5psXEptEzl6r
303    * UnNTNVPt6Eueu1d5jOX8X77u8Y1mbrDIrWomGdjGu/Zxc0k3X5vbvb2Ln4VzBjev
304    * 987ylW9LzDyK18j9ZeZOi9zW7ihpwhlv7+n9/Izn1eMmN5es5tFx1Yec4UQ+VPMV
305    * Px/dkK6ox83HwhkvbalJ3cSKcqrmrYdLKadq7uBmhnC+/pXuThcq4QvWnW4mHdgm
306    * k1M1ubne+LVmqwknNp9u5r1WUeQyUzVNN4cOHLN953EiN2FI+4SrkcbSZjlVExO7
307    * GYmvETCLm9hFTjtU0XbovXqnatrRMg9qJwtnDjfBTFut/6BXNe1l3Bw1rlVNmG4+
308    * vL1UE9yceMapmmDm0EDWfLCqmjDdzDmoVU0I52sTysrNqZoQzv4+Nh8EUk0wc9Tm
309    * GDjWVU20uLmYnn8O8MS376HdOAjkbCAI55vVjFEHcFUTm7t5LlOUXA8ot5tg5k7b
310    * os+31VQTppu93ZjHTWwzv9rmE5QzJxO8OagmN1d1M6pvNpWbMN3cYCvEGxtcNbGT
311    * m5FII+6AmaoJbq6XTNUEOTNugnhvg6sm9nAz8glknwMzU000VRPczDnRVE2QM+lE
312    * c+wDqObObm5RzWndxNbVXGbPihcmmqoJI9qcE03VBDk/Aln6Bei4c6eZ5FRN003V
313    * zOkmTDfn2LNqvgMdN52s/551/02omtyM6V/Pmm6CnNNUs89tSmap8b6ZqsnN/G52
314    * ruYkbmJLM/cZ1Z5fPbrn43XehKrJzZWrObObYObqch61x3biTTNVk5v53YwudzGb
315    * myDnHnI+oXTXi2mqJje3FPPGiX7x7jPF9tXMvGfFSC2vLX1jlSLV5OaeYt6LoxXE
316    * QM5xe30UrynfMLDttglVk5tbTTfjflDfchPMXH9Ue/ejkwY/VROmm6P0jBxugpzT
317    * yBm/p48n88jou+FUE9x8aTibxE1sb+Yx2XHay0ZGm5NtcqomRrm50nRzajdBzgUv
318    * 6R6dDhypJrjZvZox7kFVE+TMuOFUEy+5ucZ0c+AJRKoJ1cy47VQTqlm768f8boKc
319    * wqmaEM75qvnl3lQTj+7VqlmyYZxDi1fdnHq6GUu4CWaqpmqCm31eRtz/L9UEOVVT
320    * NbHpdPO4u268akI1F99kT31woprc5Ga/atpgGGQmOVUTM7hp5zicCgRDWmaqpn2N
321    * m6oJ081NtFRNcPO97eVDTTBz88GsatrdSn6EniaaSCdn2ExvDGZV005nUGuiCWbO
322    * vI1UE6abqglymm6Wbh3VhEFtzm2kmkgh505mXn8j+3EtVZObppsmmmDmpGN71URq
323    * N7eKg4kmhDN9NXv8hGpCOB+faKomnp9lrR7O6PNDqon33dwkEaqJ7MnYPZwvaama
324    * 3BROycQicqqmakI4VRPMNN1UTaxQzWOTc/dUE3nN3Hm6qZqYyk3VVE08sB9WnhYU
325    * O2wF1cRsbi5cDNXEVAPbDYa0UfMKVRPcVE2Qc1czX78CrWqCm6Uv1CUOMN2QVjVV
326    * E9zMOt0kEfINabcfz47aAKoJbrZVk0FIOaRVTdUEN1UTzFRN1QQ3VRNol3O3jzZf
327    * +uBENdHo5vrV9KEmVFM1VROqmdxN4P6utrqcqokF3FRNewxUUzXBTW6qJqaTk5mq
328    * CW6qJsDMm2YOfOmqCW5evHgn0IKZqqma4KZqgpnMVE1ws/crV01MI+c+1XzvgxPV
329    * BDdrh7OqCWbuZ6Zqgpu1E03VBDMzJlM1wc2cyVRNMHOzZKomuCmZWNbMETtp/Psn
330    * XzJVE6qZM5mqif2qGc9KECm1VE1sXk3JxEJmjpQzHlch27FZ1URON2eoJmuw2XQz
331    * Hhci50RTNbH5dNNEE6rZ+pBbJVM1kc3Nl1+hZGJ6M8fI+WQ1o/IBVBOTuRmzv7xk
332    * bgLdqtm420qmaiLtiPa1lyeZEM66Bxt4wlH02yCqCdPNF5KpmpjFzK7hfKaaUX/X
333    * j2qpmujrZszwkhybxQZm/g5n9H6w6O5kYi1VE93djMR5mchNYFQ471o6qJpnFxhK
334    * rKVqYpCb0WkA2vHwj2SCnDWWRrVgdT+cW0vVxFA3644G/blVxWUvo+R6mZKJLeSM
335    * IlvaW1jy89FtHaHXtFRNPOBm9ButXhY0On/rkiDYIJxfR5ot59xGxR3GHCfmqSYe
336    * czOu/nRxs0+cVRNLmXk88HFHc2OPObRUTXBTMrGHnEfbLPLPbWPQubPZtVRNcFMy
337    * sY2Zn5o1fnhSd8yo3ur3tVRNcFMysaWcJ3a1fHhycsPW8+JTaKma4KZkYm8zhz6p
338    * 6PIlskTfAldNcDOnm4DhXsbXpZrgpikmVtqJg5aqCW5KJrDh3pzxhagmuCmZMLD1
339    * /FUT3Nxz0gz83b/DE1ZN2NU3e8LA4nv8NEKqJrgJ7Kaop6Sa4KZeAreHt1G8Tknh
340    * n4onMNu2U01wE+DqgKuPdFyqQTXBTW4CUE0AAFQTAACoJgAAqgkAgGoCAKCaAACo
341    * JgAAqgkAgGoCAADVBABANQEAUE0AAFQTAADVBABANQEAgGoCAKCaAACoJgAAqgkA
342    * gGoCAKCaAACoJgAAUE0AAFQTAADVBABANQEAUE0AAFQTAACoJgAAqgkAgGoCAKCa
343    * AACoJgAAqgkAgGoCAADVBABANQEAUE0AAFQTAADVBABANQEAgGoCAKCaAACoJgAA
344    * qgkAgGoCAKCaAACoJgAAUE0AAFQTAADVBABANQEAUE0AAFQTAACoJgAAqgkAgGoC
345    * AKCaAACoJgAAqgkAgGqqJgAAqgkAgGoCAKCaAACoJgAAqgkAgGoCAADVBABANQEA
346    * UE0AAFQTAADVBABANQEAgGoCAKCaAACoJgAAqgkAgGoCAKCaAACoJgAAUE0AAFQT
347    * AADVBABANQEAUE0AAFQTAACoJgAAqgkAgGoCAKCaAH7ICQAASjBsAMw1AQBQTQDT
348    * yVzy8yPuE9itmtwEdhkCf73PLseRC98XHLPGhtXkJrBaNdsfdMRoGlBNbgIXY70/
349    * +/Hn0O9zSPh1hPj15l//fnLDT5e+Ps/CUSozMWM1uQmk9vaPb19/4Ne/fyr0S8Lz
350    * Gzb+1+W7j6NAmKWa3ASm8fZyHHp+JydD13Op6vQ7GXSfjIv96jFRNbkJ5FK35IhN
351    * u5lfJSk08zg9OtT33QrIUE1uAtM4nHA8W3irkvEsMzFLNbkJpPb2ATPL77z7Zyd/
352    * 1PWrR/JqchPIq271UaCvI8dy27+OOn+NRsvP3zt/mbTELHNNbgIAcGMqBgAAVBMA
353    * ANUEAEA1AQBQTQAAVBMAANUE4M3CGwqIwE0AV9Y9v/Ltr3/3RgDV5CYwQTXb30Eq
354    * LuZ5/kwA1eQmcGOsV36FraN54dxPT0asfHvycJnfwrB5NbnJTSRN5i8hby34fr5w
355    * bvl68S3/xUwsU01uchPZva0ws/zYTkczL307ucb0yRNmJrJVk5vcRNK55q/DO4UO
356    * t6yrUGjm0WPlW+NZTDfX5CY3MYe3jUeBRphZLtjd8SwzkXyuyU17CzJ62/Gzk5Z/
357    * 7HiA6PJtyHl6mKKa3ARyqdvlPL2SFeE/f/KZlW8v75OZyFZNbnITixsO4BWnuAmo
358    * JsApbgIMB6CaAJDhTbjsD4xEwU2Ak0N+GKoJbgKradky+IVqgpuAYazhrWqCm3hM
359    * 5rqVCjCVk+2DYjxdTW5yE3Mr3W5m+XeZz7+IvdN7REx1t+ivDze5ia2rWXJXv1b7
360    * 23IkO+mdY75qcpObOBsnXspQscjtLbHvXrVyPzNjiYdA6Q7PTW5yM7W3FddoLl/k
361    * dpCZ2xwFevK8AOcgpKgmN7nJzckcLjTz3Ktfn3P8GZAWjk9PJFxazlj9vYBx3OQm
362    * N6dSt3rl2/IlFEaMZ6vfg9Y3s9tVR8j5ZjW5yU1upva2l5mXixl9jmeZ2ceNXx7W
363    * y8nM16rJTW5yc+tqXj6BW4u5f12Yd2szS9yrHNuSc/FqcpObuK3u+aKyJz/WeBTo
364    * KD4GdRQsk7u1lhXHiMiZu5rc5CY3Fzccc2hZeUO//bxOcZObUE2UyRBtY9Z7A1s7
365    * gGqCm2Dmclr+r25FJ+vd8NM+oJqodDO4CbxpZtQoduEnM41EMcDN8vOBuAk8ZObd
366    * pW8bpCOnanLznpsHN4F5tWyWk5mqyc17E01uAtNXk5yqCW7iHe03Wz8vnZnR/WGC
367    * mStUk5vzVpOb5P/HYSa3mhk9LamS069vmrkmN7mJiatpQyU08/s9GNJuVk2/i1Wq
368    * yc2n1D25FNb5VbuYuUA1v9yJT1ByVJObRrQHNzN7+3VFvZLrPjsK9KiZR7fPNduq
369    * Sc6x1eQmN6PeOb/HZx1uNPPrwkOfP2CD56nm3/thZo65Jje5aUSbVN3PAz7dx7PV
370    * 7xfMvPx/cq5aTW6qpulmam8fG88yc4JqHj5Bebma3OQmN3epZskDMTObmYeTaTeo
371    * Jje5iT7qFh4FOh+ftiyxi6RmkvO9anKTm8K5uOFgJl5xips7uHnUnHxgx1BNZn78
372    * fzzw2ORUTSRwUzhVE1U7egx3QjhVE9wEmDl0SEtOteMmN4HcWg61gZyqCW4CzBxq
373    * 5v//HD9Vk5vcBDKYeTx0FKhNzv/8pKhqcpOb8NaTaUgbTz6VqLsDim5aTW6mrCY3
374    * EwvT8UvTXb6Ffeti2a9+4ztrNZseL27+UU1uprKSmyr71NtBl728yx0W3rD7k+9s
375    * 5jH8O2G9p5vtJqsmN5PMXxO52a+a3GweeJ78/XOs9+uiX4Vqnd/8133+d0Wxk9WO
376    * LtfyTelk+moer5iypqV1q1Jv66ZqcjPvpPDXP/76ga8Xybxl5q+bn5vZ8l/nGyT1
377    * Edodzfxq6eLV5KZqcnO+ghYOckt26/KbXz56tX4lqyb9eafIf4T2WPkgbdtWmn+u
378    * yc3z2TY3ufmaur92zZIDKdnMPD86dPeNTDUTmzm9nHVLkWzrpmpyM+n8stCNzGYW
379    * JvDXXFM10x8IWkTOiiO0O7upmtxMXc1yGQo9vHXzlsM+7Z+dOEI7lZnHvB+l1FVz
380    * ZzdV8/rZcPMZdX+dgFdydOVErZOT5SrMLD/7rvz8vVsbRDVzDyJjRvW4We7m3bNt
381    * M7h5PHytA25iV+LWj8QrTyipnDP56Yp6y7t5PHk1Wm6CmYtXc9TlR6bxUzVVM2k1
382    * t3cTK8r5ejWrH7jQvQ5+qiZedvOdanITzExazbv+VMhWP7xVTaxczeirGzehmrn8
383    * bD8wU6NodjlVk5vD52vcBDMfG8y2+jno+pRRsZSgamJBN+s94CaYeSSR8/7As/Js
384    * gxhos2qCm5u7udDeutG7zLRm9vC3yNKoe0z7s9eyjpvHgHNdC908lnZzrCojlut7
385    * bAHqBF+azm5mjJG8j8OTn4kwdK/j5ibV7LijV5w/1PXbL3uEc5CZ7XdYcfE8Zj6w
386    * I0fbbWMtOWesJjdnqWb5o3eZm+4VzoqVb3+tuvfnCq4la+ReLpz72ALUyeRcrZq9
387    * DsGsFM4Rq1JzUzULH73v+UDrDWrvDR7vrlJbsnTt5c3fWoCamaN34XhgwjqnnL1W
388    * peamatZV84lHXPtQ7fliCMfvhYf+LLN1d43cLmZeWneyKNjJMvfMjAT3MFLOSGIc
389    * N7m5ZjXndPNayJKVb4/T5fpeMfNYYQFq1ewazqkGtr1Wpeamat59rk9ffmGlqwiV
390    * r3yb08xCzRIvQD2BmfHSbSuHo/PI2WtVam6qZp5qHsufH3TrGMv5v3zd4xvNfHIB
391    * 6my/GdV8dtIZ79rHTW4uU82LQe0Mbl7vneUr35aYeRSvkfvLzJ0WoK7dUZJcvT3H
392    * zt7poFA8rx43ublqNad2EzOSzsx45Cb9n97ta2Mmqia4OeK5RpLtldhNrCjnGweC
393    * YtgPD396KeVUTW6uPdGc100Y0vZ8TpF47+50ia+Hnr5qcnP5ieakbsKQdogAkXW/
394    * bj6FTzWx7HQz6VdIk7kJQ9qH+pTqm8kN30tRTSw43YzHD0p9HV63DWopgyXkjMRX
395    * 8qg69101sVQ44+0zgEqH12ncBDNtu27/p5rgZt2mKe13AjdBTltNNcHNJBtFNZHJ
396    * zJV2q7izTv3thexVE9xUTZBzmT0rCn4gqj+z8bkmuJmzmj7XxCtmLrBzxdC7Vk1w
397    * M6G8zgaCIe2bySw8tvuenKrJzU2rmd5NMHOnZPY50V41wc3em2MeN7HLAG6BE/Za
398    * PxGJLM9FNbm5mJtNm2M2N7HjkDYWfHnPezR2K6omN7fYFhO6iR3NnHG3Uk1wc82J
399    * pmrCkDaXB5HxSakmNzecbsYSbsKQ1kRTNcHN4a8/VnET5DTRzCinanJz8Vc+rZtg
400    * pmqqJrj54BRTNUHO6asZ6cqsmtyc8eSDG1cmmNZNMHPvZP6vk/Xfs+6/FVWTm3Od
401    * eRDbuAlyZt+tOlezZDAc75upmtw8Fj4UNLOb2NLMY6Yz3btdEujWWPVtOVWTm8fa
402    * H6BM6yaYmX23ii53EaMfWDWxnZsdnuSEboKc2fesePex451nqprcnOhdP3ZyE8zc
403    * uppRuHSRauI9N4+1P0Z54oHZhJ3CGcPu9sYJeqUD225PVjW5OV04o/e9RUo3wcwd
404    * p5tRJ+CDq8mrJjdnfOOvWUm6TLK4vGZCPPwuAnLuNN2M6lGzasJ0c8wENO5/jfMV
405    * N7G9mccEZ7rHG/dWFU7VxCg3533vL89h6+Z7xE2Qc7vppmrCdDPhq432O1RNvCLn
406    * 2tWcZTyrmtycaFDb/mr7fGlTNcHMvs9qIjNVk5vrVfPoeKW8r7dSTZhu9t3xVRPc
407    * zPmCVRMTyjmJmfHuDZ1Di1fdXHW6GfO4CWbOZ2bFWQPR6+FUE9wc8IJVE6abw19J
408    * 9Psx1YRwvvuCVRPMfLmdMei026c+OFFNbq5dzf9eRTzx6QmbMMjMY9ZViqLHpbyK
409    * NplqgpuPvux0boKc65upmuAmNwFD2se314MfnKgmN7nZKZnexsDMDQazqml3K/mR
410    * sLFMNCGcJpqqaY/jpokmmKmaqgnTTdWEcDJTNcHNFzeQaiKFnNsPad/9ArVqcpOb
411    * ny9TMjGTmVvtgK8fAlJNbqpm0WtUTQjnbBNN1YRw5kymauJhMzcOp2piOjdjw22h
412    * mkhn5rHj4aBIoKVqcpObkglyqqZqgpudNoRqYiIz194fVRPcnGBbqCbymrnZdFM1
413    * wc0JNodq4r290WlBqgluzhZO1UT2ge2e5+ypJripmkDNHrbtme5vfI1aNdHipnCq
414    * Jgxp0043VRPcVE2QUzWLXuSoDaCa4GZON4GmIS0zVRPcVE0wk5mqCW5yE2iXc7eP
415    * T1764EQ10ejm+tX0oSZUc7bBrGpCNVUTzLz5I6qpmkjo5vrn0KomVJOZqgluqiaW
416    * k5OZqgluqibAzJtmDnzpqglu5nQTYKZqgpvcBJjZ5cWrJripmkCTnPtU870PTlQT
417    * 3PzzWnyoCWZOPdFUTXDzvdesmmDmbMlUTXAzZzJVE8zcLJmqCW5KJpi5UjJVE9zM
418    * mUzVBDP3S6ZqgpuSiYXMPJa63GW2Y7OqiV5ujthZ498/yarJGhjSbjnRVE2kdTMe
419    * kcFEE8yUTNXEC25232XjESUyuwnU7oKThzMqjVNN7OtmPGJFcjeBPmbOtc/m11I1
420    * kdDNF6opmSDntMlUTew+3XygmpHeTaBtR5wwnFMc/1FN9HUzRj5q7OQm0Lwvfgtn
421    * zPKSsmqpmujuZqOYMVgPycTW4eyyL/f9TlhMpaVqItugNoZJEpIJct71M779+fq/
422    * R+0dHlNpqZpI4mb5D0ez6ZKJneSMyinj3ZFvdLnD9FqqJh5zs72XJT8fnb91SRAs
423    * LeeNYWZtQaO8lzOcmKeaeMzN+P3nOTHncRPouo+O/RyyR2CPObRUTXBTMrGHnEfb
424    * SPXPbXtNSMe9WNUEN3dyExi5s946QHP5kcjdI0f1Vr+vpWqCm5KJLeU8savlI5ST
425    * G7Z+ZJpCS9UENyUTe5s59En1Ob8o0eUZVBPczOkmYLiX8XWpJrhpiomVduKgpWqC
426    * m5IJbLg3Z3whqgluSiYMbD1/1QQ395w0A3/37/CEVRN29c2eMLD4Hj+NkKoJbgK7
427    * KeopqSa4qZfA7eFtFC+uUPin4gnMtu1UE9wEuDrg6iPdF2xQTXCTmwBUEwAA1QQA
428    * QDUBAIBqAgCgmgAAqCYAAKoJAIBqAgCgmgAAQDUBAFBNAABUEwAA1QQAQDUBAFBN
429    * AABUUzUBAFBNAABUEwAA1QQAQDUBAFBNAABUEwAAqCYAAKoJAIBqAgCgmgAAqCYA
430    * AKoJAABUEwAA1QQAQDUBAFBNAABUEwAA1QQAQDUBAIBqAgCgmgAAqCYAAKoJAIBq
431    * AgCgmgAAQDUBAFBNAABUEwAA1QQAQDUBAFBNAABUEwAAqCYAAKoJAIBqAgCgmgAA
432    * qCYAAKoJAABUEwAA1QQAQDUBAFBNAABUEwAA1QQAQDUBAIBqAgCgmgAAqCYAAKoJ
433    * AIBqAgCgmgAAQDUBAFBNAABUEwAA1QQAQDUBAFBNAABU00YAAEA1AQBQTQAAVBMA
434    * ANUEAEA1AQBQTQAAoJoAAKgmAACqCQCAagIAoJoAAKgmAABQTQAAVBMAANUEAEA1
435    * AfzD/wFqNHlO4MJ0zwAAAABJRU5ErkJggg==" />
436    * 
437    * @author p.baniukiewicz
438    * @see OutlineProcessor#shrinknl(double, double, double, double, double, double, double, double)
439    * @see OutlineProcessor#amplificationFactor(double, double, double)
440    */
441   public static class Contour extends PropagateSeeds {
442 
443     /**
444      * Scale magnification. Used by {@link PropagateSeeds.Contour} only.
445      * 
446      * @see OutlineProcessor#shrinknl(double, double, double, double, double, double, double,
447      *      double)
448      */
449     public double scaleMagn = 5;
450     /**
451      * Sigma. Used by {@link PropagateSeeds.Contour} only.
452      * 
453      * @see OutlineProcessor#shrinknl(double, double, double, double, double, double, double,
454      *      double)
455      */
456     public double scaleSigma = 0.3;
457     /**
458      * Distance to set normals equal. Used by {@link PropagateSeeds.Contour} only.
459      * 
460      * @see OutlineProcessor#shrinknl(double, double, double, double, double, double, double,
461      *      double)
462      * @see Contour#Contour(boolean, ij.process.AutoThresholder.Method, double, double, double,
463      *      double)
464      */
465     public double averageNormalsDist = 0;
466     /**
467      * Distance to average curvature over. This is multiplier of average node distance. Always at
468      * least three nodes are averaged (n-1,n,n+1).
469      * 
470      * @see OutlineProcessor#shrinknl(double, double, double, double, double, double, double,
471      *      double)
472      * @see Contour#Contour(boolean, ij.process.AutoThresholder.Method, double, double, double,
473      *      double)
474      */
475     public double averageCurvDist = 1.0;
476     /**
477      * Use or not extra filtering defined in {@link TrackOutline}.
478      */
479     public boolean useFiltering = false;
480 
481     /**
482      * Step size during object outline shrinking.
483      * 
484      * @see Outline#scaleOutline(double, double, double, double)
485      * @see ANAp
486      */
487     public static final double stepSize = 0.04;
488 
489     /**
490      * Default constructor without storing seed history.
491      */
492     public Contour() {
493       this(false, null);
494     }
495 
496     /**
497      * Allow to store seed history that can be later presented in form of composite image.
498      * 
499      * @param storeSeeds <tt>true</tt> to store seeds.
500      * @param trueBackground if not null, selected method will be used for estimating true
501      *        background - excluding bright objects from it
502      * @see #getCompositeSeed(ImagePlus, int)
503      */
504     public Contour(boolean storeSeeds, AutoThresholder.Method trueBackground) {
505       super(storeSeeds, trueBackground);
506     }
507 
508     /**
509      * Allow additionally to set power of shrinking curved parts of the outline and window for
510      * equalising normals.
511      * 
512      * @param storeSeeds <tt>true</tt> to store seeds.
513      * @param trueBackground if not null, selected method will be used for estimating true
514      *        background - excluding bright objects from it
515      * @param sigma sigma of Gaussian
516      * @param scaleMagn maximum amplification of scaling index (for curv<<0). 1.0 causes linear
517      *        shrink.
518      * @param averageCurvDist distance to average curvature over. At any case at least 3 nodes are
519      *        taken (current, previous and next). See {@link OutlineProcessor}. This parameter plays
520      *        as multiplier of average node distance. Setting it to e.g. 5, likely 5 nodes will be
521      *        takes in total.
522      * @param averageNormalsDist distance to look along for node with smallest curvature (negative)
523      *        from current node (forward and backward). Set 0 to turn off this feature. See
524      *        {@link OutlineProcessor} and description of previous parameter.
525      * @see Contour#Contour(boolean, ij.process.AutoThresholder.Method)
526      * @see OutlineProcessor#shrinknl(double, double, double, double, double, double, double,
527      *      double)
528      */
529     public Contour(boolean storeSeeds, AutoThresholder.Method trueBackground, double sigma,
530             double scaleMagn, double averageCurvDist, double averageNormalsDist) {
531       super(storeSeeds, trueBackground);
532       this.scaleMagn = scaleMagn;
533       this.scaleSigma = sigma;
534       this.averageNormalsDist = averageNormalsDist;
535       this.averageCurvDist = averageCurvDist;
536     }
537 
538     /**
539      * Generate seeds for next frame using provided mask.
540      * 
541      * <p>The mask provided to this method is shrunk to get new seeds of object (that can move
542      * meanwhile). The same mask is expanded and subtracted from image forming the background.
543      * 
544      * <p>Setting <tt>shrinkPower</tt> or <tt>expandPower</tt> to zero prevents contour
545      * modifications.
546      * 
547      * <p>Algorithm can utilise object colour to process them in groups within one colour. Objects
548      * in one group are processed separately, but then all are stored in the same {@link Seeds}
549      * {@link SeedTypes#FOREGROUNDS} map.
550      * 
551      * @param previous Previous result of segmentation. BW mask with white object on black
552      *        background.
553      * @param org original image that new seeds are computed for. Usually it is current image
554      * @param shrinkPower Shrink size for objects in pixels.
555      * @param expandPower Expand size used to generate background (object is expanded and then
556      *        subtracted from background)
557      * @return List of background and foreground coordinates.
558      * @see PropagateSeeds.Morphological#propagateSeed(ImageProcessor, ImageProcessor, double,
559      *      double)
560      * @see Outline#scaleOutline(double, double, double, double)
561      * @see #getTrueBackground(ImageProcessor, ImageProcessor)
562      * @see #setTrueBackgroundProcessing(ij.process.AutoThresholder.Method)
563      */
564     @Override
565     public Seeds propagateSeed(ImageProcessor previous, ImageProcessor org, double shrinkPower,
566             double expandPower) {
567       double stepsshrink = shrinkPower / stepSize; // total shrink/step size
568       double stepsexp = (expandPower) / stepSize; // total shrink/step size
569       Seedsamics/quimp/plugin/randomwalk/Seeds.html#Seeds">Seeds ret = new Seeds(2);
570 
571       // this supports grayscales;
572       List<Pair<Outline, Color>> oc = getPairsOutlineAndColors(previous, useFiltering);
573       // sort according to colors, need to collect outlines with the same object color in the same
574       // map
575       Collections.sort(oc, new Comparator<Pair<Outline, Color>>() {
576 
577         @Override
578         public int compare(Pair<Outline, Color> o1, Pair<Outline, Color> o2) {
579           Color c1 = o1.getRight();
580           Color c2 = o2.getRight();
581           Integer ci1 = new Integer(c1.getRed() + c1.getBlue() + c1.getGreen());
582           Integer ci2 = new Integer(c2.getRed() + c2.getBlue() + c2.getGreen());
583           return ci1.compareTo(ci2);
584         }
585       });
586 
587       Iterator<Pair<Outline, Color>> oci = oc.iterator();
588 
589       // save extra debug info if property set
590       if (QuimP.SUPER_DEBUG) {
591         String tmp = System.getProperty("java.io.tmpdir");
592         for (Pair<Outline, Color> o : oc) {
593           long time = new Date().getTime();
594           RoiSaver.saveRoi(tmp + File.separator + "propagateSeed_" + time + "_" + oc.hashCode(),
595                   o.getLeft().asList());
596         }
597       }
598       int cprev = -1; // previous object color, IT CANT EXIST IN LIST
599       ByteProcessor small = null;
600       while (oci.hasNext()) {
601         Pair<Outline, Color> p = oci.next();
602         Outline o = p.getLeft();
603         Color c = p.getRight();
604         // restore int (see TrackOutline.getColors())
605         int color = c.getRed() + c.getBlue() + c.getGreen();
606         if (o.getNumPoints() < 4) {
607           continue;
608         }
609         if (color != cprev) { // if new color, create new processor (list is sorted)
610           small = new ByteProcessor(previous.getWidth(), previous.getHeight());
611         }
612         small.setColor(Color.WHITE); // for fill(Roi)
613         // shrink outline - copy as we want to expand it later
614         Outlinecs/quimp/Outline.html#Outline">Outline copy = new Outline(o);
615         LOGGER.debug("Shrink object");
616         new OutlineProcessor<Outline>(copy).shrinknl(stepsshrink, stepSize, 0.1, 1.5, scaleSigma,
617                 scaleMagn, averageCurvDist, averageNormalsDist);
618         copy.unfreezeAll();
619         Roi fr = copy.asIntRoi();
620         fr.setFillColor(Color.WHITE);
621         fr.setStrokeWidth(1.1);
622         fr.setStrokeColor(Color.WHITE);
623         small.fill(fr);
624         small.drawRoi(fr);
625         // small.resetRoi();
626         if (color != cprev) { // store only new, old will be updated by reference
627           ret.put(SeedTypes.FOREGROUNDS, small);
628         }
629         cprev = color; // for next iter
630       }
631 
632       ByteProcessor big = new ByteProcessor(previous.getWidth(), previous.getHeight());
633       // big.setColor(Color.BLACK);
634       // big.fill();
635       for (Pair<Outline, Color> p : oc) {
636         Outline o = p.getLeft();
637         if (o.getNumPoints() < 4) {
638           continue;
639         }
640         // frezeTh influences artifacts that appear when concave regions are expanded
641         // 0 prevent a little
642         LOGGER.debug("Expand object");
643         new OutlineProcessor<Outline>(o).shrinknl(stepsexp, -stepSize, 0.1, 0, scaleSigma,
644                 scaleMagn, 1.0, 0);
645         o.unfreezeAll();
646         Roi fr = o.asFloatRoi();
647         fr.setFillColor(Color.WHITE);
648         fr.setStrokeColor(Color.WHITE);
649         big.drawRoi(fr);
650       }
651       big.invert();
652       // store seeds if option ticked
653       ret.put(SeedTypes.BACKGROUND, getTrueBackground(big, org));
654       if (storeSeeds) {
655         seeds.add(ret);
656       }
657 
658       return ret;
659 
660     }
661 
662     /**
663      * Convert mask to outline.
664      * 
665      * @param previous image to be converted outline. White object on black background.
666      * @param filtering if true enable default filtering from {@link TrackOutline} class
667      * @return List of Outline for current frame and colors
668      * @see TrackOutline
669      * @see #getPairsOutlineAndColors(ImageProcessor, boolean)
670      */
671     public static Pair<List<Outline>, List<Color>> getOutlineAndColors(ImageProcessor previous,
672             boolean filtering) {
673       TrackOutline track = null;
674       if (filtering) {
675         track = new TrackOutline(previous, 0);
676       } else {
677         track = new TrackOutlinNoFilter(previous, 0);
678       }
679       return track.getOutlinesColors(STEPS, false);
680     }
681 
682     /**
683      * Convert mask to outline.
684      * 
685      * @param previous image to be converted outline. White object on black background.
686      * @param filtering if true enable default filtering from {@link TrackOutline} class
687      * @return List of Outline for current frame and colors enclosed in pairs
688      * @see TrackOutline
689      * @see #getOutlineAndColors(ImageProcessor, boolean)
690      */
691     public static List<Pair<Outline, Color>> getPairsOutlineAndColors(ImageProcessor previous,
692             boolean filtering) {
693       TrackOutline track = null;
694       if (filtering) {
695         track = new TrackOutline(previous, 0);
696       } else {
697         track = new TrackOutlinNoFilter(previous, 0);
698       }
699       return track.getPairs(STEPS, false);
700     }
701 
702   }
703 
704   /**
705    * Contain methods for propagating seeds to next frame using morphological operations.
706    * 
707    * @author p.baniukiewicz
708    *
709    */
710   public static class Morphological extends PropagateSeeds {
711 
712     /**
713      * Default constructor without storing seed history.
714      */
715     public Morphological() {
716       this(false, null);
717     }
718 
719     /**
720      * Allow to store seed history that can be later presented in form of composite image.
721      * 
722      * @param storeSeeds <tt>true</tt> to store seeds.
723      * @param trueBackground if not null, selected method will be used for estimating true
724      *        background - excluding bright objects from it
725      * @see #getCompositeSeed(ImagePlus, int)
726      */
727     public Morphological(boolean storeSeeds, AutoThresholder.Method trueBackground) {
728       super(storeSeeds, trueBackground);
729     }
730 
731     /**
732      * Generate new seeds using segmented image.
733      * 
734      * <p>Setting <tt>shrinkPower</tt> or <tt>expandPower</tt> to zero prevents contour
735      * modifications.
736      * 
737      * @param previous segmented image, background on zero
738      * @param org original image that new seeds are computed for. Usually it is current image
739      * @param shrinkPower number of erode iterations
740      * @param expandPower number of dilate iterations
741      * 
742      * @return Map containing list of coordinates that belong to foreground and background. Map is
743      *         addressed by two enums: <tt>FOREGROUND</tt> and <tt>BACKGROUND</tt>
744      * @see SeedProcessor#decodeSeedsfromRgb(ImagePlus, List, Color)
745      * @see #getTrueBackground(ImageProcessor, ImageProcessor)
746      * @see #setTrueBackgroundProcessing(ij.process.AutoThresholder.Method)
747      */
748     @Override
749     public Seeds propagateSeed(ImageProcessor previous, ImageProcessor org, double shrinkPower,
750             double expandPower) {
751       ImageProcessor cp = previous.duplicate();
752       // object smaller than on frame n
753       ImageProcessor small = cp.duplicate();
754       // object bigger than on frame n
755       ImageProcessor big = cp.duplicate();
756       // store seeds if option ticked
757       Seedsamics/quimp/plugin/randomwalk/Seeds.html#Seeds">Seeds ret = new Seeds(2);
758       // need to process each object separately to preserve multi fg seeds.
759       Seeds decodedSeeds;
760       try {
761         decodedSeeds = SeedProcessor.decodeSeedsfromGrayscaleImage(cp);
762         if (decodedSeeds.get(SeedTypes.FOREGROUNDS) == null) {
763           throw new RandomWalkException("no FG maps");
764         }
765         for (ImageProcessor ip : decodedSeeds.get(SeedTypes.FOREGROUNDS)) {
766           // make objects smaller
767           small = BinaryFilters.iterateMorphological(ip, MorphoOperations.ERODE, shrinkPower);
768           ret.put(SeedTypes.FOREGROUNDS, small);
769         }
770 
771         // make background bigger but for all, convert to binary first
772         big.threshold(0);
773         big = BinaryFilters.iterateMorphological(big, MorphoOperations.DILATE, expandPower);
774         cp.threshold(0); // input also must be binary (after computing FG)
775 
776         // apply big to old background making object bigger and prevent covering objects on
777         // frame
778         // n+1
779         // by previous background (make "empty" not seeded space around objects)
780         // IJ.saveAsTiff(new ImagePlus("", big), "/tmp/testIterateMorphological_bigbef.tif");
781         // IJ.saveAsTiff(new ImagePlus("", cp), "/tmp/testIterateMorphological_cp.tif");
782         for (int x = 0; x < cp.getWidth(); x++) {
783           for (int y = 0; y < cp.getHeight(); y++) {
784             big.putPixel(x, y, big.getPixel(x, y) | cp.getPixel(x, y));
785           }
786         }
787 
788         big.invert(); // invert to have BG pixels white in seed. (required by convertToList)
789         ret.put(SeedTypes.BACKGROUND, getTrueBackground(big, org));
790         if (storeSeeds) {
791           seeds.add(ret);
792         }
793       } catch (RandomWalkException e) { // from decodeseeds - no FG seeds
794         // this is handled to console only as return is still valid (no FG seeds in output)
795         LOGGER.debug("Empty seeds. " + e.getMessage());
796       }
797       return ret;
798     }
799 
800   }
801 
802   /**
803    * Produce composite image containing seeds generated during segmentation of particular frames.
804    * 
805    * <p>To have this method working, the Contour object must be created with storeSeeds==true.
806    * 
807    * @param org Original image (or stack) where composite layer will be added to.
808    * @param offset Slice number to display in composite if there is stack provided. Ignored if org
809    *        is single image. Set it to 0 to dispplay whole stack.
810    * @return Composite image with marked foreground and background.
811    * @throws RandomWalkException When seeds were not collected.
812    */
813   public ImagePlus getCompositeSeed(ImagePlus org, int offset) throws RandomWalkException {
814     ImagePlus ret = null;
815     try {
816       ImageStack smallstack =
817               new ImageStack(seeds.get(0).get(SeedTypes.FOREGROUNDS).get(0).getWidth(),
818                       seeds.get(0).get(SeedTypes.FOREGROUNDS).get(0).getHeight());
819       ImageStack bigstack =
820               new ImageStack(seeds.get(0).get(SeedTypes.FOREGROUNDS).get(0).getWidth(),
821                       seeds.get(0).get(SeedTypes.FOREGROUNDS).get(0).getHeight());
822 
823       for (Seeds p : seeds) {
824         // just in case convert to byte
825         ImageProcessor fg = SeedProcessor.flatten(p, SeedTypes.FOREGROUNDS, 1).convertToByte(true);
826         fg.threshold(0); // need 255 not real value of map
827         // why duplicate here? without returned map is at 128???????????????? Seems that
828         // converToByte returns duplicate only if input is not 8-bit
829         // (PropagateSeedsTest.testGetCompositeSeed_Morphological()
830         ImageProcessor bg = p.get(SeedTypes.BACKGROUND, 1).duplicate().convertToByte(true);
831         bg.threshold(0); // need 255 not real value of map
832         // make colors transparent
833         bg.multiply(colorScaling);
834         fg.multiply(colorScaling);
835         // set gray lut just in case
836         fg.setLut(IJTools.getGrayLut());
837         bg.setLut(IJTools.getGrayLut());
838         smallstack.addSlice((ImageProcessor) fg);
839         bigstack.addSlice((ImageProcessor) bg);
840       }
841       // check if stack or not. getComposite requires the same type
842       if (org.getStack().getSize() == 1) { // single image
843         ret = IJTools.getComposite(org.duplicate(), new ImagePlus("", smallstack.getProcessor(1)),
844                 new ImagePlus("", bigstack.getProcessor(1)));
845       } else {
846         if (offset > 0) { // stack but show only one image
847           ImageProcessor tmp = org.getStack().getProcessor(offset).duplicate();
848           ret = IJTools.getComposite(new ImagePlus("", tmp), new ImagePlus("", smallstack),
849                   new ImagePlus("", bigstack));
850         } else { // stack
851           ret = IJTools.getComposite(org.duplicate(), new ImagePlus("", smallstack),
852                   new ImagePlus("", bigstack));
853         }
854       }
855     } catch (NullPointerException | IndexOutOfBoundsException e) {
856       throw new RandomWalkException("Problem with showing seeds. Seeds were not stored. "
857               + "You need at least two time frames to collect one seed or "
858               + "segmentation returned empty image.");
859     }
860     return ret;
861   }
862 
863   /**
864    * Propagate seed.
865    * 
866    * <p>Each separated object found in <tt>previous</tt> is returned as separate BW map in
867    * {@link Seeds} under {@link SeedTypes#FOREGROUNDS} map. <tt>previous</tt> can contain grayscale
868    * labelled objects.
869    *
870    * @param previous the previous
871    * @param org original image that new seeds are computed for. Usually it is current image
872    * @param shrinkPower the shrink power
873    * @param expandPower the expand power
874    * @return the map
875    * @see #getTrueBackground(ImageProcessor, ImageProcessor)
876    * @see #setTrueBackgroundProcessing(ij.process.AutoThresholder.Method)
877    */
878   public abstract Seeds propagateSeed(ImageProcessor previous, ImageProcessor org,
879           double shrinkPower, double expandPower);
880 
881   /**
882    * Excludes objects from estimated background.
883    * 
884    * <p>If seed propagator is used, background is obtained by expanding initially segmented cell and
885    * then negating the image. Thus background covers all area except cell. If there are other cell
886    * there they can influence background mean. To avoid this, that background is thresholded to
887    * detect objects that should be excluded from mean. This should be used when there are other
888    * objects around.
889    * 
890    * @param bck Background (white) estimated from Propagator
891    * @param org Original 8-bit image
892    * @return Background without objects above threshold
893    * @see #setTrueBackgroundProcessing(ij.process.AutoThresholder.Method)
894    */
895   ImageProcessor getTrueBackground(ImageProcessor bck, ImageProcessor org) {
896     if (thresholdMethod == null) {
897       return bck;
898     }
899     ImageProcessor orgD = org.duplicate();
900     orgD.threshold(new AutoThresholder().getThreshold(thresholdMethod, orgD.getHistogram()));
901     orgD.invert();
902     orgD.copyBits(bck, 0, 0, Blitter.AND); // cut
903     return orgD;
904   }
905 
906   /**
907    * Turn on processing background before using it as seed.
908    * 
909    * @param method Threshold method. null for turning off processing
910    */
911   public void setTrueBackgroundProcessing(AutoThresholder.Method method) {
912     thresholdMethod = method;
913   }
914 
915 }
916 
917 /**
918  * Disabled preliminary filtering default for {@link TrackOutline}.
919  * 
920  * <p>In purpose of overriding {@link TrackOutline#prepare()} which in super class can remove thin
921  * lines.
922  * 
923  * @author p.baniukiewicz
924  *
925  */
926 class TrackOutlinNoFilter extends TrackOutline {
927 
928   /**
929    * Default constructor here.
930    * 
931    * @param imp image to process
932    * @param background background color
933    */
934   public TrackOutlinNoFilter(ImageProcessor imp, int background) {
935     super(imp, background);
936   }
937 
938   /*
939    * (non-Javadoc)
940    * 
941    * @see com.github.celldynamics.quimp.geom.TrackOutline#prepare()
942    */
943   @Override
944   public ImageProcessor prepare() {
945     ImageProcessor filtered = imp.duplicate();
946     return filtered;
947   }
948 
949 }