Skip to content

Commit

Permalink
Added structures for holding grayscale maps.
Browse files Browse the repository at this point in the history
Introduced seed maps and probability maps and rewritten core processors
to support them.

Task: #284 (http://github.com/CellDynamics/QuimP/issues/284)
Comments

Changed storage for seed maps to modified HashMap.

Accept multiple maps for one type of seeds.

Added maps

Solver process only fg maps

Support for multicell in most of RW internal methods

Set bck iter number dependent on fg

Propagate seeds supports muticolor images

Fixed decodeSeeds

Tests for multi color seeds and some todos

Verified multi seed segmentation

Added simple UI for selecting seeds by ROIs. New TODOs

Refactored and cleaned seeds
  • Loading branch information
CellDynamics committed Jan 10, 2018
1 parent 7c96396 commit c4aa3b7
Show file tree
Hide file tree
Showing 32 changed files with 1,792 additions and 583 deletions.
2 changes: 0 additions & 2 deletions src/main/java/ANA_Runner.java
Expand Up @@ -3,8 +3,6 @@
/**
* Compatibility layer for ImageJ.
*
* <p>Runnable classes packaged cause problems in pure ImageJ.
*
* @author p.baniukiewicz
*
*/
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/BOA_Runner.java
Expand Up @@ -3,8 +3,6 @@
/**
* Compatibility layer for ImageJ.
*
* <p>Runnable classes packaged cause problems in pure ImageJ.
*
* @author p.baniukiewicz
*
*/
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/DicLidReconstruction_Runner.java
Expand Up @@ -3,8 +3,6 @@
/**
* Compatibility layer for ImageJ.
*
* <p>Runnable classes packaged cause problems in pure ImageJ.
*
* @author p.baniukiewicz
*
*/
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/ECMM_MappingRunner.java
Expand Up @@ -3,8 +3,6 @@
/**
* Compatibility layer for ImageJ.
*
* <p>Runnable classes packaged cause problems in pure ImageJ.
*
* @author p.baniukiewicz
*
*/
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/GenerateMask_Runner.java
Expand Up @@ -3,8 +3,6 @@
/**
* Compatibility layer for ImageJ.
*
* <p>Runnable classes packaged cause problems in pure ImageJ.
*
* @author p.baniukiewicz
*
*/
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/Prot_AnalysisRunner.java
Expand Up @@ -3,8 +3,6 @@
/**
* Compatibility layer for ImageJ.
*
* <p>Runnable classes packaged cause problems in pure ImageJ.
*
* @author p.baniukiewicz
*
*/
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/Q_AnalysisRunner.java
Expand Up @@ -3,8 +3,6 @@
/**
* Compatibility layer for ImageJ.
*
* <p>Runnable classes packaged cause problems in pure ImageJ.
*
* @author p.baniukiewicz
*
*/
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/QuimP_BarRunner.java
Expand Up @@ -3,8 +3,6 @@
/**
* Compatibility layer for ImageJ.
*
* <p>Runnable classes packaged cause problems in pure ImageJ.
*
* @author p.baniukiewicz
*
*/
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/RandomWalkSegmentationPlugin_Runner.java
Expand Up @@ -3,8 +3,6 @@
/**
* Compatibility layer for ImageJ.
*
* <p>Runnable classes packaged cause problems in pure ImageJ.
*
* @author p.baniukiewicz
*
*/
Expand Down
@@ -0,0 +1,58 @@
/**
*
*/
package com.github.celldynamics.quimp.plugin.randomwalk;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import com.github.celldynamics.quimp.plugin.randomwalk.RandomWalkSegmentation.SeedTypes;

