View Javadoc
1   package com.github.celldynamics.quimp.plugin.randomwalk;
2   
3   import java.awt.Color;
4   import java.util.ArrayList;
5   import java.util.List;
6   
7   import org.apache.commons.math3.linear.RealMatrix;
8   
9   import com.github.celldynamics.quimp.plugin.randomwalk.RandomWalkSegmentation.SeedTypes;
10  
11  import ij.ImagePlus;
12  import ij.ImageStack;
13  import ij.process.ImageProcessor;
14  
15  /**
16   * Keep information about seeds for different Seed types.
17   * 
18   * <p>Seed type specifies type of object scribbled by user ({@link SeedTypes}, seed map is an binary
19   * image of size of scribbled (original) image where pixels selected by user are white.
20   * 
21   * <p>Each seed map is stored as binary ImageProcessor but it is assumed that for one key
22   * ({@link SeedTypes}) there can be several separate seeds (e.g. for different objects that should
23   * be treated separately). This is common case for e.g. {@link SeedTypes#FOREGROUNDS}.
24   * 
25   * <p>Internally each seed image is stored in {@link ImageStack} structure, note that <i>put</i>
26   * method does not verify is there is the same slice in stack already. Seed types other than
27   * {@link SeedTypes#FOREGROUNDS} usually have only one seed map associated, but it is also stored
28   * in {@link ImageStack}. One needs to take care about proper unwrapping data if standard
29   * {@link #get(Object)} is used.
30   * 
31   * <p>Seed maps can be converted from various sources like RGB images or grayscale images but maps
32   * are always binary.
33   * 
34   * @author p.baniukiewicz
35   * @see SeedProcessor
36   */
37  @SuppressWarnings("serial")
38  public class Seeds extends ListMap<ImageProcessor> {
39  
40    /**
41     * Default constructor.
42     */
43    public Seeds() {
44      super();
45    }
46  
47    /**
48     * Allow to set initial capacity.
49     * 
50     * @param initialCapacity initial capacity
51     */
52    public Seeds(int initialCapacity) {
53      super(initialCapacity);
54    }
55  
56    /**
57     * Retrieve specified seed map from specified key.
58     * 
59     * @param key key to retrieve
60     * @param slice seed map number (starts from 1)
61     * @return specified map or null if not available (neither key nor slice number)
62     */
63    public ImageProcessor get(SeedTypes key, int slice) {
64      slice--;
65      List<ImageProcessor> tmp = super.get(key);
66      if (tmp == null) {
67        return null;
68      }
69      if (slice < 0 || slice >= tmp.size()) {
70        return null;
71      }
72      return tmp.get(slice);
73  
74    }
75  
76    /**
77     * Convert binary maps representing foregrounds and background seeds to lists of coordinates
78     * (format accepted by {@link RandomWalkSegmentation#solver(Seeds, RealMatrix[])}).
79     * 
80     * <p>Assumes that there can be more labels for each key (e.g. few objects labelled as foreground
81     * that will be treated separately). Each label for specified key is converted to list of points
82     * separately as well.
83     * 
84     * @param key which map to convert
85     * @return List of point coordinates accepted by RW algorithm for each label within specified key.
86     *         If there is more labels for e.g. FOREGROUND key, each is converted to list of points
87     *         separately. If seed map is empty (black) or key does not
88     *         exist an empty list is returned.
89     * @see SeedProcessor#decodeSeedsfromRgb(ImagePlus, List, Color)
90     * @see SeedProcessor#decodeSeedsfromRgb(ImageProcessor, List, Color)
91     */
92    public List<List<Point>> convertToList(SeedTypes key) {
93      List<ImageProcessor> seeds = get(key);
94      // output map integrating two lists of points
95      List<List<Point>> out = new ArrayList<>();
96      // Can be empty if points not found
97      if (seeds == null || seeds.isEmpty()) {
98        return out;
99      }
100 
101     for (int s = 0; s < seeds.size(); s++) {
102       ImageProcessor slice = seeds.get(s);
103       List<Point> points = new ArrayList<>();
104       for (int x = 0; x < slice.getWidth(); x++) {
105         for (int y = 0; y < slice.getHeight(); y++) {
106           if (slice.get(x, y) > 0) {
107             points.add(new Point(x, y)); // remember foreground coords
108           }
109         }
110       }
111       out.add(points);
112     }
113     return out;
114   }
115 
116   /**
117    * Convert seeds to ImageStack.
118    * 
119    * @param key which map to convert
120    * @return Stack of seeds for selected key or null if key does not exist or there aren't maps
121    *         under it.
122    */
123   public ImageStack convertToStack(SeedTypes key) {
124     List<ImageProcessor> seeds = get(key);
125     if (seeds == null || seeds.isEmpty()) {
126       return null;
127     }
128     ImageProcessor tmp = seeds.get(0);
129     ImageStack ret = new ImageStack(tmp.getWidth(), tmp.getHeight());
130     for (ImageProcessor ip : seeds) {
131       ret.addSlice(ip);
132     }
133     return ret;
134   }
135 
136   /*
137    * (non-Javadoc)
138    * 
139    * @see java.lang.Object#toString()
140    */
141   @Override
142   public String toString() {
143     return super.toString();
144   }
145 
146 }