14
14
15
15
import java .util .ArrayList ;
16
16
import java .util .List ;
17
+ import java .util .stream .Collectors ;
18
+ import java .util .stream .Stream ;
17
19
18
20
import static org .bytedeco .javacpp .opencv_core .CV_32SC1 ;
19
21
import static org .bytedeco .javacpp .opencv_core .CV_8UC1 ;
24
26
import static org .bytedeco .javacpp .opencv_core .Point ;
25
27
import static org .bytedeco .javacpp .opencv_core .Point2f ;
26
28
import static org .bytedeco .javacpp .opencv_core .Scalar ;
29
+ import static org .bytedeco .javacpp .opencv_core .bitwise_xor ;
27
30
import static org .bytedeco .javacpp .opencv_imgproc .CV_CHAIN_APPROX_TC89_KCOS ;
28
31
import static org .bytedeco .javacpp .opencv_imgproc .CV_FILLED ;
29
32
import static org .bytedeco .javacpp .opencv_imgproc .CV_RETR_EXTERNAL ;
@@ -64,12 +67,22 @@ public class WatershedOperation implements Operation {
64
67
private final InputSocket <ContoursReport > contoursSocket ;
65
68
private final OutputSocket <ContoursReport > outputSocket ;
66
69
70
+ private static final int MAX_MARKERS = 253 ;
71
+ private final List <Mat > markerPool ;
72
+ private final MatVector contour = new MatVector (); // vector with a single element
73
+ private final Mat markers = new Mat ();
74
+ private final Mat output = new Mat ();
75
+ private final Point backgroundLabel = new Point ();
76
+
67
77
@ SuppressWarnings ("JavadocMethod" )
68
78
public WatershedOperation (InputSocket .Factory inputSocketFactory ,
69
79
OutputSocket .Factory outputSocketFactory ) {
70
80
srcSocket = inputSocketFactory .create (srcHint );
71
81
contoursSocket = inputSocketFactory .create (contoursHint );
72
82
outputSocket = outputSocketFactory .create (outputHint );
83
+ markerPool = ImmutableList .copyOf (
84
+ Stream .generate (Mat ::new ).limit (MAX_MARKERS ).collect (Collectors .toList ())
85
+ );
73
86
}
74
87
75
88
@ Override
@@ -97,54 +110,50 @@ public void perform() {
97
110
final ContoursReport contourReport = contoursSocket .getValue ().get ();
98
111
final MatVector contours = contourReport .getContours ();
99
112
100
- final int maxMarkers = 253 ;
101
- if (contours .size () > maxMarkers ) {
113
+ if (contours .size () > MAX_MARKERS ) {
102
114
throw new IllegalArgumentException (
103
- "A maximum of " + maxMarkers + " contours can be used as markers."
115
+ "A maximum of " + MAX_MARKERS + " contours can be used as markers."
104
116
+ " Filter contours before connecting them to this operation if this keeps happening."
105
117
+ " The contours must also all be external; nested contours will not work" );
106
118
}
107
119
108
- final Mat markers = new Mat (input .size (), CV_32SC1 , new Scalar (0.0 ));
109
- final Mat output = new Mat (markers .size (), CV_8UC1 , new Scalar (0.0 ));
120
+ markers .create (input .size (), CV_32SC1 );
121
+ output .create (input .size (), CV_8UC1 );
122
+ bitwise_xor (markers , markers , markers );
123
+ bitwise_xor (output , output , output );
110
124
111
- try {
112
- // draw foreground markers (these have to be different colors)
113
- for (int i = 0 ; i < contours .size (); i ++) {
114
- drawContours (markers , contours , i , Scalar .all (i + 1 ), CV_FILLED , LINE_8 , null , 2 , null );
115
- }
125
+ // draw foreground markers (these have to be different colors)
126
+ for (int i = 0 ; i < contours .size (); i ++) {
127
+ drawContours (markers , contours , i , Scalar .all (i + 1 ), CV_FILLED , LINE_8 , null , 2 , null );
128
+ }
116
129
117
- // draw background marker a different color from the foreground markers
118
- Point backgroundLabel = fromPoint2f (findBackgroundMarker (markers , contours ));
119
- circle (markers , backgroundLabel , 1 , Scalar .WHITE , -1 , LINE_8 , 0 );
120
-
121
- // Perform watershed
122
- watershed (input , markers );
123
- markers .convertTo (output , CV_8UC1 );
124
-
125
- List <Mat > contourList = new ArrayList <>();
126
- for (int i = 1 ; i < contours .size (); i ++) {
127
- Mat dst = new Mat ();
128
- output .copyTo (dst , opencv_core .equals (markers , i ).asMat ());
129
- MatVector contour = new MatVector (); // vector with a single element
130
- findContours (dst , contour , CV_RETR_EXTERNAL , CV_CHAIN_APPROX_TC89_KCOS );
131
- assert contour .size () == 1 ;
132
- contourList .add (contour .get (0 ).clone ());
133
- contour .get (0 ).deallocate ();
134
- contour .deallocate ();
130
+ // draw background marker a different color from the foreground markers
131
+ findBackgroundMarker (markers , contours );
132
+ circle (markers , backgroundLabel , 1 , Scalar .WHITE , -1 , LINE_8 , 0 );
133
+
134
+ // Perform watershed
135
+ watershed (input , markers );
136
+ markers .convertTo (output , CV_8UC1 );
137
+
138
+ List <Mat > contourList = new ArrayList <>((int ) contours .size ());
139
+ for (int i = 1 ; i < contours .size (); i ++) {
140
+ Mat dst = markerPool .get (i - 1 );
141
+ bitwise_xor (dst , dst , dst );
142
+ output .copyTo (dst , opencv_core .equals (markers , i ).asMat ());
143
+ findContours (dst , contour , CV_RETR_EXTERNAL , CV_CHAIN_APPROX_TC89_KCOS );
144
+ if (contour .size () < 1 ) {
145
+ throw new IllegalArgumentException ("No contours for marker" );
135
146
}
136
- MatVector foundContours = new MatVector (contourList .toArray (new Mat [contourList .size ()]));
137
- outputSocket .setValue (new ContoursReport (foundContours , output .rows (), output .cols ()));
138
- } finally {
139
- // make sure that the working mat is freed to avoid a memory leak
140
- markers .release ();
147
+ contourList .add (contour .get (0 ).clone ());
141
148
}
149
+ MatVector foundContours = new MatVector (contourList .toArray (new Mat [contourList .size ()]));
150
+ outputSocket .setValue (new ContoursReport (foundContours , output .rows (), output .cols ()));
142
151
}
143
152
144
153
/**
145
154
* Finds the first available point to place a background marker for the watershed operation.
146
155
*/
147
- private static Point2f findBackgroundMarker (Mat markers , MatVector contours ) {
156
+ private void findBackgroundMarker (Mat markers , MatVector contours ) {
148
157
final int cols = markers .cols ();
149
158
final int rows = markers .rows ();
150
159
final int minDist = 5 ;
@@ -169,13 +178,16 @@ private static Point2f findBackgroundMarker(Mat markers, MatVector contours) {
169
178
}
170
179
if (!found ) {
171
180
// Should only happen if the image is clogged with contours
181
+ backgroundLabel .deallocate ();
172
182
throw new IllegalStateException ("Could not find a point for the background label" );
173
183
}
174
- return backgroundLabel ;
184
+ setBackgroundLabel (backgroundLabel );
185
+ backgroundLabel .deallocate ();
175
186
}
176
187
177
- private static Point fromPoint2f (Point2f p ) {
178
- return new Point ((int ) p .x (), (int ) p .y ());
188
+ private void setBackgroundLabel (Point2f p ) {
189
+ this .backgroundLabel .x ((int ) p .x ());
190
+ this .backgroundLabel .y ((int ) p .y ());
179
191
}
180
192
181
193
}
0 commit comments