/**
* Store objects (maps) under specified key. There are many maps allowed under one key.
*
* <p>For particular key, maps are stored in ArrayList structure.
*
* @author p.baniukiewicz
* @param <T> type of map stored
*
*/
@SuppressWarnings("serial")
public class ListMap<T> extends HashMap<SeedTypes, List<T>> {

/**
*
*/
public ListMap() {
super();
}

/**
* @param initialCapacity
*/
public ListMap(int initialCapacity) {
super(initialCapacity);
}

/**
* Store specified map in this structure.
*
* <p>This method does not verify if this particular map is already present.
*
* @param key key to store map under, will be created if necessary
* @param val map to store under specified key
* @return List of maps where specified seed map has been stored
*/
public List<T> put(SeedTypes key, T val) {
List<T> loc = get(key);
if (loc != null) {
loc.add(val);
} else {
put(key, new ArrayList<>());
loc = get(key);
loc.add(val);
}
return loc;
}
}
@@ -0,0 +1,62 @@
package com.github.celldynamics.quimp.plugin.randomwalk;

import java.util.List;
import java.util.ListIterator;

import org.apache.commons.math3.linear.RealMatrix;

import com.github.celldynamics.quimp.plugin.randomwalk.RandomWalkSegmentation.SeedTypes;

/**
* Store probability maps computed for foreground and background objects.
*
* <p>Allow to keep many maps for FG and BG, e.g. when there are more than one object that should be
* handled separately. For given {@link SeedTypes} key maps are stored in ArrayList.
*
* @author p.baniukiewicz
* @see Seeds
*/
@SuppressWarnings("serial")
public class ProbabilityMaps extends ListMap<RealMatrix> {

/**
*
*/
public ProbabilityMaps() {
super();
}

/**
* Convert list of maps under specified key to 3d array of doubles.
*
* <p>Data in output array are references. Require the same size of all maps within specified key.
*
* @param key which map to convert
* @return 3d array [map][width][height] or null if there is no maps under specified key
* @throws IllegalArgumentException if size is not equal
*/
public double[][][] convertTo3dMatrix(Object key) {
List<RealMatrix> maps = get(key);
// Can be null if points not found
if (maps == null || maps.isEmpty()) {
return null;
}
// assume all maps have the same resolution
int width = maps.get(0).getColumnDimension();
int height = maps.get(0).getRowDimension();
int depth = maps.size();
for (int i = 1; i < maps.size(); i++) {
RealMatrix tmp = maps.get(i);
if (tmp.getRowDimension() != height || tmp.getColumnDimension() != width) {
throw new IllegalArgumentException("All maps must have the same resoultion");
}
}
double[][][] ret = new double[depth][][];
ListIterator<RealMatrix> it = maps.listIterator();
while (it.hasNext()) {
ret[it.nextIndex()] = it.next().getData();
}

return ret;
}
}
Expand Up @@ -4,9 +4,7 @@
import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -17,7 +15,7 @@
import com.github.celldynamics.quimp.geom.filters.OutlineProcessor;
import com.github.celldynamics.quimp.plugin.ana.ANAp;
import com.github.celldynamics.quimp.plugin.randomwalk.BinaryFilters.MorphoOperations;
import com.github.celldynamics.quimp.plugin.randomwalk.RandomWalkSegmentation.Seeds;
import com.github.celldynamics.quimp.plugin.randomwalk.RandomWalkSegmentation.SeedTypes;
import com.github.celldynamics.quimp.utils.IJTools;
import com.github.celldynamics.quimp.utils.test.RoiSaver;

