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 }