Skip to content

Commit 4dda181

Browse files
committed
Add AdvancedFilterCountoursOperation
Fixes issue #441
1 parent 20ec054 commit 4dda181

2 files changed

Lines changed: 116 additions & 0 deletions

File tree

core/src/main/java/edu/wpi/grip/core/operations/Operations.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public static void addOperations(EventBus eventBus) {
2727
eventBus.post(new OperationAddedEvent(new HSLThresholdOperation()));
2828
eventBus.post(new OperationAddedEvent(new FindContoursOperation()));
2929
eventBus.post(new OperationAddedEvent(new FilterContoursOperation()));
30+
eventBus.post(new OperationAddedEvent(new AdvancedFilterContoursOperation()));
3031
eventBus.post(new OperationAddedEvent(new ConvexHullsOperation()));
3132
eventBus.post(new OperationAddedEvent(new FindBlobsOperation()));
3233
eventBus.post(new OperationAddedEvent(new FindLinesOperation()));
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package edu.wpi.grip.core.operations.composite;
2+
3+
import com.google.common.eventbus.EventBus;
4+
import edu.wpi.grip.core.*;
5+
6+
import java.io.InputStream;
7+
import java.util.List;
8+
import java.util.Optional;
9+
10+
import static org.bytedeco.javacpp.opencv_core.*;
11+
import static org.bytedeco.javacpp.opencv_imgproc.*;
12+
13+
/**
14+
* An {@link Operation} that takes in a list of contours and outputs a list of any contours in the input that match
15+
* all of several criteria. Right now, the user can specify a minimum area, minimum perimeter, and ranges for width
16+
* and height.
17+
* <p>
18+
* This is useful because running a Find Contours on a real-life image typically leads to many small undesirable
19+
* contours from noise and small objects, as well as contours that do not meet the expected characteristics of the
20+
* feature we're actually looking for. So, this operation can help narrow them down.
21+
*/
22+
public class AdvancedFilterContoursOperation implements Operation {
23+
24+
private final SocketHint<ContoursReport> contoursHint = new SocketHint.Builder<>(ContoursReport.class)
25+
.identifier("Contours").initialValueSupplier(ContoursReport::new).build();
26+
27+
private final SocketHint<Number> minVertexHint =
28+
SocketHints.Inputs.createNumberSpinnerSocketHint("Min Vertex Count", 0, 0, Integer.MAX_VALUE);
29+
30+
private final SocketHint<Number> maxVertexHint =
31+
SocketHints.Inputs.createNumberSpinnerSocketHint("Max Vertex Count", 0, 1000, Integer.MAX_VALUE);
32+
33+
private final SocketHint<Boolean> forceConvexHint =
34+
SocketHints.createBooleanSocketHint("Force Convex", false);
35+
36+
private final SocketHint<Number> minRatioHint =
37+
SocketHints.Inputs.createNumberSpinnerSocketHint("Min Ratio", 0, 0, Integer.MAX_VALUE);
38+
39+
private final SocketHint<Number> maxRatioHint =
40+
SocketHints.Inputs.createNumberSpinnerSocketHint("Max Ratio", 1000, 0, Integer.MAX_VALUE);
41+
42+
@Override
43+
public String getName() {
44+
return "Advanced Filter Contours";
45+
}
46+
47+
@Override
48+
public String getDescription() {
49+
return "Find contours matching certain advanced criteria.";
50+
}
51+
52+
@Override
53+
public Category getCategory() {
54+
return Category.FEATURE_DETECTION;
55+
}
56+
57+
@Override
58+
public Optional<InputStream> getIcon() {
59+
return Optional.of(getClass().getResourceAsStream("/edu/wpi/grip/ui/icons/find-contours.png"));
60+
}
61+
62+
@Override
63+
public InputSocket<?>[] createInputSockets(EventBus eventBus) {
64+
return new InputSocket<?>[]{
65+
new InputSocket<>(eventBus, contoursHint),
66+
new InputSocket<>(eventBus, minVertexHint),
67+
new InputSocket<>(eventBus, maxVertexHint),
68+
new InputSocket<>(eventBus, forceConvexHint),
69+
new InputSocket<>(eventBus, minRatioHint),
70+
new InputSocket<>(eventBus, maxRatioHint),
71+
};
72+
}
73+
74+
@Override
75+
public OutputSocket<?>[] createOutputSockets(EventBus eventBus) {
76+
return new OutputSocket<?>[]{new OutputSocket<>(eventBus, contoursHint)};
77+
}
78+
79+
@Override
80+
@SuppressWarnings("unchecked")
81+
public void perform(InputSocket<?>[] inputs, OutputSocket<?>[] outputs) {
82+
final InputSocket<ContoursReport> inputSocket = (InputSocket<ContoursReport>) inputs[0];
83+
final double minVertexCount = ((Number) inputs[1].getValue().get()).doubleValue();
84+
final double maxVertexCount = ((Number) inputs[2].getValue().get()).doubleValue();
85+
final boolean forceConvex = ((Boolean) inputs[3].getValue().get()).booleanValue();
86+
final double minRatio = ((Number) inputs[4].getValue().get()).doubleValue();
87+
final double maxRatio = ((Number) inputs[5].getValue().get()).doubleValue();
88+
89+
final MatVector inputContours = inputSocket.getValue().get().getContours();
90+
final MatVector outputContours = new MatVector(inputContours.size());
91+
92+
// Add contours from the input vector to the output vector only if they pass all of the criteria (minimum
93+
// area, minimum perimeter, width, and height, etc...)
94+
int filteredContourCount = 0;
95+
for (int i = 0; i < inputContours.size(); i++) {
96+
final Mat contour = inputContours.get(i);
97+
98+
if(contour.rows() < minVertexCount || contour.rows() > maxVertexCount) continue;
99+
100+
if(forceConvex && !isContourConvex(contour)) continue;
101+
102+
final Rect bb = boundingRect(contour);
103+
final double ratio = bb.width() / bb.height();
104+
if (ratio < minRatio || ratio > maxRatio) continue;
105+
106+
outputContours.put(filteredContourCount++, contour);
107+
}
108+
109+
outputContours.resize(filteredContourCount);
110+
111+
final OutputSocket<ContoursReport> outputSocket = (OutputSocket<ContoursReport>) outputs[0];
112+
outputSocket.setValue(new ContoursReport(outputContours,
113+
inputSocket.getValue().get().getRows(), inputSocket.getValue().get().getCols()));
114+
}
115+
}

0 commit comments

Comments
 (0)