Expand Down Expand Up @@ -128,7 +126,7 @@ public PropagateSeeds(boolean storeSeeds, AutoThresholder.Method trueBackground)
* @see #getCompositeSeed(ImagePlus, int)
* @see PropagateSeeds#storeSeeds
*/
protected List<Map<Seeds, ImageProcessor>> seeds;
protected List<Seeds> seeds;
/**
* Scale color values in composite preview.
*
Expand Down Expand Up @@ -196,8 +194,8 @@ public Dummy(boolean storeSeeds) {
*
*/
@Override
public Map<Seeds, ImageProcessor> propagateSeed(ImageProcessor previous, ImageProcessor org,
double shrinkPower, double expandPower) {
public Seeds propagateSeed(ImageProcessor previous, ImageProcessor org, double shrinkPower,
double expandPower) {
return binary.propagateSeed(previous, org, 0, 0);
}

Expand Down Expand Up @@ -538,8 +536,8 @@ public Contour(boolean storeSeeds, AutoThresholder.Method trueBackground, double
* @see #setTrueBackgroundProcessing(ij.process.AutoThresholder.Method)
*/
@Override
public Map<Seeds, ImageProcessor> propagateSeed(ImageProcessor previous, ImageProcessor org,
double shrinkPower, double expandPower) {
public Seeds propagateSeed(ImageProcessor previous, ImageProcessor org, double shrinkPower,
double expandPower) {
ByteProcessor small = new ByteProcessor(previous.getWidth(), previous.getHeight());
ByteProcessor big = new ByteProcessor(previous.getWidth(), previous.getHeight());
small.setColor(Color.BLACK);
Expand Down Expand Up @@ -598,9 +596,9 @@ public Map<Seeds, ImageProcessor> propagateSeed(ImageProcessor previous, ImagePr
}
big.invert();
// store seeds if option ticked
Map<Seeds, ImageProcessor> ret = new HashMap<Seeds, ImageProcessor>(2);
ret.put(Seeds.FOREGROUND, small);
ret.put(Seeds.BACKGROUND, getTrueBackground(big, org));
Seeds ret = new Seeds(2);
ret.put(SeedTypes.FOREGROUNDS, small);
ret.put(SeedTypes.BACKGROUND, getTrueBackground(big, org));
if (storeSeeds) {
seeds.add(ret);
}
Expand Down Expand Up @@ -663,13 +661,13 @@ public Morphological(boolean storeSeeds, AutoThresholder.Method trueBackground)
*
* @return Map containing list of coordinates that belong to foreground and background. Map is
* addressed by two enums: <tt>FOREGROUND</tt> and <tt>BACKGROUND</tt>
* @see RandomWalkSegmentation#decodeSeeds(ImagePlus, Color, Color)
* @see SeedProcessor#decodeSeedsfromRgb(ImagePlus, List, Color)
* @see #getTrueBackground(ImageProcessor, ImageProcessor)
* @see #setTrueBackgroundProcessing(ij.process.AutoThresholder.Method)
*/
@Override
public Map<Seeds, ImageProcessor> propagateSeed(ImageProcessor previous, ImageProcessor org,
double shrinkPower, double expandPower) {
public Seeds propagateSeed(ImageProcessor previous, ImageProcessor org, double shrinkPower,
double expandPower) {
ImageProcessor cp = previous;
// object smaller than on frame n
ImageProcessor small = cp.duplicate();
Expand All @@ -694,9 +692,9 @@ public Map<Seeds, ImageProcessor> propagateSeed(ImageProcessor previous, ImagePr

big.invert(); // invert to have BG pixels white in seed. (required by convertToList)
// store seeds if option ticked
Map<Seeds, ImageProcessor> ret = new HashMap<Seeds, ImageProcessor>(2);
ret.put(Seeds.FOREGROUND, small);
ret.put(Seeds.BACKGROUND, getTrueBackground(big, org));
Seeds ret = new Seeds(2);
ret.put(SeedTypes.FOREGROUNDS, small);
ret.put(SeedTypes.BACKGROUND, getTrueBackground(big, org));
if (storeSeeds) {
seeds.add(ret);
}
Expand All @@ -723,15 +721,17 @@ public ImagePlus getCompositeSeed(ImagePlus org, int offset) throws RandomWalkEx
throw new RandomWalkException(
"Seeds were not stored. You need at least two time frames to collect one seed");
}
ImageStack smallstack = new ImageStack(seeds.get(0).get(Seeds.FOREGROUND).getWidth(),
seeds.get(0).get(Seeds.FOREGROUND).getHeight());
ImageStack bigstack = new ImageStack(seeds.get(0).get(Seeds.FOREGROUND).getWidth(),
seeds.get(0).get(Seeds.FOREGROUND).getHeight());
ImageStack smallstack =
new ImageStack(seeds.get(0).get(SeedTypes.FOREGROUNDS).get(0).getWidth(),
seeds.get(0).get(SeedTypes.FOREGROUNDS).get(0).getHeight());
ImageStack bigstack = new ImageStack(seeds.get(0).get(SeedTypes.FOREGROUNDS).get(0).getWidth(),
seeds.get(0).get(SeedTypes.FOREGROUNDS).get(0).getHeight());

for (Map<Seeds, ImageProcessor> p : seeds) {
for (Seeds p : seeds) {
// just in case convert to byte
ImageProcessor fg = (ImageProcessor) p.get(Seeds.FOREGROUND).convertToByte(true);
ImageProcessor bg = (ImageProcessor) p.get(Seeds.BACKGROUND).convertToByte(true);
// FIXME compile foregorunds from all images
ImageProcessor fg = (ImageProcessor) p.get(SeedTypes.FOREGROUNDS, 1).convertToByte(true);
ImageProcessor bg = (ImageProcessor) p.get(SeedTypes.BACKGROUND, 1).convertToByte(true);
// make colors transparent
bg.multiply(colorScaling);
fg.multiply(colorScaling);
Expand Down Expand Up @@ -769,8 +769,8 @@ public ImagePlus getCompositeSeed(ImagePlus org, int offset) throws RandomWalkEx
* @see #getTrueBackground(ImageProcessor, ImageProcessor)
* @see #setTrueBackgroundProcessing(ij.process.AutoThresholder.Method)
*/
public abstract Map<Seeds, ImageProcessor> propagateSeed(ImageProcessor previous,
ImageProcessor org, double shrinkPower, double expandPower);
public abstract Seeds propagateSeed(ImageProcessor previous, ImageProcessor org,
double shrinkPower, double expandPower);

/**
* Excludes objects from estimated background.
Expand Down Expand Up @@ -809,7 +809,7 @@ public void setTrueBackgroundProcessing(AutoThresholder.Method method) {
}

/**
* In purpose of overriding {@link TrackOutline#prepare()} which in super class can remove this
* In purpose of overriding {@link TrackOutline#prepare()} which in super class can remove thin
* lines.
*
* @author p.baniukiewicz
Expand Down
Expand Up @@ -69,7 +69,7 @@ public String[] getShrinkMethods() {
*
* @return array of filters
* @see BinaryFilters
* @see RandomWalkSegmentation#run(java.util.Map)
* @see RandomWalkSegmentation#run(Seeds)
* @see RandomWalkSegmentation
*/
public String[] getFilteringMethods() {
Expand Down
Expand Up @@ -4,7 +4,7 @@

import org.apache.commons.lang3.ArrayUtils;

import com.github.celldynamics.quimp.plugin.randomwalk.RandomWalkSegmentation.Seeds;
import com.github.celldynamics.quimp.plugin.randomwalk.RandomWalkSegmentation.SeedTypes;

/**
* Hold algorithm parameters.
Expand Down Expand Up @@ -52,7 +52,7 @@ public class RandomWalkOptions {
* true if local mean algorithm is used. false if global mean for seeds is computed.
*
* <p>If localMean is used, the seeds provided to
* {@link RandomWalkSegmentation#run(java.util.Map)} must have {@link Seeds#ROUGHMASK} entry.
* {@link RandomWalkSegmentation#run(Seeds)} must have {@link SeedTypes#ROUGHMASK} entry.
*/
public boolean useLocalMean;

Expand Down

0 comments on commit c4aa3b7

Please sign in to comment.