Skip to content

Commit 08e53d3

Browse files
committed
Move Mat drawing to a utility class
1 parent 4afbf20 commit 08e53d3

File tree

5 files changed

+108
-91
lines changed

5 files changed

+108
-91
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package edu.wpi.grip.core.util;
2+
3+
import java.util.function.BiConsumer;
4+
import java.util.function.Supplier;
5+
6+
import static org.bytedeco.javacpp.opencv_core.Mat;
7+
import static org.bytedeco.javacpp.opencv_core.bitwise_xor;
8+
import static org.bytedeco.javacpp.opencv_imgproc.CV_GRAY2BGR;
9+
import static org.bytedeco.javacpp.opencv_imgproc.cvtColor;
10+
11+
/**
12+
* Utility class for working with OpenCV Mats.
13+
*/
14+
public class MatUtils {
15+
16+
/**
17+
* Draws some data on an image, such as lines, contours, or other shapes.
18+
*
19+
* @param image the image
20+
* @param showOriginal true if the original image should be shown
21+
* @param dataSupplier supplier for the data being drawn on the image
22+
* @param drawingFunction the function to draw on the image with
23+
* @param <T> the type of the data being drawn
24+
*
25+
* @return a mat with the data drawn on it
26+
*/
27+
public static <T> Mat draw(Mat image,
28+
boolean showOriginal,
29+
Supplier<T> dataSupplier,
30+
BiConsumer<Mat, T> drawingFunction) {
31+
Mat tmp = new Mat();
32+
if (image.channels() == 3) {
33+
image.copyTo(tmp);
34+
} else {
35+
cvtColor(image, tmp, CV_GRAY2BGR);
36+
}
37+
if (!showOriginal) {
38+
bitwise_xor(tmp, tmp, tmp);
39+
}
40+
41+
drawingFunction.accept(tmp, dataSupplier.get());
42+
43+
return tmp;
44+
}
45+
46+
}

ui/src/main/java/edu/wpi/grip/ui/preview/BlobsSocketPreviewView.java

+14-23
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import edu.wpi.grip.core.events.RenderEvent;
44
import edu.wpi.grip.core.operations.composite.BlobsReport;
55
import edu.wpi.grip.core.sockets.OutputSocket;
6+
import edu.wpi.grip.core.util.ImageDrawer;
67
import edu.wpi.grip.ui.util.GripPlatform;
78
import edu.wpi.grip.ui.util.ImageConverter;
89

@@ -24,6 +25,7 @@
2425
import static org.bytedeco.javacpp.opencv_core.Point;
2526
import static org.bytedeco.javacpp.opencv_core.Scalar;
2627
import static org.bytedeco.javacpp.opencv_core.bitwise_xor;
28+
import static org.bytedeco.javacpp.opencv_core.cvInsertNodeIntoTree;
2729
import static org.bytedeco.javacpp.opencv_imgproc.CV_GRAY2BGR;
2830
import static org.bytedeco.javacpp.opencv_imgproc.circle;
2931
import static org.bytedeco.javacpp.opencv_imgproc.cvtColor;
@@ -37,11 +39,10 @@ public class BlobsSocketPreviewView extends SocketPreviewView<BlobsReport> {
3739
private final ImageConverter imageConverter = new ImageConverter();
3840
private final ImageView imageView = new ImageView();
3941
private final Label infoLabel = new Label();
40-
private final Mat tmp = new Mat();
4142
private final GripPlatform platform;
4243
@SuppressWarnings("PMD.ImmutableField")
4344
@SuppressFBWarnings(value = "IS2_INCONSISTENT_SYNC",
44-
justification = "Do not need to synchronize inside of a constructor")
45+
justification = "Do not need to synchronize inside of a constructor")
4546
private boolean showInputImage = false;
4647

