-
Notifications
You must be signed in to change notification settings - Fork 107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Split Filter Contours #627
base: master
Are you sure you want to change the base?
Changes from 6 commits
cfe6c8a
12d5f55
f299c47
c009f1f
633788f
a51ead0
ed9e1df
1499526
c06d93e
50922b1
ecf42d2
394cd58
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,8 @@ | |
|
||
import com.google.common.collect.ImmutableList; | ||
|
||
import org.bytedeco.javacpp.opencv_core; | ||
|
||
import java.util.List; | ||
|
||
import static org.bytedeco.javacpp.opencv_core.Mat; | ||
|
@@ -19,6 +21,7 @@ | |
import static org.bytedeco.javacpp.opencv_imgproc.boundingRect; | ||
import static org.bytedeco.javacpp.opencv_imgproc.contourArea; | ||
import static org.bytedeco.javacpp.opencv_imgproc.convexHull; | ||
import static org.bytedeco.javacpp.opencv_imgproc.minAreaRect; | ||
|
||
/** | ||
* An {@link Operation} that takes in a list of contours and outputs a list of any contours in the | ||
|
@@ -28,11 +31,11 @@ | |
* small objects, as well as contours that do not meet the expected characteristics of the feature | ||
* we're actually looking for. So, this operation can help narrow them down. | ||
*/ | ||
public class FilterContoursOperation implements Operation { | ||
public class AdvancedFilterContoursOperation implements Operation { | ||
|
||
public static final OperationDescription DESCRIPTION = | ||
OperationDescription.builder() | ||
.name("Filter Contours") | ||
.name("Advanced Filter Contours") | ||
.summary("Find contours matching certain criteria") | ||
.category(OperationDescription.Category.FEATURE_DETECTION) | ||
.icon(Icon.iconStream("find-contours")) | ||
|
@@ -45,9 +48,19 @@ public class FilterContoursOperation implements Operation { | |
private final SocketHint<Number> minAreaHint = | ||
SocketHints.Inputs.createNumberSpinnerSocketHint("Min Area", 0, 0, Integer.MAX_VALUE); | ||
|
||
private final SocketHint<Number> maxAreaHint = | ||
SocketHints.Inputs.createNumberSpinnerSocketHint("Max Area", 10000, 0, Integer.MAX_VALUE); | ||
|
||
private final SocketHint<Number> minPerimeterHint = | ||
SocketHints.Inputs.createNumberSpinnerSocketHint("Min Perimeter", 0, 0, Integer.MAX_VALUE); | ||
|
||
private final SocketHint<Boolean> rotatedRectHint = | ||
SocketHints.createBooleanSocketHint("Rotated Rectangles", false); | ||
|
||
private final SocketHint<Number> maxPerimeterHint = | ||
SocketHints.Inputs.createNumberSpinnerSocketHint("Max Perimeter", 10000, 0, | ||
Integer.MAX_VALUE); | ||
|
||
private final SocketHint<Number> minWidthHint = | ||
SocketHints.Inputs.createNumberSpinnerSocketHint("Min Width", 0, 0, Integer.MAX_VALUE); | ||
|
||
|
@@ -79,7 +92,10 @@ public class FilterContoursOperation implements Operation { | |
|
||
private final InputSocket<ContoursReport> contoursSocket; | ||
private final InputSocket<Number> minAreaSocket; | ||
private final InputSocket<Number> maxAreaSocket; | ||
private final InputSocket<Number> minPerimeterSocket; | ||
private final InputSocket<Number> maxPerimeterSocket; | ||
private final InputSocket<Boolean> rotatedRectSocket; | ||
private final InputSocket<Number> minWidthSocket; | ||
private final InputSocket<Number> maxWidthSocket; | ||
private final InputSocket<Number> minHeightSocket; | ||
|
@@ -93,11 +109,14 @@ public class FilterContoursOperation implements Operation { | |
private final OutputSocket<ContoursReport> outputSocket; | ||
|
||
@SuppressWarnings("JavadocMethod") | ||
public FilterContoursOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory | ||
outputSocketFactory) { | ||
public AdvancedFilterContoursOperation(InputSocket.Factory inputSocketFactory, | ||
OutputSocket.Factory outputSocketFactory) { | ||
this.contoursSocket = inputSocketFactory.create(contoursHint); | ||
this.minAreaSocket = inputSocketFactory.create(minAreaHint); | ||
this.maxAreaSocket = inputSocketFactory.create(maxAreaHint); | ||
this.minPerimeterSocket = inputSocketFactory.create(minPerimeterHint); | ||
this.maxPerimeterSocket = inputSocketFactory.create(maxPerimeterHint); | ||
this.rotatedRectSocket = inputSocketFactory.create(rotatedRectHint); | ||
this.minWidthSocket = inputSocketFactory.create(minWidthHint); | ||
this.maxWidthSocket = inputSocketFactory.create(maxWidthHint); | ||
this.minHeightSocket = inputSocketFactory.create(minHeightHint); | ||
|
@@ -116,16 +135,19 @@ public List<InputSocket> getInputSockets() { | |
return ImmutableList.of( | ||
contoursSocket, | ||
minAreaSocket, | ||
maxAreaSocket, | ||
minPerimeterSocket, | ||
maxPerimeterSocket, | ||
rotatedRectSocket, | ||
minWidthSocket, | ||
maxWidthSocket, | ||
minHeightSocket, | ||
maxHeightSocket, | ||
soliditySocket, | ||
maxVertexSocket, | ||
minVertexSocket, | ||
maxVertexSocket, | ||
minRatioSocket, | ||
maxRatioSocket | ||
maxRatioSocket, | ||
soliditySocket | ||
); | ||
} | ||
|
||
|
@@ -141,7 +163,10 @@ public List<OutputSocket> getOutputSockets() { | |
public void perform() { | ||
final InputSocket<ContoursReport> inputSocket = contoursSocket; | ||
final double minArea = minAreaSocket.getValue().get().doubleValue(); | ||
final double maxArea = maxAreaSocket.getValue().get().doubleValue(); | ||
final double minPerimeter = minPerimeterSocket.getValue().get().doubleValue(); | ||
final double maxPerimeter = maxPerimeterSocket.getValue().get().doubleValue(); | ||
final boolean rotatedRect = rotatedRectSocket.getValue().get().booleanValue(); | ||
final double minWidth = minWidthSocket.getValue().get().doubleValue(); | ||
final double maxWidth = maxWidthSocket.getValue().get().doubleValue(); | ||
final double minHeight = minHeightSocket.getValue().get().doubleValue(); | ||
|
@@ -165,19 +190,41 @@ public void perform() { | |
for (int i = 0; i < inputContours.size(); i++) { | ||
final Mat contour = inputContours.get(i); | ||
|
||
final Rect bb = boundingRect(contour); | ||
if (bb.width() < minWidth || bb.width() > maxWidth) { | ||
double width; | ||
double height; | ||
if (rotatedRect) { | ||
final opencv_core.RotatedRect bb = minAreaRect(contour); | ||
opencv_core.Point2f points = new opencv_core.Point2f(4); | ||
bb.points(points); | ||
final double rotatedWidth = Math.sqrt( Math.pow(points.position(0).x() - points.position(1) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Get rid of the space in |
||
.x(), 2) + Math.pow(points.position(0).y() - points.position(1).y(), 2)); | ||
final double rotatedHeight = Math.sqrt( Math.pow(points.position(1).x() - points.position(2) | ||
.x(), 2) + Math.pow(points.position(1).y() - points.position(2).y(), 2)); | ||
if (Math.abs(bb.angle()) >= 45) { | ||
width = rotatedWidth; | ||
height = rotatedHeight; | ||
} else { | ||
width = rotatedHeight; | ||
height = rotatedWidth; | ||
} | ||
} else { | ||
final Rect normbb = boundingRect(contour); | ||
width = normbb.width(); | ||
height = normbb.height(); | ||
} | ||
|
||
if (width < minWidth || width > maxWidth) { | ||
continue; | ||
} | ||
if (bb.height() < minHeight || bb.height() > maxHeight) { | ||
if (width < minHeight || width > maxHeight) { | ||
continue; | ||
} | ||
|
||
final double area = contourArea(contour); | ||
if (area < minArea) { | ||
if (area < minArea || area > maxArea) { | ||
continue; | ||
} | ||
if (arcLength(contour, true) < minPerimeter) { | ||
if (arcLength(contour, true) < minPerimeter || arcLength(contour, true) > maxPerimeter) { | ||
continue; | ||
} | ||
|
||
|
@@ -191,7 +238,7 @@ public void perform() { | |
continue; | ||
} | ||
|
||
final double ratio = bb.width() / bb.height(); | ||
final double ratio = width / height; | ||
if (ratio < minRatio || ratio > maxRatio) { | ||
continue; | ||
} | ||
|
@@ -204,4 +251,4 @@ public void perform() { | |
outputSocket.setValue(new ContoursReport(outputContours, | ||
inputSocket.getValue().get().getRows(), inputSocket.getValue().get().getCols())); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
package edu.wpi.grip.core.operations.composite; | ||
|
||
import edu.wpi.grip.core.Operation; | ||
import edu.wpi.grip.core.OperationDescription; | ||
import edu.wpi.grip.core.sockets.InputSocket; | ||
import edu.wpi.grip.core.sockets.OutputSocket; | ||
import edu.wpi.grip.core.sockets.SocketHint; | ||
import edu.wpi.grip.core.sockets.SocketHints; | ||
import edu.wpi.grip.core.util.Icon; | ||
|
||
import com.google.common.collect.ImmutableList; | ||
|
||
import java.util.List; | ||
|
||
import static org.bytedeco.javacpp.opencv_core.Mat; | ||
import static org.bytedeco.javacpp.opencv_core.MatVector; | ||
import static org.bytedeco.javacpp.opencv_imgproc.arcLength; | ||
import static org.bytedeco.javacpp.opencv_imgproc.contourArea; | ||
|
||
/** | ||
* An {@link Operation} that takes in a list of contours and outputs a list of any contours in the | ||
* input that match all of several criteria. The user can specify a minimum area and perimeter. | ||
* This is useful because running a FindContours on a real-life image typically leads to many small | ||
* undesirable contours from noise and small objects, as well as contours that do not meet the | ||
* expected characteristics of the feature we're actually looking for. So, this operation can | ||
* help narrow them down. | ||
*/ | ||
public class SimpleFilterContoursOperation implements Operation { | ||
|
||
public static final OperationDescription DESCRIPTION = | ||
OperationDescription.builder() | ||
.name("Simple Filter Contours") | ||
.summary("Find contours matching certain criteria") | ||
.category(OperationDescription.Category.FEATURE_DETECTION) | ||
.icon(Icon.iconStream("find-contours")) | ||
.build(); | ||
|
||
private final SocketHint<ContoursReport> contoursHint = new SocketHint.Builder<>(ContoursReport | ||
.class) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Funky formatting |
||
.identifier("Contours").initialValueSupplier(ContoursReport::new).build(); | ||
|
||
private final SocketHint<Number> minAreaHint = | ||
SocketHints.Inputs.createNumberSpinnerSocketHint("Min Area", 0, 0, Integer.MAX_VALUE); | ||
|
||
private final SocketHint<Number> minPerimeterHint = | ||
SocketHints.Inputs.createNumberSpinnerSocketHint("Min Perimeter", 0, 0, Integer.MAX_VALUE); | ||
|
||
|
||
private final InputSocket<ContoursReport> contoursSocket; | ||
private final InputSocket<Number> minAreaSocket; | ||
private final InputSocket<Number> minPerimeterSocket; | ||
|
||
private final OutputSocket<ContoursReport> outputSocket; | ||
|
||
@SuppressWarnings("JavadocMethod") | ||
public SimpleFilterContoursOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Put parameters on separate lines to avoid a newline inside a parameter definition |
||
outputSocketFactory) { | ||
this.contoursSocket = inputSocketFactory.create(contoursHint); | ||
this.minAreaSocket = inputSocketFactory.create(minAreaHint); | ||
this.minPerimeterSocket = inputSocketFactory.create(minPerimeterHint); | ||
|
||
this.outputSocket = outputSocketFactory.create(contoursHint); | ||
} | ||
|
||
@Override | ||
public List<InputSocket> getInputSockets() { | ||
return ImmutableList.of( | ||
contoursSocket, | ||
minAreaSocket, | ||
minPerimeterSocket | ||
); | ||
} | ||
|
||
@Override | ||
public List<OutputSocket> getOutputSockets() { | ||
return ImmutableList.of( | ||
outputSocket | ||
); | ||
} | ||
|
||
@Override | ||
@SuppressWarnings("unchecked") | ||
public void perform() { | ||
final InputSocket<ContoursReport> inputSocket = contoursSocket; | ||
final double minArea = minAreaSocket.getValue().get().doubleValue(); | ||
final double minPerimeter = minPerimeterSocket.getValue().get().doubleValue(); | ||
|
||
|
||
final MatVector inputContours = inputSocket.getValue().get().getContours(); | ||
final MatVector outputContours = new MatVector(inputContours.size()); | ||
|
||
// Add contours from the input vector to the output vector only if they pass all of the | ||
// criteria (minimum area, minimum perimeter) | ||
int filteredContourCount = 0; | ||
for (int i = 0; i < inputContours.size(); i++) { | ||
final Mat contour = inputContours.get(i); | ||
|
||
final double area = contourArea(contour); | ||
if (area < minArea) { | ||
continue; | ||
} | ||
if (arcLength(contour, true) < minPerimeter) { | ||
continue; | ||
} | ||
outputContours.put(filteredContourCount++, contour); | ||
} | ||
|
||
outputContours.resize(filteredContourCount); | ||
|
||
outputSocket.setValue(new ContoursReport(outputContours, | ||
inputSocket.getValue().get().getRows(), inputSocket.getValue().get().getCols())); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Arguments should all be on one line (if it fits), or each should be on its own line |
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RotatedRect
andPoint2f
should be imported directly fromorg.bytedeco.javacpp.opencv_core
to avoid theopencv_core.RotatedRect
crap