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 }