4748
/**
@@ -75,31 +76,21 @@ public void onRender(RenderEvent event) {
7576
private void convertImage() {
7677
synchronized (this) {
7778
final BlobsReport blobsReport = this.getSocket().getValue().get();
78-
final Mat input = blobsReport.getInput();
79+
Mat input = blobsReport.getInput();
7980

80-
if (input.channels() == 3) {
81-
input.copyTo(tmp);
82-
} else {
83-
cvtColor(input, tmp, CV_GRAY2BGR);
84-
}
85-
// If we don't want to see the background image, set it to black
86-
if (!this.showInputImage) {
87-
bitwise_xor(tmp, tmp, tmp);
88-
}
81+
input = ImageDrawer.draw(
82+
input,
83+
showInputImage,
84+
blobsReport::getBlobs,
85+
(m, br) -> br.forEach(b -> circle(
86+
m, new Point((int) b.x, (int) b.y), (int) (b.size / 2), Scalar.WHITE, 2, LINE_8, 0)
87+
)
88+
);
8989

90-
// If there were lines found, draw them on the image before displaying it
91-
if (!blobsReport.getBlobs().isEmpty()) {
92-
// For each line in the report, draw a line along with the starting and ending points
93-
for (BlobsReport.Blob blob : blobsReport.getBlobs()) {
94-
final Point point = new Point((int) blob.x, (int) blob.y);
95-
circle(tmp, point, (int) (blob.size / 2), Scalar.WHITE, 2, LINE_8, 0);
96-
}
97-
}
98-
99-
final Mat output = tmp;
10090
final int numBlobs = blobsReport.getBlobs().size();
91+
final Mat convertInput = input;
10192
platform.runAsSoonAsPossible(() -> {
102-
final Image image = this.imageConverter.convert(output);
93+
final Image image = this.imageConverter.convert(convertInput);
10394
this.imageView.setImage(image);
10495
this.infoLabel.setText("Found " + numBlobs + " blobs");
10596
});

ui/src/main/java/edu/wpi/grip/ui/preview/ContoursSocketPreviewView.java

+22-17
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import edu.wpi.grip.core.events.RenderEvent;
44
import edu.wpi.grip.core.operations.composite.ContoursReport;
55
import edu.wpi.grip.core.sockets.OutputSocket;
6+
import edu.wpi.grip.core.util.ImageDrawer;
67
import edu.wpi.grip.ui.util.GripPlatform;
78
import edu.wpi.grip.ui.util.ImageConverter;
89

@@ -17,8 +18,8 @@
1718

1819
import static org.bytedeco.javacpp.opencv_core.CV_8UC3;
1920
import static org.bytedeco.javacpp.opencv_core.Mat;
21+
import static org.bytedeco.javacpp.opencv_core.MatVector;
2022
import static org.bytedeco.javacpp.opencv_core.Scalar;
21-
import static org.bytedeco.javacpp.opencv_core.bitwise_xor;
2223
import static org.bytedeco.javacpp.opencv_imgproc.drawContours;
2324

2425
/**
@@ -28,7 +29,7 @@
2829
*/
2930
public final class ContoursSocketPreviewView extends SocketPreviewView<ContoursReport> {
3031

31-
private static final Scalar[] CONTOUR_COLORS = new Scalar[]{
32+
private static final Scalar[] CONTOUR_COLORS = new Scalar[] {
3233
Scalar.RED,
3334
Scalar.YELLOW,
3435
Scalar.GREEN,
@@ -69,34 +70,38 @@ public void onRender(RenderEvent event) {
6970
private void render() {
7071
synchronized (this) {
7172
final ContoursReport contours = this.getSocket().getValue().get();
72-
long numContours = 0;
73+
long numContours = contours.getContours().size();
7374

7475
if (!contours.getContours().isNull() && contours.getRows() > 0 && contours.getCols() > 0) {
7576
// Allocate a completely black OpenCV Mat to draw the contours onto. We can easily
7677
// render contours
7778
// by using OpenCV's drawContours function and converting the Mat into a JavaFX Image.
7879
this.tmp.create(contours.getRows(), contours.getCols(), CV_8UC3);
79-
bitwise_xor(tmp, tmp, tmp);
80-
81-
numContours = contours.getContours().size();
82-
83-
if (this.colorContours.isSelected()) {
84-
for (int i = 0; i < numContours; i++) {
85-
drawContours(this.tmp, contours.getContours(), i, CONTOUR_COLORS[i % CONTOUR_COLORS
86-
.length]);
87-
}
88-
} else {
89-
drawContours(this.tmp, contours.getContours(), -1, Scalar.WHITE);
90-
}
80+
ImageDrawer.draw(
81+
tmp,
82+
false,
83+
contours::getContours,
84+
(m, c) -> draw(m, c, numContours)
85+
).copyTo(tmp);
9186
}
9287

9388
final long finalNumContours = numContours;
94-
final Mat convertInput = tmp;
9589
platform.runAsSoonAsPossible(() -> {
96-
final Image image = this.imageConverter.convert(convertInput);
90+
final Image image = this.imageConverter.convert(tmp);
9791
this.imageView.setImage(image);
9892
this.infoLabel.setText("Found " + finalNumContours + " contours");
9993
});
10094
}
10195
}
96+
97+
private void draw(Mat image, MatVector contours, long size) {
98+
if (colorContours.isSelected()) {
99+
for (int i = 0; i < size; i++) {
100+
drawContours(image, contours, i, CONTOUR_COLORS[i % CONTOUR_COLORS.length]);
101+
}
102+
} else {
103+
drawContours(image, contours, -1, Scalar.WHITE);
104+
}
105+
}
106+
102107
}

ui/src/main/java/edu/wpi/grip/ui/preview/LinesSocketPreviewView.java

+19-29
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import edu.wpi.grip.core.events.RenderEvent;
44
import edu.wpi.grip.core.operations.composite.LinesReport;
55
import edu.wpi.grip.core.sockets.OutputSocket;
6+
import edu.wpi.grip.core.util.ImageDrawer;
67
import edu.wpi.grip.ui.util.GripPlatform;
78
import edu.wpi.grip.ui.util.ImageConverter;
89

@@ -11,6 +12,7 @@
1112
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
1213

1314
import java.util.List;
15+
1416
import javafx.application.Platform;
1517
import javafx.geometry.Orientation;
1618
import javafx.scene.control.CheckBox;
@@ -24,10 +26,7 @@
2426
import static org.bytedeco.javacpp.opencv_core.Mat;
2527
import static org.bytedeco.javacpp.opencv_core.Point;
2628
import static org.bytedeco.javacpp.opencv_core.Scalar;
27-
import static org.bytedeco.javacpp.opencv_core.bitwise_xor;
28-
import static org.bytedeco.javacpp.opencv_imgproc.CV_GRAY2BGR;
2929
import static org.bytedeco.javacpp.opencv_imgproc.circle;
30-
import static org.bytedeco.javacpp.opencv_imgproc.cvtColor;
3130
import static org.bytedeco.javacpp.opencv_imgproc.line;
3231

3332
/**
@@ -39,11 +38,10 @@ public class LinesSocketPreviewView extends SocketPreviewView<LinesReport> {
3938
private final ImageConverter imageConverter = new ImageConverter();
4039
private final ImageView imageView = new ImageView();
4140
private final Label infoLabel = new Label();
42-
private final Mat tmp = new Mat();
4341
private final GripPlatform platform;
4442
@SuppressWarnings("PMD.ImmutableField")
4543
@SuppressFBWarnings(value = "IS2_INCONSISTENT_SYNC",
46-
justification = "Do not need to synchronize inside of a constructor")
44+
justification = "Do not need to synchronize inside of a constructor")
4745
private boolean showInputImage = false;
4846

4947
/**
@@ -82,30 +80,13 @@ private void convertImage() {
8280
final List<LinesReport.Line> lines = linesReport.getLines();
8381
Mat input = linesReport.getInput();
8482

85-
// If there were lines found, draw them on the image before displaying it
86-
if (!linesReport.getLines().isEmpty()) {
87-
if (input.channels() == 3) {
88-
input.copyTo(tmp);
89-
} else {
90-
cvtColor(input, tmp, CV_GRAY2BGR);
91-
}
92-
93-
input = tmp;
94-
95-
// If we don't want to see the background image, set it to black
96-
if (!this.showInputImage) {
97-
bitwise_xor(tmp, tmp, tmp);
98-
}
99-
100-
// For each line in the report, draw a line along with the starting and ending points
101-
for (LinesReport.Line line : lines) {
102-
final Point startPoint = new Point((int) line.x1, (int) line.y1);
103-
final Point endPoint = new Point((int) line.x2, (int) line.y2);
104-
line(input, startPoint, endPoint, Scalar.WHITE, 2, LINE_8, 0);
105-
circle(input, startPoint, 2, Scalar.WHITE, 2, LINE_8, 0);
106-
circle(input, endPoint, 2, Scalar.WHITE, 2, LINE_8, 0);
107-
}
108-
}
83+
input = ImageDrawer.draw(
84+
input,
85+
showInputImage,
86+
linesReport::getLines,
87+
(m, lr) -> lr.forEach(l -> drawLine(m, l))
88+
);
89+
10990
final Mat convertInput = input;
11091
final int numLines = lines.size();
11192
platform.runAsSoonAsPossible(() -> {
@@ -115,4 +96,13 @@ private void convertImage() {
11596
});
11697
}
11798
}
99+
100+
private void drawLine(Mat image, LinesReport.Line line) {
101+
final Point startPoint = new Point((int) line.x1, (int) line.y1);
102+
final Point endPoint = new Point((int) line.x2, (int) line.y2);
103+
line(image, startPoint, endPoint, Scalar.WHITE, 2, LINE_8, 0);
104+
circle(image, startPoint, 2, Scalar.WHITE, 2, LINE_8, 0);
105+
circle(image, endPoint, 2, Scalar.WHITE, 2, LINE_8, 0);
106+
}
107+
118108
}

ui/src/main/java/edu/wpi/grip/ui/preview/RectangleSocketPreviewView.java

+7-22
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import edu.wpi.grip.core.events.RenderEvent;
44
import edu.wpi.grip.core.operations.composite.RectsReport;
55
import edu.wpi.grip.core.sockets.OutputSocket;
6+
import edu.wpi.grip.core.util.ImageDrawer;
67
import edu.wpi.grip.ui.util.GripPlatform;
78
import edu.wpi.grip.ui.util.ImageConverter;
89

@@ -24,9 +25,6 @@
2425
import static org.bytedeco.javacpp.opencv_core.Mat;
2526
import static org.bytedeco.javacpp.opencv_core.Rect;
2627
import static org.bytedeco.javacpp.opencv_core.Scalar;
27-
import static org.bytedeco.javacpp.opencv_core.bitwise_xor;
28-
import static org.bytedeco.javacpp.opencv_imgproc.CV_GRAY2BGR;
29-
import static org.bytedeco.javacpp.opencv_imgproc.cvtColor;
3028
import static org.bytedeco.javacpp.opencv_imgproc.rectangle;
3129

3230
/**
@@ -37,7 +35,6 @@ public class RectangleSocketPreviewView extends SocketPreviewView<RectsReport> {
3735
private final ImageConverter imageConverter = new ImageConverter();
3836
private final ImageView imageView = new ImageView();
3937
private final Label infoLabel = new Label();
40-
private final Mat tmp = new Mat();
4138
private final GripPlatform platform;
4239
@SuppressWarnings("PMD.ImmutableField")
4340
@SuppressFBWarnings(value = "IS2_INCONSISTENT_SYNC",
@@ -78,24 +75,12 @@ private void convertImage() {
7875
Mat input = report.getImage();
7976

8077
// If rectangles were found, draw them on the image before displaying it
81-
if (!rectangles.isEmpty()) {
82-
if (input.channels() == 3) {
83-
input.copyTo(tmp);
84-
} else {
85-
cvtColor(input, tmp, CV_GRAY2BGR);
86-
}
87-
88-
input = tmp;
89-
90-
// If we don't want to see the background image, set it to black
91-
if (!this.showInputImage) {
92-
bitwise_xor(tmp, tmp, tmp);
93-
}
94-
95-
for (Rect r : rectangles) {
96-
rectangle(input, r, Scalar.WHITE);
97-
}
98-
}
78+
input = ImageDrawer.draw(
79+
input,
80+
showInputImage,
81+
report::getRectangles,
82+
(m, rr) -> rr.forEach(r -> rectangle(m, r, Scalar.WHITE))
83+
);
9984
final Mat convertInput = input;
10085
final int numRegions = rectangles.size();
10186
platform.runAsSoonAsPossible(() -> {

0 commit comments

Comments
 (0)