ifGpu) {
+ if (isCpu) {
+ return ifCpu.apply(cpuMat);
+ } else {
+ return ifGpu.apply(gpuMat);
+ }
+ }
+
+ /**
+ * Gets the number of columns in this image.
+ */
+ public int cols() {
+ return extract(Mat::cols, GpuMat::cols);
+ }
+
+ /**
+ * Gets the number of rows in this image.
+ */
+ public int rows() {
+ return extract(Mat::rows, GpuMat::rows);
+ }
+
+ /**
+ * Gets the type of the data format of this image.
+ */
+ public int type() {
+ return extract(Mat::type, GpuMat::type);
+ }
+
+ /**
+ * Gets the number of color channels in this image.
+ */
+ public int channels() {
+ return extract(Mat::channels, GpuMat::channels);
+ }
+
+ /**
+ * Gets the channel depth of this image.
+ */
+ public int depth() {
+ return extract(Mat::depth, GpuMat::depth);
+ }
+
+ /**
+ * Checks if this image is empty.
+ */
+ public boolean empty() {
+ return extract(Mat::empty, GpuMat::empty);
+ }
+
+ /**
+ * Gets the size (width by height) of this image.
+ */
+ public Size size() {
+ return extract(Mat::size, GpuMat::size);
+ }
+
+ /**
+ * Gets the maximum possible value able to be held as a single element in this image.
+ */
+ @SuppressWarnings("PMD")
+ public double highValue() {
+ return extract(Mat::highValue, g -> {
+ double highValue = 0.0;
+ switch (arrayDepth(g)) {
+ case IPL_DEPTH_8U:
+ highValue = 0xFF;
+ break;
+ case IPL_DEPTH_16U:
+ highValue = 0xFFFF;
+ break;
+ case IPL_DEPTH_8S:
+ highValue = Byte.MAX_VALUE;
+ break;
+ case IPL_DEPTH_16S:
+ highValue = Short.MAX_VALUE;
+ break;
+ case IPL_DEPTH_32S:
+ highValue = Integer.MAX_VALUE;
+ break;
+ case IPL_DEPTH_1U:
+ case IPL_DEPTH_32F:
+ case IPL_DEPTH_64F:
+ highValue = 1.0;
+ break;
+ default:
+ assert false;
+ }
+ return highValue;
+ });
+ }
+
+ private static int arrayDepth(GpuMat m) {
+ switch (m.depth()) {
+ case CV_8U:
+ return IPL_DEPTH_8U;
+ case CV_8S:
+ return IPL_DEPTH_8S;
+ case CV_16U:
+ return IPL_DEPTH_16U;
+ case CV_16S:
+ return IPL_DEPTH_16S;
+ case CV_32S:
+ return IPL_DEPTH_32S;
+ case CV_32F:
+ return IPL_DEPTH_32F;
+ case CV_64F:
+ return IPL_DEPTH_64F;
+ default:
+ throw new UnsupportedOperationException("Unsupported depth " + m.depth());
+ }
+ }
+
+ /**
+ * Allocates new array data if needed.
+ *
+ * @param rows New number of rows.
+ * @param cols New number of columns.
+ * @param type New matrix type.
+ */
+ public void create(int rows, int cols, int type) {
+ if (isCpu) {
+ cpuMat.create(rows, cols, type);
+ } else {
+ gpuMat.create(rows, cols, type);
+ }
+ changed = true;
+ }
+
+ /**
+ * Sets all or some of the array elements to the specified value.
+ *
+ * @param value Assigned scalar converted to the actual array type.
+ */
+ public MatWrapper put(opencv_core.Scalar value) {
+ if (isCpu()) {
+ cpuMat.put(value);
+ } else {
+ gpuMat.setTo(value);
+ }
+ changed = true;
+ return this;
+ }
+}
diff --git a/core/src/main/java/edu/wpi/grip/core/cuda/AccelerationMode.java b/core/src/main/java/edu/wpi/grip/core/cuda/AccelerationMode.java
new file mode 100644
index 0000000000..06a1b29fb1
--- /dev/null
+++ b/core/src/main/java/edu/wpi/grip/core/cuda/AccelerationMode.java
@@ -0,0 +1,13 @@
+package edu.wpi.grip.core.cuda;
+
+/**
+ * App-wide hardware acceleration mode.
+ */
+public interface AccelerationMode {
+
+ /**
+ * Flag marking that GRIP is using CUDA-accelerated OpenCV.
+ */
+ boolean isUsingCuda();
+
+}
diff --git a/core/src/main/java/edu/wpi/grip/core/cuda/CudaAccelerationMode.java b/core/src/main/java/edu/wpi/grip/core/cuda/CudaAccelerationMode.java
new file mode 100644
index 0000000000..215bf59d50
--- /dev/null
+++ b/core/src/main/java/edu/wpi/grip/core/cuda/CudaAccelerationMode.java
@@ -0,0 +1,8 @@
+package edu.wpi.grip.core.cuda;
+
+public class CudaAccelerationMode implements AccelerationMode {
+ @Override
+ public boolean isUsingCuda() {
+ return true;
+ }
+}
diff --git a/core/src/main/java/edu/wpi/grip/core/cuda/CudaDetector.java b/core/src/main/java/edu/wpi/grip/core/cuda/CudaDetector.java
new file mode 100644
index 0000000000..57127d3ba5
--- /dev/null
+++ b/core/src/main/java/edu/wpi/grip/core/cuda/CudaDetector.java
@@ -0,0 +1,13 @@
+package edu.wpi.grip.core.cuda;
+
+/**
+ * Detects CUDA installs.
+ */
+public interface CudaDetector {
+
+ /**
+ * Checks if a CUDA runtime is installed that is compatible with what we need for OpenCV.
+ */
+ boolean isCompatibleCudaInstalled();
+
+}
diff --git a/core/src/main/java/edu/wpi/grip/core/cuda/CudaVerifier.java b/core/src/main/java/edu/wpi/grip/core/cuda/CudaVerifier.java
new file mode 100644
index 0000000000..ed61b13915
--- /dev/null
+++ b/core/src/main/java/edu/wpi/grip/core/cuda/CudaVerifier.java
@@ -0,0 +1,71 @@
+package edu.wpi.grip.core.cuda;
+
+import edu.wpi.grip.core.util.SafeShutdown;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import java.util.Properties;
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+public class CudaVerifier {
+
+ private static final Logger logger = Logger.getLogger(CudaVerifier.class.getName());
+
+ private static final String CUDA_VERSION_KEY = "edu.wpi.grip.cuda.version";
+
+ private final AccelerationMode accelerationMode;
+ private final CudaDetector cudaDetector;
+ private final String cudaVersion;
+
+ @Inject
+ public CudaVerifier(AccelerationMode accelerationMode,
+ CudaDetector cudaDetector,
+ @Named("cudaProperties") Properties cudaProperties) {
+ this.accelerationMode = accelerationMode;
+ this.cudaDetector = cudaDetector;
+ this.cudaVersion = cudaProperties.getProperty(CUDA_VERSION_KEY, "Unknown");
+ }
+
+ /**
+ * Verifies the presence of a CUDA runtime, if required by the GRIP runtime, and exits the
+ * app if no compatible CUDA runtime is available.
+ */
+ public void verifyCuda() {
+ if (!verify()) {
+ String message = "This version of GRIP requires CUDA version "
+ + cudaVersion
+ + " to be installed and an NVIDIA graphics card in your computer. "
+ + "If your computer does not have an NVIDIA graphics card, use a version of GRIP "
+ + "without CUDA acceleration. Otherwise, you need to install the appropriate CUDA "
+ + "runtime for your computer.";
+ logger.severe(message);
+ exit();
+ }
+ }
+
+ /**
+ * Verifies that, if GRIP is using CUDA acceleration, a compatible CUDA runtime is available. If
+ * GRIP is not using CUDA acceleration, this will always return {@code true}.
+ *
+ * @return false if GRIP is using CUDA acceleration but no compatible CUDA runtime is available,
+ * true otherwise
+ */
+ public boolean verify() {
+ if (accelerationMode.isUsingCuda()) {
+ return cudaDetector.isCompatibleCudaInstalled();
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Exits the application.
+ */
+ @VisibleForTesting
+ void exit() {
+ SafeShutdown.exit(SafeShutdown.ExitCode.CUDA_UNAVAILABLE);
+ }
+}
diff --git a/core/src/main/java/edu/wpi/grip/core/cuda/LoadingCudaDetector.java b/core/src/main/java/edu/wpi/grip/core/cuda/LoadingCudaDetector.java
new file mode 100644
index 0000000000..f19ffbb84b
--- /dev/null
+++ b/core/src/main/java/edu/wpi/grip/core/cuda/LoadingCudaDetector.java
@@ -0,0 +1,36 @@
+package edu.wpi.grip.core.cuda;
+
+import org.bytedeco.javacpp.Loader;
+import org.bytedeco.javacpp.opencv_cudaarithm;
+
+/**
+ * Checks if CUDA is available by attempting to load one of the OpenCV CUDA class' JNI. If the JNI
+ * cannot be loaded, then no compatible CUDA runtime is available. This approach is probably the
+ * most flexible; it's OS-agnostic, since it lets the JVM handle loading the JNI libraries and
+ * linking, and doesn't require knowledge of CUDA installation locations - it just needs to be on
+ * the PATH.
+ *
+ * Only one attempt is made to load the JNI, and the result is cached. Any later calls to
+ * {@link #isCompatibleCudaInstalled()} will simply return the cached value.
+ */
+public class LoadingCudaDetector implements CudaDetector {
+
+ private volatile boolean hasCuda = false;
+ private volatile boolean checkedForCuda = false;
+
+ @Override
+ public boolean isCompatibleCudaInstalled() {
+ if (!checkedForCuda) {
+ try {
+ Loader.load(opencv_cudaarithm.class);
+ hasCuda = true;
+ } catch (UnsatisfiedLinkError | NoClassDefFoundError e) {
+ // Couldn't load the JNI, no compatible CUDA runtime is available
+ hasCuda = false;
+ }
+ checkedForCuda = true;
+ }
+
+ return hasCuda;
+ }
+}
diff --git a/core/src/main/java/edu/wpi/grip/core/cuda/NullAccelerationMode.java b/core/src/main/java/edu/wpi/grip/core/cuda/NullAccelerationMode.java
new file mode 100644
index 0000000000..9272f7a4a2
--- /dev/null
+++ b/core/src/main/java/edu/wpi/grip/core/cuda/NullAccelerationMode.java
@@ -0,0 +1,8 @@
+package edu.wpi.grip.core.cuda;
+
+public class NullAccelerationMode implements AccelerationMode {
+ @Override
+ public boolean isUsingCuda() {
+ return false;
+ }
+}
diff --git a/core/src/main/java/edu/wpi/grip/core/cuda/NullCudaDetector.java b/core/src/main/java/edu/wpi/grip/core/cuda/NullCudaDetector.java
new file mode 100644
index 0000000000..bb4ed1449d
--- /dev/null
+++ b/core/src/main/java/edu/wpi/grip/core/cuda/NullCudaDetector.java
@@ -0,0 +1,8 @@
+package edu.wpi.grip.core.cuda;
+
+public class NullCudaDetector implements CudaDetector {
+ @Override
+ public boolean isCompatibleCudaInstalled() {
+ return false;
+ }
+}
diff --git a/core/src/main/java/edu/wpi/grip/core/events/UnexpectedThrowableEvent.java b/core/src/main/java/edu/wpi/grip/core/events/UnexpectedThrowableEvent.java
index b54ff29151..8897e9b1f4 100644
--- a/core/src/main/java/edu/wpi/grip/core/events/UnexpectedThrowableEvent.java
+++ b/core/src/main/java/edu/wpi/grip/core/events/UnexpectedThrowableEvent.java
@@ -55,7 +55,7 @@ public void handleSafely(UnexpectedThrowableEventHandler handler) {
try {
logger.log(Level.SEVERE, "Failed to handle safely", throwable);
} finally {
- SafeShutdown.exit(1);
+ SafeShutdown.exit(SafeShutdown.ExitCode.MISC_ERROR);
}
} finally {
shutdownIfFatal();
@@ -72,7 +72,7 @@ public void shutdownIfFatal() {
logger.log(Level.SEVERE, "Shutting down from error", throwable);
} finally {
// If all else fails then shutdown
- SafeShutdown.exit(1);
+ SafeShutdown.exit(SafeShutdown.ExitCode.MISC_ERROR);
}
}
}
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/CVOperations.java b/core/src/main/java/edu/wpi/grip/core/operations/CVOperations.java
index af12ad0da7..7df0d45a75 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/CVOperations.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/CVOperations.java
@@ -1,6 +1,5 @@
package edu.wpi.grip.core.operations;
-
import edu.wpi.grip.core.OperationMetaData;
import edu.wpi.grip.core.events.OperationAddedEvent;
import edu.wpi.grip.core.operations.opencv.CVOperation;
@@ -24,16 +23,48 @@
import com.google.common.eventbus.EventBus;
import com.google.inject.Inject;
-import org.bytedeco.javacpp.opencv_core;
import org.bytedeco.javacpp.opencv_core.Point;
import org.bytedeco.javacpp.opencv_core.Scalar;
import org.bytedeco.javacpp.opencv_core.Size;
+import org.bytedeco.javacpp.opencv_cudaarithm;
+import org.bytedeco.javacpp.opencv_cudafilters.Filter;
+import org.bytedeco.javacpp.opencv_cudaimgproc;
import org.bytedeco.javacpp.opencv_imgproc;
+import static org.bytedeco.javacpp.opencv_core.absdiff;
+import static org.bytedeco.javacpp.opencv_core.add;
+import static org.bytedeco.javacpp.opencv_core.addWeighted;
+import static org.bytedeco.javacpp.opencv_core.bitwise_and;
+import static org.bytedeco.javacpp.opencv_core.bitwise_not;
+import static org.bytedeco.javacpp.opencv_core.bitwise_or;
+import static org.bytedeco.javacpp.opencv_core.bitwise_xor;
+import static org.bytedeco.javacpp.opencv_core.compare;
+import static org.bytedeco.javacpp.opencv_core.divide;
+import static org.bytedeco.javacpp.opencv_core.extractChannel;
+import static org.bytedeco.javacpp.opencv_core.flip;
+import static org.bytedeco.javacpp.opencv_core.max;
+import static org.bytedeco.javacpp.opencv_core.min;
+import static org.bytedeco.javacpp.opencv_core.multiply;
+import static org.bytedeco.javacpp.opencv_core.scaleAdd;
+import static org.bytedeco.javacpp.opencv_core.subtract;
+import static org.bytedeco.javacpp.opencv_core.transpose;
+import static org.bytedeco.javacpp.opencv_cudafilters.createSobelFilter;
+import static org.bytedeco.javacpp.opencv_imgproc.GaussianBlur;
+import static org.bytedeco.javacpp.opencv_imgproc.Laplacian;
+import static org.bytedeco.javacpp.opencv_imgproc.Sobel;
+import static org.bytedeco.javacpp.opencv_imgproc.adaptiveThreshold;
+import static org.bytedeco.javacpp.opencv_imgproc.applyColorMap;
+import static org.bytedeco.javacpp.opencv_imgproc.cvtColor;
+import static org.bytedeco.javacpp.opencv_imgproc.dilate;
+import static org.bytedeco.javacpp.opencv_imgproc.medianBlur;
+import static org.bytedeco.javacpp.opencv_imgproc.rectangle;
+import static org.bytedeco.javacpp.opencv_imgproc.resize;
+import static org.bytedeco.javacpp.opencv_imgproc.threshold;
+
/**
* A list of all of the raw opencv operations.
*/
-@SuppressWarnings("PMD.AvoidDuplicateLiterals")
+@SuppressWarnings({"PMD.AvoidDuplicateLiterals", "CodeBlock2Expr"})
public class CVOperations {
private final EventBus eventBus;
@@ -47,138 +78,196 @@ public class CVOperations {
this.coreOperations = ImmutableList.of(
new OperationMetaData(CVOperation.defaults("CV absdiff",
"Calculate the per-element absolute difference of two images."),
- templateFactory.createAllMatTwoSource(opencv_core::absdiff)),
+ templateFactory.createAllMatTwoSourceCuda((src1, src2, useCuda, dst) -> {
+ if (useCuda) {
+ opencv_cudaarithm.absdiff(src1.getGpu(), src2.getGpu(), dst.rawGpu());
+ } else {
+ absdiff(src1.getCpu(), src2.getCpu(), dst.rawCpu());
+ }
+ })),
new OperationMetaData(CVOperation.defaults("CV add",
"Calculate the per-pixel sum of two images."),
- templateFactory.createAllMatTwoSource(opencv_core::add)),
+ templateFactory.createAllMatTwoSourceCuda((src1, src2, useCuda, dst) -> {
+ if (useCuda) {
+ opencv_cudaarithm.add(src1.getGpu(), src2.getGpu(), dst.rawGpu());
+ } else {
+ add(src1.getCpu(), src2.getCpu(), dst.rawCpu());
+ }
+ })),
new OperationMetaData(CVOperation.defaults("CV addWeighted",
"Calculate the weighted sum of two images."),
- templateFactory.create(
- SocketHints.Inputs.createMatSocketHint("src1", false),
+ templateFactory.createCuda(
+ SocketHints.createImageSocketHint("src1"),
SocketHints.Inputs.createNumberSpinnerSocketHint("alpha", 0),
- SocketHints.Inputs.createMatSocketHint("src2", false),
+ SocketHints.createImageSocketHint("src2"),
SocketHints.Inputs.createNumberSpinnerSocketHint("beta", 0),
SocketHints.Inputs.createNumberSpinnerSocketHint("gamma", 0),
- SocketHints.Outputs.createMatSocketHint("dst"),
- (src1, alpha, src2, beta, gamma, dst) -> {
- opencv_core.addWeighted(src1, alpha.doubleValue(), src2, beta.doubleValue(),
- gamma.doubleValue(), dst);
+ SocketHints.createImageSocketHint("dst"),
+ (src1, alpha, src2, beta, gamma, useCuda, dst) -> {
+ if (useCuda) {
+ opencv_cudaarithm.addWeighted(src1.getGpu(), alpha.doubleValue(), src2.getGpu(),
+ beta.doubleValue(), gamma.doubleValue(), dst.rawGpu());
+ } else {
+ addWeighted(src1.getCpu(), alpha.doubleValue(), src2.getCpu(),
+ beta.doubleValue(), gamma.doubleValue(), dst.rawCpu());
+ }
}
)),
new OperationMetaData(CVOperation.defaults("CV bitwise_and",
"Calculate the per-element bitwise conjunction of two images."),
- templateFactory.createAllMatTwoSource(opencv_core::bitwise_and)),
+ templateFactory.createAllMatTwoSourceCuda((src1, src2, useCuda, dst) -> {
+ if (useCuda) {
+ opencv_cudaarithm.bitwise_and(src1.getGpu(), src2.getGpu(), dst.rawGpu());
+ } else {
+ bitwise_and(src1.getCpu(), src2.getCpu(), dst.rawCpu());
+ }
+ })),
new OperationMetaData(CVOperation.defaults("CV bitwise_not",
"Calculate per-element bit-wise inversion of an image."),
- templateFactory.createAllMatOneSource(opencv_core::bitwise_not)),
+ templateFactory.createAllMatOneSourceCuda((src, useCuda, dst) -> {
+ if (useCuda) {
+ opencv_cudaarithm.bitwise_not(src.getGpu(), dst.rawGpu());
+ } else {
+ bitwise_not(src.getCpu(), dst.rawCpu());
+ }
+ })),
new OperationMetaData(CVOperation.defaults("CV bitwise_or",
"Calculate the per-element bit-wise disjunction of two images."),
- templateFactory.createAllMatTwoSource(opencv_core::bitwise_or)),
+ templateFactory.createAllMatTwoSourceCuda((src1, src2, useCuda, dst) -> {
+ if (useCuda) {
+ opencv_cudaarithm.bitwise_or(src1.getGpu(), src2.getGpu(), dst.rawGpu());
+ } else {
+ bitwise_or(src1.getCpu(), src2.getCpu(), dst.rawCpu());
+ }
+ })),
new OperationMetaData(CVOperation.defaults("CV bitwise_xor",
"Calculate the per-element bit-wise \"exclusive or\" on two images."),
- templateFactory.createAllMatTwoSource(opencv_core::bitwise_xor)),
+ templateFactory.createAllMatTwoSourceCuda((src1, src2, useCuda, dst) -> {
+ if (useCuda) {
+ opencv_cudaarithm.bitwise_xor(src1.getGpu(), src2.getGpu(), dst.rawGpu());
+ } else {
+ bitwise_xor(src1.getCpu(), src2.getCpu(), dst.rawCpu());
+ }
+ })),
new OperationMetaData(CVOperation.defaults("CV compare",
"Compare each pixel in two images using a given rule."),
- templateFactory.create(
- SocketHints.Inputs.createMatSocketHint("src1", false),
- SocketHints.Inputs.createMatSocketHint("src2", false),
+ templateFactory.createCuda(
+ SocketHints.createImageSocketHint("src1"),
+ SocketHints.createImageSocketHint("src2"),
SocketHints.createEnumSocketHint("cmpop", CmpTypesEnum.CMP_EQ),
- SocketHints.Outputs.createMatSocketHint("dst"),
- (src1, src2, cmp, dst) -> {
- opencv_core.compare(src1, src2, dst, cmp.value);
+ SocketHints.createImageSocketHint("dst"),
+ (src1, src2, cmp, useCuda, dst) -> {
+ int cmpop = cmp.value;
+ if (useCuda) {
+ opencv_cudaarithm.compare(src1.getGpu(), src2.getGpu(), dst.rawGpu(), cmpop);
+ } else {
+ compare(src1.getCpu(), src2.getCpu(), dst.rawCpu(), cmpop);
+ }
}
)),
new OperationMetaData(CVOperation.defaults("CV divide",
"Perform per-pixel division of two images."),
templateFactory.create(
- SocketHints.Inputs.createMatSocketHint("src1", false),
- SocketHints.Inputs.createMatSocketHint("src2", false),
+ SocketHints.createImageSocketHint("src1"),
+ SocketHints.createImageSocketHint("src2"),
SocketHints.Inputs.createNumberSpinnerSocketHint("scale", 1.0, -Double.MAX_VALUE,
Double.MAX_VALUE),
- SocketHints.Outputs.createMatSocketHint("dst"),
+ SocketHints.createImageSocketHint("dst"),
(src1, src2, scale, dst) -> {
- opencv_core.divide(src1, src2, dst, scale.doubleValue(), -1);
+ divide(src1.getCpu(), src2.getCpu(), dst.rawCpu(), scale.doubleValue(), -1);
}
)),
new OperationMetaData(CVOperation.defaults("CV extractChannel",
"Extract a single channel from a image."),
templateFactory.create(
- SocketHints.Inputs.createMatSocketHint("src", false),
+ SocketHints.createImageSocketHint("src"),
SocketHints.Inputs.createNumberSpinnerSocketHint("channel", 0, 0, Integer
.MAX_VALUE),
- SocketHints.Outputs.createMatSocketHint("dst"),
+ SocketHints.createImageSocketHint("dst"),
(src1, coi, dst) -> {
- opencv_core.extractChannel(src1, dst, coi.intValue());
+ extractChannel(src1.getCpu(), dst.rawCpu(), coi.intValue());
}
)),
new OperationMetaData(CVOperation.defaults("CV flip",
"Flip image around vertical, horizontal, or both axes."),
templateFactory.create(
- SocketHints.Inputs.createMatSocketHint("src", false),
+ SocketHints.createImageSocketHint("src"),
SocketHints.createEnumSocketHint("flipCode", FlipCode.Y_AXIS),
- SocketHints.Outputs.createMatSocketHint("dst"),
+ SocketHints.createImageSocketHint("dst"),
(src, flipCode, dst) -> {
- opencv_core.flip(src, dst, flipCode.value);
+ flip(src.getCpu(), dst.rawCpu(), flipCode.value);
}
)),
new OperationMetaData(CVOperation.defaults("CV max",
"Calculate per-element maximum of two images."),
- templateFactory.createAllMatTwoSource(opencv_core::max)),
+ templateFactory.createAllMatTwoSource((src1, src2, dst) -> {
+ max(src1.getCpu(), src2.getCpu(), dst.rawCpu());
+ })),
new OperationMetaData(CVOperation.defaults("CV min",
"Calculate the per-element minimum of two images."),
- templateFactory.createAllMatTwoSource(opencv_core::min)),
+ templateFactory.createAllMatTwoSource((src1, src2, dst) -> {
+ min(src1.getCpu(), src2.getCpu(), dst.rawCpu());
+ })),
new OperationMetaData(CVOperation.defaults("CV multiply",
"Calculate the per-pixel scaled product of two images."),
templateFactory.create(
- SocketHints.Inputs.createMatSocketHint("src1", false),
- SocketHints.Inputs.createMatSocketHint("src2", false),
+ SocketHints.createImageSocketHint("src1"),
+ SocketHints.createImageSocketHint("src2"),
SocketHints.Inputs.createNumberSpinnerSocketHint("scale", 1.0, Integer.MIN_VALUE,
Integer.MAX_VALUE),
- SocketHints.Outputs.createMatSocketHint("dst"),
+ SocketHints.createImageSocketHint("dst"),
(src1, src2, scale, dst) -> {
- opencv_core.multiply(src1, src2, dst, scale.doubleValue(), -1);
+ multiply(src1.getCpu(), src2.getCpu(), dst.getCpu(), scale.doubleValue(), -1);
}
)),
new OperationMetaData(CVOperation.defaults("CV scaleAdd",
"Calculate the sum of two images where one image is multiplied by a scalar."),
templateFactory.create(
- SocketHints.Inputs.createMatSocketHint("src1", false),
+ SocketHints.createImageSocketHint("src1"),
SocketHints.Inputs.createNumberSpinnerSocketHint("scale", 1.0),
- SocketHints.Inputs.createMatSocketHint("src2", false),
- SocketHints.Outputs.createMatSocketHint("dst"),
+ SocketHints.createImageSocketHint("src2"),
+ SocketHints.createImageSocketHint("dst"),
(src1, alpha, src2, dst) -> {
- opencv_core.scaleAdd(src1, alpha.doubleValue(), src2, dst);
+ scaleAdd(src1.getCpu(), alpha.doubleValue(), src2.getCpu(), dst.rawCpu());
}
)),
new OperationMetaData(CVOperation.defaults("CV subtract",
"Calculate the per-pixel difference between two images."),
- templateFactory.createAllMatTwoSource(opencv_core::subtract)),
+ templateFactory.createAllMatTwoSourceCuda((src1, src2, useCuda, dst) -> {
+ if (useCuda) {
+ opencv_cudaarithm.subtract(src1.getGpu(), src2.getGpu(), dst.rawGpu());
+ } else {
+ subtract(src1.getCpu(), src2.getCpu(), dst.rawCpu());
+ }
+ })),
new OperationMetaData(CVOperation.defaults("CV transpose",
"Calculate the transpose of an image."),
- templateFactory.createAllMatOneSource(opencv_core::transpose))
+ templateFactory.createAllMatOneSource((src, dst) -> {
+ transpose(src.getCpu(), dst.rawCpu());
+ }))
);
this.imgprocOperation = ImmutableList.of(
new OperationMetaData(CVOperation.defaults("CV adaptiveThreshold",
"Transforms a grayscale image to a binary image)."),
templateFactory.create(
- SocketHints.Inputs.createMatSocketHint("src", false),
+ SocketHints.createImageSocketHint("src"),
SocketHints.Inputs.createNumberSpinnerSocketHint("maxValue", 0.0),
SocketHints.createEnumSocketHint("adaptiveMethod",
AdaptiveThresholdTypesEnum.ADAPTIVE_THRESH_MEAN_C),
@@ -186,9 +275,9 @@ public class CVOperations {
CVAdaptThresholdTypesEnum.THRESH_BINARY),
SocketHints.Inputs.createNumberSpinnerSocketHint("blockSize", 0.0),
SocketHints.Inputs.createNumberSpinnerSocketHint("C", 0.0),
- SocketHints.Outputs.createMatSocketHint("dst"),
+ SocketHints.createImageSocketHint("dst"),
(src, maxValue, adaptiveMethod, thresholdType, blockSize, c, dst) -> {
- opencv_imgproc.adaptiveThreshold(src, dst, maxValue.doubleValue(),
+ adaptiveThreshold(src.getCpu(), dst.rawCpu(), maxValue.doubleValue(),
adaptiveMethod.value, thresholdType.value, blockSize.intValue(), c
.doubleValue());
}
@@ -197,54 +286,43 @@ public class CVOperations {
new OperationMetaData(CVOperation.defaults("CV applyColorMap",
"Apply a MATLAB equivalent colormap to an image."),
templateFactory.create(
- SocketHints.Inputs.createMatSocketHint("src", false),
+ SocketHints.createImageSocketHint("src"),
SocketHints.createEnumSocketHint("colormap", ColormapTypesEnum.COLORMAP_AUTUMN),
- SocketHints.Outputs.createMatSocketHint("dst"),
+ SocketHints.createImageSocketHint("dst"),
(src, colormap, dst) -> {
- opencv_imgproc.applyColorMap(src, dst, colormap.value);
- }
- )),
-
- new OperationMetaData(CVOperation.defaults("CV Canny",
- "Apply a \"canny edge detection\" algorithm to an image."),
- templateFactory.create(
- SocketHints.Inputs.createMatSocketHint("image", false),
- SocketHints.Inputs.createNumberSpinnerSocketHint("threshold1", 0.0),
- SocketHints.Inputs.createNumberSpinnerSocketHint("threshold2", 0.0),
- SocketHints.Inputs.createNumberSpinnerSocketHint("apertureSize", 3),
- SocketHints.Inputs.createCheckboxSocketHint("L2gradient", false),
- SocketHints.Outputs.createMatSocketHint("edges"),
- (image, threshold1, threshold2, apertureSize, l2gradient, edges) -> {
- opencv_imgproc.Canny(image, edges, threshold1.doubleValue(), threshold2
- .doubleValue(), apertureSize.intValue(), l2gradient);
+ applyColorMap(src.getCpu(), dst.rawCpu(), colormap.value);
}
)),
new OperationMetaData(CVOperation.defaults("CV cvtColor",
"Convert an image from one color space to another."),
- templateFactory.create(
- SocketHints.Inputs.createMatSocketHint("src", false),
+ templateFactory.createCuda(
+ SocketHints.createImageSocketHint("src"),
SocketHints.createEnumSocketHint("code", ColorConversionCodesEnum.COLOR_BGR2BGRA),
- SocketHints.Outputs.createMatSocketHint("dst"),
- (src, code, dst) -> {
- opencv_imgproc.cvtColor(src, dst, code.value);
+ SocketHints.createImageSocketHint("dst"),
+ (src, code, useCuda, dst) -> {
+ if (useCuda) {
+ opencv_cudaimgproc.cvtColor(dst.getGpu(), dst.rawGpu(), code.value);
+ } else {
+ cvtColor(src.getCpu(), dst.rawCpu(), code.value);
+ }
}
)),
new OperationMetaData(CVOperation.defaults("CV dilate",
"Expands areas of higher values in an image."),
templateFactory.create(
- SocketHints.Inputs.createMatSocketHint("src", false),
- SocketHints.Inputs.createMatSocketHint("kernel", true),
+ SocketHints.createImageSocketHint("src"),
+ SocketHints.createImageSocketHint("kernel"),
new SocketHint.Builder<>(Point.class).identifier("anchor").initialValueSupplier(
() -> new Point(-1, -1)).build(),
SocketHints.Inputs.createNumberSpinnerSocketHint("iterations", 1),
SocketHints.createEnumSocketHint("borderType", BorderTypesEnum.BORDER_CONSTANT),
new SocketHint.Builder<>(Scalar.class).identifier("borderValue")
.initialValueSupplier(opencv_imgproc::morphologyDefaultBorderValue).build(),
- SocketHints.Outputs.createMatSocketHint("dst"),
+ SocketHints.createImageSocketHint("dst"),
(src, kernel, anchor, iterations, borderType, borderValue, dst) -> {
- opencv_imgproc.dilate(src, dst, kernel, anchor, iterations.intValue(),
+ dilate(src.getCpu(), dst.rawCpu(), kernel.getCpu(), anchor, iterations.intValue(),
borderType.value, borderValue);
}
)),
@@ -252,33 +330,33 @@ public class CVOperations {
new OperationMetaData(CVOperation.defaults("CV erode",
"Expands areas of lower values in an image."),
templateFactory.create(
- SocketHints.Inputs.createMatSocketHint("src", false),
- SocketHints.Inputs.createMatSocketHint("kernel", true),
+ SocketHints.createImageSocketHint("src"),
+ SocketHints.createImageSocketHint("kernel"),
new SocketHint.Builder<>(Point.class).identifier("anchor").initialValueSupplier(
() -> new Point(-1, -1)).build(),
SocketHints.Inputs.createNumberSpinnerSocketHint("iterations", 1),
SocketHints.createEnumSocketHint("borderType", BorderTypesEnum.BORDER_CONSTANT),
new SocketHint.Builder<>(Scalar.class).identifier("borderValue")
.initialValueSupplier(opencv_imgproc::morphologyDefaultBorderValue).build(),
- SocketHints.Outputs.createMatSocketHint("dst"),
+ SocketHints.createImageSocketHint("dst"),
(src, kernel, anchor, iterations, borderType, borderValue, dst) -> {
- opencv_imgproc.erode(src, dst, kernel, anchor, iterations.intValue(),
- borderType.value, borderValue);
+ opencv_imgproc.erode(src.getCpu(), dst.rawCpu(), kernel.getCpu(), anchor,
+ iterations.intValue(), borderType.value, borderValue);
}
)),
new OperationMetaData(CVOperation.defaults("CV GaussianBlur",
"Apply a Gaussian blur to an image."),
templateFactory.create(
- SocketHints.Inputs.createMatSocketHint("src", true),
+ SocketHints.createImageSocketHint("src"),
new SocketHint.Builder<>(Size.class).identifier("ksize").initialValueSupplier(()
-> new Size(1, 1)).build(),
SocketHints.Inputs.createNumberSpinnerSocketHint("sigmaX", 0.0),
- SocketHints.Inputs.createNumberSpinnerSocketHint("sigmaY", 0.0), SocketHints
- .createEnumSocketHint("borderType", CVBorderTypesEnum.BORDER_DEFAULT),
- SocketHints.Outputs.createMatSocketHint("dst"),
+ SocketHints.Inputs.createNumberSpinnerSocketHint("sigmaY", 0.0),
+ SocketHints.createEnumSocketHint("borderType", CVBorderTypesEnum.BORDER_DEFAULT),
+ SocketHints.createImageSocketHint("dst"),
(src, ksize, sigmaX, sigmaY, borderType, dst) -> {
- opencv_imgproc.GaussianBlur(src, dst, ksize, sigmaX.doubleValue(), sigmaY
+ GaussianBlur(src.getCpu(), dst.rawCpu(), ksize, sigmaX.doubleValue(), sigmaY
.doubleValue(), borderType.value);
}
)),
@@ -286,14 +364,14 @@ public class CVOperations {
new OperationMetaData(CVOperation.defaults("CV Laplacian",
"Find edges by calculating the Laplacian for the given image."),
templateFactory.create(
- SocketHints.Inputs.createMatSocketHint("src", false),
+ SocketHints.createImageSocketHint("src"),
SocketHints.Inputs.createNumberSpinnerSocketHint("ksize", 1),
SocketHints.Inputs.createNumberSpinnerSocketHint("scale", 1.0),
SocketHints.Inputs.createNumberSpinnerSocketHint("delta", 0.0),
SocketHints.createEnumSocketHint("borderType", BorderTypesEnum.BORDER_DEFAULT),
- SocketHints.Outputs.createMatSocketHint("dst"),
+ SocketHints.createImageSocketHint("dst"),
(src, ksize, scale, delta, borderType, dst) -> {
- opencv_imgproc.Laplacian(src, dst, 0, ksize.intValue(), scale.doubleValue(),
+ Laplacian(src.getCpu(), dst.rawCpu(), 0, ksize.intValue(), scale.doubleValue(),
delta.doubleValue(), borderType.value);
}
)),
@@ -301,18 +379,18 @@ public class CVOperations {
new OperationMetaData(CVOperation.defaults("CV medianBlur",
"Apply a Median blur to an image."),
templateFactory.create(
- SocketHints.Inputs.createMatSocketHint("src", false),
+ SocketHints.createImageSocketHint("src"),
SocketHints.Inputs.createNumberSpinnerSocketHint("ksize", 1, 1, Integer.MAX_VALUE),
- SocketHints.Outputs.createMatSocketHint("dst"),
+ SocketHints.createImageSocketHint("dst"),
(src, ksize, dst) -> {
- opencv_imgproc.medianBlur(src, dst, ksize.intValue());
+ medianBlur(src.getCpu(), dst.rawCpu(), ksize.intValue());
}
)),
new OperationMetaData(CVOperation.defaults("CV rectangle",
"Draw a rectangle (outline or filled) on an image."),
templateFactory.create(
- SocketHints.Inputs.createMatSocketHint("src", false),
+ SocketHints.createImageSocketHint("src"),
SocketHints.Inputs.createPointSocketHint("pt1", 0, 0),
SocketHints.Inputs.createPointSocketHint("pt2", 0, 0),
new SocketHint.Builder<>(Scalar.class).identifier("color").initialValueSupplier(
@@ -321,12 +399,12 @@ public class CVOperations {
.MIN_VALUE, Integer.MAX_VALUE),
SocketHints.createEnumSocketHint("lineType", LineTypesEnum.LINE_8),
SocketHints.Inputs.createNumberSpinnerSocketHint("shift", 0),
- SocketHints.Outputs.createMatSocketHint("dst"),
+ SocketHints.createImageSocketHint("dst"),
(src, pt1, pt2, color, thickness, lineType, shift, dst) -> {
// Rectangle only has one input and it modifies it so we have to copy the input
// image to the dst
src.copyTo(dst);
- opencv_imgproc.rectangle(dst, pt1, pt2, color, thickness.intValue(), lineType
+ rectangle(dst.rawCpu(), pt1, pt2, color, thickness.intValue(), lineType
.value, shift.intValue());
}
)),
@@ -334,49 +412,74 @@ public class CVOperations {
new OperationMetaData(CVOperation.defaults("CV resize",
"Resizes the image to the specified size."),
templateFactory.create(
- SocketHints.Inputs.createMatSocketHint("src", false),
+ SocketHints.createImageSocketHint("src"),
new SocketHint.Builder<>(Size.class).identifier("dsize").initialValueSupplier(()
-> new Size(0, 0)).build(),
SocketHints.Inputs.createNumberSpinnerSocketHint("fx", .25), SocketHints.Inputs
.createNumberSpinnerSocketHint("fy", .25),
SocketHints.createEnumSocketHint("interpolation", InterpolationFlagsEnum
.INTER_LINEAR),
- SocketHints.Outputs.createMatSocketHint("dst"),
+ SocketHints.createImageSocketHint("dst"),
(src, dsize, fx, fy, interpolation, dst) -> {
- opencv_imgproc.resize(src, dst, dsize, fx.doubleValue(), fy.doubleValue(),
+ resize(src.getCpu(), dst.rawCpu(), dsize, fx.doubleValue(), fy.doubleValue(),
interpolation.value);
}
)),
new OperationMetaData(CVOperation.defaults("CV Sobel",
"Find edges by calculating the requested derivative order for the given image."),
- templateFactory.create(
- SocketHints.Inputs.createMatSocketHint("src", false),
+ templateFactory.createCuda(
+ SocketHints.createImageSocketHint("src"),
SocketHints.Inputs.createNumberSpinnerSocketHint("dx", 0),
SocketHints.Inputs.createNumberSpinnerSocketHint("dy", 0),
SocketHints.Inputs.createNumberSpinnerSocketHint("ksize", 3),
SocketHints.Inputs.createNumberSpinnerSocketHint("scale", 1),
SocketHints.Inputs.createNumberSpinnerSocketHint("delta", 0),
SocketHints.createEnumSocketHint("borderType", BorderTypesEnum.BORDER_DEFAULT),
- SocketHints.Outputs.createMatSocketHint("dst"),
- (src, dx, dy, ksize, scale, delta, borderType, dst) -> {
- opencv_imgproc.Sobel(src, dst, 0, dx.intValue(), dy.intValue(),
- ksize.intValue(), scale.doubleValue(), delta.doubleValue(), borderType.value);
+ SocketHints.createImageSocketHint("dst"),
+ (src, dx, dy, ksize, scale, delta, borderType, useCuda, dst) -> {
+ if (useCuda) {
+ try (Filter sobelFilter = createSobelFilter(
+ src.type(),
+ src.type(),
+ dx.intValue(),
+ dy.intValue(),
+ ksize.intValue(),
+ scale.doubleValue(),
+ borderType.value,
+ borderType.value)) {
+ sobelFilter.apply(src.getGpu(), dst.rawGpu());
+ }
+ } else {
+ Sobel(src.getCpu(), dst.rawCpu(), 0, dx.intValue(), dy.intValue(),
+ ksize.intValue(), scale.doubleValue(), delta.doubleValue(),
+ borderType.value);
+ }
}
)),
new OperationMetaData(CVOperation.defaults("CV Threshold",
"Apply a fixed-level threshold to each array element in an image.",
"CV threshold"),
- templateFactory.create(
- SocketHints.Inputs.createMatSocketHint("src", false),
+ templateFactory.createCuda(
+ SocketHints.createImageSocketHint("src"),
SocketHints.Inputs.createNumberSpinnerSocketHint("thresh", 0),
SocketHints.Inputs.createNumberSpinnerSocketHint("maxval", 0),
SocketHints.createEnumSocketHint("type", CVThresholdTypesEnum.THRESH_BINARY),
- SocketHints.Outputs.createMatSocketHint("dst"),
- (src, thresh, maxval, type, dst) -> {
- opencv_imgproc.threshold(src, dst, thresh.doubleValue(), maxval.doubleValue(),
- type.value);
+ SocketHints.createImageSocketHint("dst"),
+ (src, thresh, maxval, type, useCuda, dst) -> {
+ if (useCuda) {
+ opencv_cudaarithm.threshold(
+ src.getGpu(),
+ dst.rawGpu(),
+ thresh.doubleValue(),
+ maxval.doubleValue(),
+ type.value
+ );
+ } else {
+ threshold(src.getCpu(), dst.rawCpu(), thresh.doubleValue(),
+ maxval.doubleValue(), type.value);
+ }
}
))
);
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/CudaOperation.java b/core/src/main/java/edu/wpi/grip/core/operations/CudaOperation.java
new file mode 100644
index 0000000000..a6e572a3d6
--- /dev/null
+++ b/core/src/main/java/edu/wpi/grip/core/operations/CudaOperation.java
@@ -0,0 +1,85 @@
+package edu.wpi.grip.core.operations;
+
+import edu.wpi.grip.core.MatWrapper;
+import edu.wpi.grip.core.Operation;
+import edu.wpi.grip.core.sockets.CudaSocket;
+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 org.bytedeco.javacpp.opencv_core.GpuMat;
+import org.bytedeco.javacpp.opencv_core.Mat;
+
+/**
+ * A partial implementation of Operation that has the option to use CUDA acceleration.
+ */
+public abstract class CudaOperation implements Operation {
+
+ protected final SocketHint inputHint =
+ SocketHints.createImageSocketHint("Input");
+ protected final SocketHint gpuHint =
+ SocketHints.createBooleanSocketHint("Prefer GPU", false);
+ protected final SocketHint outputHint = SocketHints.createImageSocketHint("Output");
+
+ /**
+ * Default image input socket.
+ */
+ protected final InputSocket inputSocket;
+ /**
+ * Input socket telling the operation to prefer to use CUDA acceleration when possible.
+ */
+ protected final CudaSocket gpuSocket;
+ /**
+ * Default image output socket.
+ */
+ protected final OutputSocket outputSocket;
+
+ /**
+ * The mat used for an input to the CPU operation.
+ */
+ protected final Mat cpuIn = new Mat();
+
+ /**
+ * The mat used for an input to the CUDA operation.
+ */
+ protected final GpuMat gpuIn = new GpuMat();
+
+ /**
+ * The output mat of a CPU operation.
+ */
+ protected final Mat cpuOut = new Mat();
+
+ /**
+ * The output mat of a CUDA operation.
+ */
+ protected final GpuMat gpuOut = new GpuMat();
+
+ protected CudaOperation(InputSocket.Factory isf, OutputSocket.Factory osf) {
+ inputSocket = isf.create(inputHint);
+ gpuSocket = isf.createCuda(gpuHint);
+ outputSocket = osf.create(outputHint);
+
+ inputSocket.setValue(MatWrapper.using(cpuIn, gpuIn));
+ outputSocket.setValue(MatWrapper.using(cpuOut, gpuOut));
+ }
+
+ @Override
+ public void cleanUp() {
+ cpuIn.deallocate();
+ gpuIn.deallocate();
+ cpuOut.deallocate();
+ gpuOut.deallocate();
+ }
+
+ /**
+ * Checks the {@link #gpuSocket} to see if this operation should prefer to use the CUDA codepath.
+ *
+ * @return true if this operation should prefer to use CUDA, false if it should only use the CPU
+ */
+ protected boolean preferCuda() {
+ return gpuSocket.isCudaAvailable()
+ && gpuSocket.getValue().orElse(false);
+ }
+
+}
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/composite/BlobsReport.java b/core/src/main/java/edu/wpi/grip/core/operations/composite/BlobsReport.java
index 4671533e8c..881c3b16d9 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/composite/BlobsReport.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/composite/BlobsReport.java
@@ -1,6 +1,7 @@
package edu.wpi.grip.core.operations.composite;
import edu.wpi.grip.annotation.operation.PublishableObject;
+import edu.wpi.grip.core.MatWrapper;
import edu.wpi.grip.core.operations.network.PublishValue;
import edu.wpi.grip.core.operations.network.Publishable;
import edu.wpi.grip.core.sockets.NoSocketTypeLabel;
@@ -10,25 +11,23 @@
import java.util.Collections;
import java.util.List;
-import static org.bytedeco.javacpp.opencv_core.Mat;
-
/**
* This class is used as the output of operations that detect blobs in an image.
*/
@PublishableObject
@NoSocketTypeLabel
public class BlobsReport implements Publishable {
- private final Mat input;
+ private final MatWrapper input;
private final List blobs;
/**
* Create an empty blob report. This is used as the default value for sockets
*/
public BlobsReport() {
- this(new Mat(), Collections.emptyList());
+ this(MatWrapper.emptyWrapper(), Collections.emptyList());
}
- public BlobsReport(Mat input, List blobs) {
+ public BlobsReport(MatWrapper input, List blobs) {
this.input = input;
this.blobs = blobs;
}
@@ -40,7 +39,7 @@ public List getBlobs() {
/**
* @return The original image that the blob detection was performed on.
*/
- public Mat getInput() {
+ public MatWrapper getInput() {
return this.input;
}
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/composite/BlurOperation.java b/core/src/main/java/edu/wpi/grip/core/operations/composite/BlurOperation.java
index 15bc184d7a..326bf7b9c6 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/composite/BlurOperation.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/composite/BlurOperation.java
@@ -2,7 +2,9 @@
import edu.wpi.grip.annotation.operation.Description;
import edu.wpi.grip.annotation.operation.OperationCategory;
+import edu.wpi.grip.core.MatWrapper;
import edu.wpi.grip.core.Operation;
+import edu.wpi.grip.core.operations.CudaOperation;
import edu.wpi.grip.core.sockets.InputSocket;
import edu.wpi.grip.core.sockets.OutputSocket;
import edu.wpi.grip.core.sockets.SocketHint;
@@ -11,10 +13,18 @@
import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
+import org.bytedeco.javacpp.opencv_core.GpuMat;
+import org.bytedeco.javacpp.opencv_cudaimgproc;
+
import java.util.List;
-import static org.bytedeco.javacpp.opencv_core.Mat;
+import static org.bytedeco.javacpp.opencv_core.CV_8UC3;
+import static org.bytedeco.javacpp.opencv_core.CV_8UC4;
import static org.bytedeco.javacpp.opencv_core.Size;
+import static org.bytedeco.javacpp.opencv_cudafilters.Filter;
+import static org.bytedeco.javacpp.opencv_cudafilters.createGaussianFilter;
+import static org.bytedeco.javacpp.opencv_imgproc.CV_BGR2BGRA;
+import static org.bytedeco.javacpp.opencv_imgproc.CV_BGRA2BGR;
import static org.bytedeco.javacpp.opencv_imgproc.GaussianBlur;
import static org.bytedeco.javacpp.opencv_imgproc.bilateralFilter;
import static org.bytedeco.javacpp.opencv_imgproc.blur;
@@ -27,27 +37,27 @@
summary = "Blurs an image to remove noise",
category = OperationCategory.IMAGE_PROCESSING,
iconName = "blur")
-public class BlurOperation implements Operation {
+public class BlurOperation extends CudaOperation {
- private final SocketHint inputHint = SocketHints.Inputs.createMatSocketHint("Input", false);
private final SocketHint typeHint = SocketHints.createEnumSocketHint("Type", Type.BOX);
private final SocketHint radiusHint = SocketHints.Inputs
.createNumberSliderSocketHint("Radius", 0.0, 0.0, 100.0);
- private final SocketHint outputHint = SocketHints.Inputs.createMatSocketHint("Output", true);
- private final InputSocket inputSocket;
private final InputSocket typeSocket;
private final InputSocket radiusSocket;
- private final OutputSocket outputSocket;
+
+ private int lastKernelSize = 0;
+ // used to covert 3-channel images to 4-channel for CUDA
+ private final GpuMat upcast = new GpuMat();
+ private Filter gpuGaussianFilter;
+ //private Filter gpuMedianFilter;
@Inject
@SuppressWarnings("JavadocMethod")
- public BlurOperation(InputSocket.Factory inputSocketFactory,
- OutputSocket.Factory outputSocketFactory) {
- this.inputSocket = inputSocketFactory.create(inputHint);
+ public BlurOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
+ outputSocketFactory) {
+ super(inputSocketFactory, outputSocketFactory);
this.typeSocket = inputSocketFactory.create(typeHint);
this.radiusSocket = inputSocketFactory.create(radiusHint);
-
- this.outputSocket = outputSocketFactory.create(outputHint);
}
@Override
@@ -55,7 +65,8 @@ public List getInputSockets() {
return ImmutableList.of(
inputSocket,
typeSocket,
- radiusSocket
+ radiusSocket,
+ gpuSocket
);
}
@@ -68,47 +79,115 @@ public List getOutputSockets() {
@Override
public void perform() {
- final Mat input = inputSocket.getValue().get();
+ final MatWrapper input = inputSocket.getValue().get();
+ if (input.empty()) {
+ return;
+ }
final Type type = typeSocket.getValue().get();
final Number radius = radiusSocket.getValue().get();
- final Mat output = outputSocket.getValue().get();
+ final MatWrapper output = outputSocket.getValue().get();
+ int imageType;
int kernelSize;
+ boolean kernelChange;
+
+ if (preferCuda()) {
+ if (input.type() == CV_8UC3) {
+ // GPU filters generally don't take BGR images, but will take BGRA
+ // So we convert the BGR image to BGRA here and convert it back at the end
+ // Note that this doesn't care about the actual pixel format because we're
+ // converting to the same format, just with an extra channel
+ imageType = CV_8UC4;
+ opencv_cudaimgproc.cvtColor(input.getGpu(), upcast, CV_BGR2BGRA);
+ gpuIn.put(upcast);
+ } else {
+ input.copyTo(gpuIn);
+ imageType = input.type();
+ }
+ } else {
+ imageType = input.type();
+ }
switch (type) {
case BOX:
// Box filter kernels must have an odd size
kernelSize = 2 * radius.intValue() + 1;
- blur(input, output, new Size(kernelSize, kernelSize));
+
+ // Don't bother with CUDA acceleration here; CPU is fast enough that memory copies
+ // will remove the speedups from CUDA
+ blur(input.getCpu(), output.getCpu(), new Size(kernelSize, kernelSize));
break;
case GAUSSIAN:
// A Gaussian blur radius is a standard deviation, so a kernel that extends three radii
- // in either direction
- // from the center should account for 99.7% of the theoretical influence on each pixel.
+ // in either direction from the center should account for 99.7% of the theoretical
+ // influence on each pixel.
kernelSize = 6 * radius.intValue() + 1;
- GaussianBlur(input, output, new Size(kernelSize, kernelSize), radius.doubleValue());
+ kernelChange = kernelSize != lastKernelSize;
+ lastKernelSize = kernelSize;
+ if (preferCuda()/* && kernelSize < 32*/) {
+ // GPU gaussian blurs require kernel size in 0..31
+ if (kernelChange || gpuGaussianFilter == null) {
+ gpuGaussianFilter = createGaussianFilter(imageType, imageType,
+ new Size(kernelSize, kernelSize), radius.doubleValue());
+ }
+ gpuGaussianFilter.apply(gpuIn, gpuOut);
+ output.set(gpuOut);
+ } else {
+ GaussianBlur(input.getCpu(), output.getCpu(), new Size(kernelSize, kernelSize),
+ radius.doubleValue());
+ }
break;
case MEDIAN:
kernelSize = 2 * radius.intValue() + 1;
- medianBlur(input, output, kernelSize);
+ // FIXME: CUDA median filters is broken - run on CPU only for now
+ /*
+ kernelChange = kernelSize != lastKernelSize;
+ lastKernelSize = kernelSize;
+ if (preferCuda() && imageType == CV_8UC1) {
+ // GPU median filters only work on grayscale images
+ if (kernelChange || gpuMedianFilter == null) {
+ gpuMedianFilter = createMedianFilter(imageType, kernelSize);
+ }
+ gpuMedianFilter.apply(gpuIn, gpuOut);
+ output.set(gpuOut);
+ } else {
+ medianBlur(input.getCpu(), output.rawCpu(), kernelSize);
+ }
+ */
+ medianBlur(input.getCpu(), output.rawCpu(), kernelSize);
break;
case BILATERAL_FILTER:
- bilateralFilter(input, output, -1, radius.doubleValue(), radius.doubleValue());
+ if (preferCuda()) {
+ opencv_cudaimgproc.bilateralFilter(gpuIn, gpuOut,
+ -1, radius.floatValue(), radius.floatValue() / 6);
+ output.set(gpuOut);
+ } else {
+ bilateralFilter(input.getCpu(), output.rawCpu(),
+ -1, radius.doubleValue(), radius.doubleValue() / 6);
+ }
break;
default:
throw new IllegalArgumentException("Illegal blur type: " + type);
}
+ if (preferCuda() && output.type() == CV_8UC4 && input.type() == CV_8UC3) {
+ // Remove the alpha channel that was added for GPU filtering
+ opencv_cudaimgproc.cvtColor(output.getGpu(), output.rawGpu(), CV_BGRA2BGR);
+ }
+
+ //output.set(output);
outputSocket.setValue(output);
}
private enum Type {
- BOX("Box Blur"), GAUSSIAN("Gaussian Blur"), MEDIAN("Median Filter"),
+ BOX("Box Blur"),
+ GAUSSIAN("Gaussian Blur"),
+ MEDIAN("Median Filter"),
BILATERAL_FILTER("Bilateral Filter");
private final String label;
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/composite/CannyEdgeOperation.java b/core/src/main/java/edu/wpi/grip/core/operations/composite/CannyEdgeOperation.java
new file mode 100644
index 0000000000..52c52fe124
--- /dev/null
+++ b/core/src/main/java/edu/wpi/grip/core/operations/composite/CannyEdgeOperation.java
@@ -0,0 +1,93 @@
+package edu.wpi.grip.core.operations.composite;
+
+import edu.wpi.grip.annotation.operation.Description;
+import edu.wpi.grip.annotation.operation.OperationCategory;
+import edu.wpi.grip.core.operations.CudaOperation;
+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 com.google.common.collect.ImmutableList;
+import com.google.inject.Inject;
+
+import org.bytedeco.javacpp.opencv_cudaimgproc.CannyEdgeDetector;
+
+import java.util.List;
+
+import static org.bytedeco.javacpp.opencv_cudaimgproc.createCannyEdgeDetector;
+import static org.bytedeco.javacpp.opencv_imgproc.Canny;
+
+/**
+ * An operation that performs canny edge detection on an image.
+ */
+@Description(name = "CV Canny",
+ summary = "Performs canny edge detection on a grayscale image",
+ category = OperationCategory.OPENCV,
+ iconName = "opencv")
+public class CannyEdgeOperation extends CudaOperation {
+
+ private final SocketHint lowThreshHint
+ = SocketHints.Inputs.createNumberSpinnerSocketHint("Low threshold", 0);
+ private final SocketHint highThreshHint
+ = SocketHints.Inputs.createNumberSpinnerSocketHint("High threshold", 0);
+ private final SocketHint apertureSizeHint
+ = SocketHints.Inputs.createNumberSpinnerSocketHint("Aperture size", 0);
+ private final SocketHint l2gradientHint
+ = SocketHints.Inputs.createCheckboxSocketHint("L2gradient", false);
+
+ private final InputSocket lowThreshSocket;
+ private final InputSocket highThreshSocket;
+ private final InputSocket apertureSizeSocket;
+ private final InputSocket l2gradientSocket;
+
+ @Inject
+ protected CannyEdgeOperation(InputSocket.Factory isf, OutputSocket.Factory osf) {
+ super(isf, osf);
+ lowThreshSocket = isf.create(lowThreshHint);
+ highThreshSocket = isf.create(highThreshHint);
+ apertureSizeSocket = isf.create(apertureSizeHint);
+ l2gradientSocket = isf.create(l2gradientHint);
+ }
+
+ @Override
+ public List getInputSockets() {
+ return ImmutableList.of(
+ inputSocket,
+ lowThreshSocket,
+ highThreshSocket,
+ apertureSizeSocket,
+ l2gradientSocket,
+ gpuSocket
+ );
+ }
+
+ @Override
+ public List getOutputSockets() {
+ return ImmutableList.of(
+ outputSocket
+ );
+ }
+
+ @Override
+ public void perform() {
+ double lowThresh = lowThreshSocket.getValue().get().doubleValue();
+ double highThresh = highThreshSocket.getValue().get().doubleValue();
+ int apertureSize = apertureSizeSocket.getValue().get().intValue();
+ boolean l2gradient = l2gradientSocket.getValue().get();
+ if (preferCuda()) {
+ try (CannyEdgeDetector cannyEdgeDetector = createCannyEdgeDetector(
+ lowThresh,
+ highThresh,
+ apertureSize,
+ l2gradient)) {
+ cannyEdgeDetector.detect(inputSocket.getValue().get().getGpu(),
+ outputSocket.getValue().get().rawGpu());
+ }
+ } else {
+ Canny(inputSocket.getValue().get().getCpu(), outputSocket.getValue().get().getCpu(),
+ lowThresh, highThresh, apertureSize, l2gradient);
+ }
+ outputSocket.flagChanged();
+ }
+}
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/composite/CascadeClassifierOperation.java b/core/src/main/java/edu/wpi/grip/core/operations/composite/CascadeClassifierOperation.java
index e24bf84308..0bca69bd7d 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/composite/CascadeClassifierOperation.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/composite/CascadeClassifierOperation.java
@@ -2,6 +2,7 @@
import edu.wpi.grip.annotation.operation.Description;
import edu.wpi.grip.annotation.operation.OperationCategory;
+import edu.wpi.grip.core.MatWrapper;
import edu.wpi.grip.core.Operation;
import edu.wpi.grip.core.sockets.InputSocket;
import edu.wpi.grip.core.sockets.OutputSocket;
@@ -29,8 +30,8 @@
iconName = "opencv")
public class CascadeClassifierOperation implements Operation {
- private final SocketHint imageHint =
- SocketHints.Inputs.createMatSocketHint("Image", false);
+ private final SocketHint imageHint =
+ SocketHints.createImageSocketHint("Image");
private final SocketHint classifierHint =
new SocketHint.Builder<>(CascadeClassifier.class)
.identifier("Classifier")
@@ -49,7 +50,7 @@ public class CascadeClassifierOperation implements Operation {
.initialValue(RectsReport.NIL)
.build();
- private final InputSocket imageSocket;
+ private final InputSocket imageSocket;
private final InputSocket classifierSocket;
private final InputSocket scaleSocket;
private final InputSocket minNeighborsSocket;
@@ -93,7 +94,8 @@ public void perform() {
if (!imageSocket.getValue().isPresent() || !classifierSocket.getValue().isPresent()) {
return;
}
- final Mat image = imageSocket.getValue().get();
+ final MatWrapper input = imageSocket.getValue().get();
+ final Mat image = input.getCpu();
if (image.empty() || image.channels() != 3) {
throw new IllegalArgumentException("A cascade classifier needs a three-channel input");
}
@@ -108,7 +110,7 @@ public void perform() {
for (int i = 0; i < detections.size(); i++) {
rects.add(detections.get(i));
}
- output.setValue(new RectsReport(image, rects));
+ output.setValue(new RectsReport(input, rects));
}
}
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/composite/DesaturateOperation.java b/core/src/main/java/edu/wpi/grip/core/operations/composite/DesaturateOperation.java
index 59ee2e7c17..00ea135388 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/composite/DesaturateOperation.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/composite/DesaturateOperation.java
@@ -2,18 +2,19 @@
import edu.wpi.grip.annotation.operation.Description;
import edu.wpi.grip.annotation.operation.OperationCategory;
+import edu.wpi.grip.core.MatWrapper;
import edu.wpi.grip.core.Operation;
+import edu.wpi.grip.core.operations.CudaOperation;
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 com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
+import org.bytedeco.javacpp.opencv_cudaimgproc;
+
import java.util.List;
-import static org.bytedeco.javacpp.opencv_core.Mat;
import static org.bytedeco.javacpp.opencv_imgproc.COLOR_BGR2GRAY;
import static org.bytedeco.javacpp.opencv_imgproc.COLOR_BGRA2GRAY;
import static org.bytedeco.javacpp.opencv_imgproc.cvtColor;
@@ -25,27 +26,21 @@
summary = "Convert a color image into shades of gray",
category = OperationCategory.IMAGE_PROCESSING,
iconName = "desaturate")
-public class DesaturateOperation implements Operation {
-
- private final SocketHint inputHint = SocketHints.Inputs.createMatSocketHint("Input", false);
- private final SocketHint outputHint = SocketHints.Outputs.createMatSocketHint("Output");
-
- private final InputSocket inputSocket;
- private final OutputSocket outputSocket;
+public class DesaturateOperation extends CudaOperation {
@Inject
@SuppressWarnings("JavadocMethod")
public DesaturateOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
outputSocketFactory) {
- this.inputSocket = inputSocketFactory.create(inputHint);
- this.outputSocket = outputSocketFactory.create(outputHint);
+ super(inputSocketFactory, outputSocketFactory);
}
@Override
public List getInputSockets() {
return ImmutableList.of(
- inputSocket
+ inputSocket,
+ gpuSocket
);
}
@@ -58,23 +53,30 @@ public List getOutputSockets() {
@Override
public void perform() {
- final Mat input = inputSocket.getValue().get();
-
- Mat output = outputSocket.getValue().get();
+ final MatWrapper input = inputSocket.getValue().get();
+ final MatWrapper output = outputSocket.getValue().get();
switch (input.channels()) {
case 1:
// If the input is already one channel, it's already desaturated
- input.copyTo(output);
+ output.set(input);
break;
case 3:
- cvtColor(input, output, COLOR_BGR2GRAY);
+ if (preferCuda()) {
+ opencv_cudaimgproc.cvtColor(input.getGpu(), output.rawGpu(), COLOR_BGR2GRAY);
+ } else {
+ cvtColor(input.getCpu(), output.rawCpu(), COLOR_BGR2GRAY);
+ }
break;
case 4:
- cvtColor(input, output, COLOR_BGRA2GRAY);
+ if (preferCuda()) {
+ opencv_cudaimgproc.cvtColor(input.getGpu(), output.rawGpu(), COLOR_BGRA2GRAY);
+ } else {
+ cvtColor(input.getCpu(), output.rawCpu(), COLOR_BGRA2GRAY);
+ }
break;
default:
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/composite/DistanceTransformOperation.java b/core/src/main/java/edu/wpi/grip/core/operations/composite/DistanceTransformOperation.java
index 17c52ab339..0837d54680 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/composite/DistanceTransformOperation.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/composite/DistanceTransformOperation.java
@@ -2,6 +2,7 @@
import edu.wpi.grip.annotation.operation.Description;
import edu.wpi.grip.annotation.operation.OperationCategory;
+import edu.wpi.grip.core.MatWrapper;
import edu.wpi.grip.core.Operation;
import edu.wpi.grip.core.sockets.InputSocket;
import edu.wpi.grip.core.sockets.OutputSocket;
@@ -31,15 +32,15 @@
iconName = "opencv")
public class DistanceTransformOperation implements Operation {
- private final SocketHint srcHint = SocketHints.Inputs.createMatSocketHint("Input", false);
+ private final SocketHint srcHint = SocketHints.createImageSocketHint("Input");
private final SocketHint typeHint = SocketHints.createEnumSocketHint("Type", Type.DIST_L2);
private final SocketHint maskSizeHint = SocketHints.createEnumSocketHint("Mask size",
MaskSize.ZERO);
- private final SocketHint outputHint = SocketHints.Inputs.createMatSocketHint("Output", true);
- private final InputSocket srcSocket;
+ private final SocketHint outputHint = SocketHints.createImageSocketHint("Output");
+ private final InputSocket srcSocket;
private final InputSocket typeSocket;
private final InputSocket maskSizeSocket;
- private final OutputSocket outputSocket;
+ private final OutputSocket outputSocket;
@Inject
@SuppressWarnings("JavadocMethod")
@@ -70,7 +71,7 @@ public List getOutputSockets() {
@Override
public void perform() {
- final Mat input = srcSocket.getValue().get();
+ final Mat input = srcSocket.getValue().get().getCpu();
if (input.type() != CV_8U) {
throw new IllegalArgumentException("Distance transform only works on 8-bit binary images");
@@ -79,12 +80,12 @@ public void perform() {
final Type type = typeSocket.getValue().get();
final MaskSize maskSize = maskSizeSocket.getValue().get();
- final Mat output = outputSocket.getValue().get();
+ final Mat output = outputSocket.getValue().get().rawCpu();
distanceTransform(input, output, type.value, maskSize.value);
output.convertTo(output, CV_8U);
- outputSocket.setValue(output);
+ outputSocket.flagChanged();
}
private enum Type {
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/composite/FindBlobsOperation.java b/core/src/main/java/edu/wpi/grip/core/operations/composite/FindBlobsOperation.java
index b6efd38fa8..c661a3e37c 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/composite/FindBlobsOperation.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/composite/FindBlobsOperation.java
@@ -2,6 +2,7 @@
import edu.wpi.grip.annotation.operation.Description;
import edu.wpi.grip.annotation.operation.OperationCategory;
+import edu.wpi.grip.core.MatWrapper;
import edu.wpi.grip.core.Operation;
import edu.wpi.grip.core.sockets.InputSocket;
import edu.wpi.grip.core.sockets.OutputSocket;
@@ -28,7 +29,7 @@
iconName = "find-blobs")
public class FindBlobsOperation implements Operation {
- private final SocketHint inputHint = SocketHints.Inputs.createMatSocketHint("Input", false);
+ private final SocketHint inputHint = SocketHints.createImageSocketHint("Input");
private final SocketHint minAreaHint = SocketHints.Inputs
.createNumberSpinnerSocketHint("Min Area", 1);
private final SocketHint> circularityHint = SocketHints.Inputs
@@ -41,7 +42,7 @@ public class FindBlobsOperation implements Operation {
.initialValueSupplier(BlobsReport::new)
.build();
- private final InputSocket inputSocket;
+ private final InputSocket inputSocket;
private final InputSocket minAreaSocket;
private final InputSocket> circularitySocket;
private final InputSocket colorSocket;
@@ -80,7 +81,7 @@ public List getOutputSockets() {
@Override
@SuppressWarnings("unchecked")
public void perform() {
- final Mat input = inputSocket.getValue().get();
+ final Mat input = inputSocket.getValue().get().getCpu();
final Number minArea = minAreaSocket.getValue().get();
final List circularity = circularitySocket.getValue().get();
final Boolean darkBlobs = colorSocket.getValue().get();
@@ -109,6 +110,6 @@ public void perform() {
blobs.add(new BlobsReport.Blob(keyPoint.pt().x(), keyPoint.pt().y(), keyPoint.size()));
}
- outputSocket.setValue(new BlobsReport(input, blobs));
+ outputSocket.setValue(new BlobsReport(inputSocket.getValue().get(), blobs));
}
}
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/composite/FindContoursOperation.java b/core/src/main/java/edu/wpi/grip/core/operations/composite/FindContoursOperation.java
index 45b8506bdf..18285c16f1 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/composite/FindContoursOperation.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/composite/FindContoursOperation.java
@@ -2,6 +2,7 @@
import edu.wpi.grip.annotation.operation.Description;
import edu.wpi.grip.annotation.operation.OperationCategory;
+import edu.wpi.grip.core.MatWrapper;
import edu.wpi.grip.core.Operation;
import edu.wpi.grip.core.sockets.InputSocket;
import edu.wpi.grip.core.sockets.OutputSocket;
@@ -30,8 +31,7 @@
iconName = "find-contours")
public class FindContoursOperation implements Operation {
- private final SocketHint inputHint =
- new SocketHint.Builder<>(Mat.class).identifier("Input").build();
+ private final SocketHint inputHint = SocketHints.createImageSocketHint("Input");
private final SocketHint externalHint =
SocketHints.createBooleanSocketHint("External Only", false);
@@ -41,7 +41,7 @@ public class FindContoursOperation implements Operation {
.identifier("Contours").initialValueSupplier(ContoursReport::new).build();
- private final InputSocket inputSocket;
+ private final InputSocket inputSocket;
private final InputSocket externalSocket;
private final OutputSocket contoursSocket;
@@ -73,7 +73,7 @@ public List getOutputSockets() {
@Override
public void perform() {
- final Mat input = inputSocket.getValue().get();
+ final MatWrapper input = inputSocket.getValue().get();
if (input.empty()) {
return;
}
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/composite/FindLinesOperation.java b/core/src/main/java/edu/wpi/grip/core/operations/composite/FindLinesOperation.java
index 32dc98932f..4f9597ce63 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/composite/FindLinesOperation.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/composite/FindLinesOperation.java
@@ -2,6 +2,7 @@
import edu.wpi.grip.annotation.operation.Description;
import edu.wpi.grip.annotation.operation.OperationCategory;
+import edu.wpi.grip.core.MatWrapper;
import edu.wpi.grip.core.Operation;
import edu.wpi.grip.core.sockets.InputSocket;
import edu.wpi.grip.core.sockets.OutputSocket;
@@ -30,12 +31,12 @@
iconName = "find-lines")
public class FindLinesOperation implements Operation {
- private final SocketHint inputHint = SocketHints.Inputs.createMatSocketHint("Input", false);
+ private final SocketHint inputHint = SocketHints.createImageSocketHint("Input");
private final SocketHint linesHint = new SocketHint.Builder<>(LinesReport.class)
.identifier("Lines").initialValueSupplier(LinesReport::new).build();
- private final InputSocket inputSocket;
+ private final InputSocket inputSocket;
private final OutputSocket linesReportSocket;
@@ -63,17 +64,17 @@ public List getOutputSockets() {
@Override
@SuppressWarnings("unchecked")
public void perform() {
- final Mat input = inputSocket.getValue().get();
+ final MatWrapper input = inputSocket.getValue().get();
final LineSegmentDetector lsd = linesReportSocket.getValue().get().getLineSegmentDetector();
final Mat lines = new Mat();
if (input.channels() == 1) {
- lsd.detect(input, lines);
+ lsd.detect(input.getCpu(), lines);
} else {
// The line detector works on a single channel. If the input is a color image, we can just
// give the line detector a grayscale version of it
final Mat tmp = new Mat();
- cvtColor(input, tmp, COLOR_BGR2GRAY);
+ cvtColor(input.getCpu(), tmp, COLOR_BGR2GRAY);
lsd.detect(tmp, lines);
tmp.release();
}
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/composite/HSLThresholdOperation.java b/core/src/main/java/edu/wpi/grip/core/operations/composite/HSLThresholdOperation.java
index 8bf6135d62..b7958754cb 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/composite/HSLThresholdOperation.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/composite/HSLThresholdOperation.java
@@ -3,6 +3,7 @@
import edu.wpi.grip.annotation.operation.Description;
import edu.wpi.grip.annotation.operation.OperationCategory;
+import edu.wpi.grip.core.MatWrapper;
import edu.wpi.grip.core.Operation;
import edu.wpi.grip.core.sockets.InputSocket;
import edu.wpi.grip.core.sockets.OutputSocket;
@@ -33,7 +34,7 @@
public class HSLThresholdOperation extends ThresholdOperation {
private static final Logger logger = Logger.getLogger(HSLThresholdOperation.class.getName());
- private final SocketHint inputHint = SocketHints.Inputs.createMatSocketHint("Input", false);
+ private final SocketHint inputHint = SocketHints.createImageSocketHint("Input");
private final SocketHint> hueHint = SocketHints.Inputs
.createNumberListRangeSocketHint("Hue", 0.0, 180.0);
private final SocketHint> saturationHint = SocketHints.Inputs
@@ -41,14 +42,14 @@ public class HSLThresholdOperation extends ThresholdOperation {
private final SocketHint> luminanceHint = SocketHints.Inputs
.createNumberListRangeSocketHint("Luminance", 0.0, 255.0);
- private final SocketHint outputHint = SocketHints.Outputs.createMatSocketHint("Output");
+ private final SocketHint outputHint = SocketHints.createImageSocketHint("Output");
- private final InputSocket inputSocket;
+ private final InputSocket inputSocket;
private final InputSocket> hueSocket;
private final InputSocket> saturationSocket;
private final InputSocket> luminanceSocket;
- private final OutputSocket outputSocket;
+ private final OutputSocket outputSocket;
@Inject
@SuppressWarnings("JavadocMethod")
@@ -82,13 +83,13 @@ public List getOutputSockets() {
@Override
@SuppressWarnings("unchecked")
public void perform() {
- final Mat input = inputSocket.getValue().get();
+ final Mat input = inputSocket.getValue().get().getCpu();
if (input.channels() != 3) {
throw new IllegalArgumentException("HSL Threshold needs a 3-channel input");
}
- final Mat output = outputSocket.getValue().get();
+ final Mat output = outputSocket.getValue().get().rawCpu();
final List channel1 = hueSocket.getValue().get();
final List channel2 = saturationSocket.getValue().get();
final List channel3 = luminanceSocket.getValue().get();
@@ -111,7 +112,7 @@ public void perform() {
try {
cvtColor(input, hls, COLOR_BGR2HLS);
inRange(hls, low, high, output);
- outputSocket.setValue(output);
+ outputSocket.flagChanged();
} catch (RuntimeException e) {
logger.log(Level.WARNING, e.getMessage(), e);
}
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/composite/HSVThresholdOperation.java b/core/src/main/java/edu/wpi/grip/core/operations/composite/HSVThresholdOperation.java
index 839acbd632..5b0ef05d11 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/composite/HSVThresholdOperation.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/composite/HSVThresholdOperation.java
@@ -2,6 +2,7 @@
import edu.wpi.grip.annotation.operation.Description;
import edu.wpi.grip.annotation.operation.OperationCategory;
+import edu.wpi.grip.core.MatWrapper;
import edu.wpi.grip.core.Operation;
import edu.wpi.grip.core.sockets.InputSocket;
import edu.wpi.grip.core.sockets.OutputSocket;
@@ -33,7 +34,7 @@
public class HSVThresholdOperation extends ThresholdOperation {
private static final Logger logger = Logger.getLogger(HSVThresholdOperation.class.getName());
- private final SocketHint inputHint = SocketHints.Inputs.createMatSocketHint("Input", false);
+ private final SocketHint inputHint = SocketHints.createImageSocketHint("Input");
private final SocketHint> hueHint = SocketHints.Inputs
.createNumberListRangeSocketHint("Hue", 0.0, 180.0);
private final SocketHint> saturationHint = SocketHints.Inputs
@@ -41,14 +42,14 @@ public class HSVThresholdOperation extends ThresholdOperation {
private final SocketHint> valueHint = SocketHints.Inputs
.createNumberListRangeSocketHint("Value", 0.0, 255.0);
- private final SocketHint outputHint = SocketHints.Outputs.createMatSocketHint("Output");
+ private final SocketHint outputHint = SocketHints.createImageSocketHint("Output");
- private final InputSocket inputSocket;
+ private final InputSocket inputSocket;
private final InputSocket> hueSocket;
private final InputSocket> saturationSocket;
private final InputSocket> valueSocket;
- private final OutputSocket outputSocket;
+ private final OutputSocket outputSocket;
@Inject
@SuppressWarnings("JavadocMethod")
@@ -82,13 +83,13 @@ public List getOutputSockets() {
@Override
public void perform() {
- final Mat input = inputSocket.getValue().get();
+ final Mat input = inputSocket.getValue().get().getCpu();
if (input.channels() != 3) {
throw new IllegalArgumentException("HSV Threshold needs a 3-channel input");
}
- final Mat output = outputSocket.getValue().get();
+ final Mat output = outputSocket.getValue().get().rawCpu();
final List channel1 = hueSocket.getValue().get();
final List channel2 = saturationSocket.getValue().get();
final List channel3 = valueSocket.getValue().get();
@@ -109,7 +110,7 @@ public void perform() {
try {
cvtColor(input, hsv, COLOR_BGR2HSV);
inRange(hsv, low, high, output);
- outputSocket.setValue(output);
+ outputSocket.flagChanged();
} catch (RuntimeException e) {
logger.log(Level.WARNING, e.getMessage(), e);
}
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/composite/LinesReport.java b/core/src/main/java/edu/wpi/grip/core/operations/composite/LinesReport.java
index f878a26d8d..1bc34e855f 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/composite/LinesReport.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/composite/LinesReport.java
@@ -1,6 +1,7 @@
package edu.wpi.grip.core.operations.composite;
import edu.wpi.grip.annotation.operation.PublishableObject;
+import edu.wpi.grip.core.MatWrapper;
import edu.wpi.grip.core.operations.network.PublishValue;
import edu.wpi.grip.core.operations.network.Publishable;
import edu.wpi.grip.core.sockets.NoSocketTypeLabel;
@@ -9,7 +10,6 @@
import java.util.Collections;
import java.util.List;
-import static org.bytedeco.javacpp.opencv_core.Mat;
import static org.bytedeco.javacpp.opencv_imgproc.LineSegmentDetector;
import static org.bytedeco.javacpp.opencv_imgproc.createLineSegmentDetector;
@@ -24,7 +24,7 @@
@NoSocketTypeLabel
public class LinesReport implements Publishable {
private final LineSegmentDetector lsd;
- private final Mat input;
+ private final MatWrapper input;
private final List lines;
/**
@@ -32,7 +32,7 @@ public class LinesReport implements Publishable {
* LinesReports.
*/
public LinesReport() {
- this(createLineSegmentDetector(), new Mat(), Collections.emptyList());
+ this(createLineSegmentDetector(), MatWrapper.emptyWrapper(), Collections.emptyList());
}
/**
@@ -40,7 +40,7 @@ public LinesReport() {
* @param input The input matrix.
* @param lines The lines that have been found.
*/
- public LinesReport(LineSegmentDetector lsd, Mat input, List lines) {
+ public LinesReport(LineSegmentDetector lsd, MatWrapper input, List lines) {
this.lsd = lsd;
this.input = input;
this.lines = lines;
@@ -53,7 +53,7 @@ protected LineSegmentDetector getLineSegmentDetector() {
/**
* @return The original image that the line detection was performed on.
*/
- public Mat getInput() {
+ public MatWrapper getInput() {
return this.input;
}
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/composite/MaskOperation.java b/core/src/main/java/edu/wpi/grip/core/operations/composite/MaskOperation.java
index 569ec1f953..4ce0599b5a 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/composite/MaskOperation.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/composite/MaskOperation.java
@@ -2,6 +2,7 @@
import edu.wpi.grip.annotation.operation.Description;
import edu.wpi.grip.annotation.operation.OperationCategory;
+import edu.wpi.grip.core.MatWrapper;
import edu.wpi.grip.core.Operation;
import edu.wpi.grip.core.sockets.InputSocket;
import edu.wpi.grip.core.sockets.OutputSocket;
@@ -25,16 +26,16 @@
iconName = "mask")
public class MaskOperation implements Operation {
- private final SocketHint inputHint = SocketHints.Inputs.createMatSocketHint("Input", false);
- private final SocketHint maskHint = SocketHints.Inputs.createMatSocketHint("Mask", false);
+ private final SocketHint inputHint = SocketHints.createImageSocketHint("Input");
+ private final SocketHint maskHint = SocketHints.createImageSocketHint("Mask");
- private final SocketHint outputHint = SocketHints.Outputs.createMatSocketHint("Output");
+ private final SocketHint outputHint = SocketHints.createImageSocketHint("Output");
- private final InputSocket inputSocket;
- private final InputSocket maskSocket;
+ private final InputSocket inputSocket;
+ private final InputSocket maskSocket;
- private final OutputSocket outputSocket;
+ private final OutputSocket outputSocket;
@Inject
@SuppressWarnings("JavadocMethod")
@@ -63,14 +64,14 @@ public List getOutputSockets() {
@Override
public void perform() {
- final Mat input = inputSocket.getValue().get();
- final Mat mask = maskSocket.getValue().get();
+ final Mat input = inputSocket.getValue().get().getCpu();
+ final Mat mask = maskSocket.getValue().get().getCpu();
- final Mat output = outputSocket.getValue().get();
+ final Mat output = outputSocket.getValue().get().rawCpu();
// Clear the output to black, then copy the input to it with the mask
bitwise_xor(output, output, output);
input.copyTo(output, mask);
- outputSocket.setValue(output);
+ outputSocket.flagChanged();
}
}
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/composite/NormalizeOperation.java b/core/src/main/java/edu/wpi/grip/core/operations/composite/NormalizeOperation.java
index 52b331932a..6d570e4d9c 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/composite/NormalizeOperation.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/composite/NormalizeOperation.java
@@ -2,7 +2,9 @@
import edu.wpi.grip.annotation.operation.Description;
import edu.wpi.grip.annotation.operation.OperationCategory;
+import edu.wpi.grip.core.MatWrapper;
import edu.wpi.grip.core.Operation;
+import edu.wpi.grip.core.operations.CudaOperation;
import edu.wpi.grip.core.sockets.InputSocket;
import edu.wpi.grip.core.sockets.OutputSocket;
import edu.wpi.grip.core.sockets.SocketHint;
@@ -11,14 +13,15 @@
import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
+import org.bytedeco.javacpp.opencv_core;
+import org.bytedeco.javacpp.opencv_cudaarithm;
+
import java.util.List;
-import static org.bytedeco.javacpp.opencv_core.Mat;
import static org.bytedeco.javacpp.opencv_core.NORM_INF;
import static org.bytedeco.javacpp.opencv_core.NORM_L1;
import static org.bytedeco.javacpp.opencv_core.NORM_L2;
import static org.bytedeco.javacpp.opencv_core.NORM_MINMAX;
-import static org.bytedeco.javacpp.opencv_core.normalize;
/**
* GRIP {@link Operation} for {@link org.bytedeco.javacpp.opencv_core#normalize}.
@@ -27,40 +30,35 @@
summary = "Normalizes or remaps the values of pixels in an image",
category = OperationCategory.IMAGE_PROCESSING,
iconName = "opencv")
-public class NormalizeOperation implements Operation {
+public class NormalizeOperation extends CudaOperation {
- private final SocketHint srcHint = SocketHints.Inputs.createMatSocketHint("Input", false);
private final SocketHint typeHint = SocketHints.createEnumSocketHint("Type", Type.MINMAX);
private final SocketHint aHint = SocketHints.Inputs
.createNumberSpinnerSocketHint("Alpha", 0.0, 0, Double.MAX_VALUE);
private final SocketHint bHint = SocketHints.Inputs
.createNumberSpinnerSocketHint("Beta", 255, 0, Double.MAX_VALUE);
- private final SocketHint dstHint = SocketHints.Inputs.createMatSocketHint("Output", true);
- private final InputSocket srcSocket;
private final InputSocket typeSocket;
private final InputSocket alphaSocket;
private final InputSocket betaSocket;
- private final OutputSocket outputSocket;
@Inject
@SuppressWarnings("JavadocMethod")
public NormalizeOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
outputSocketFactory) {
- this.srcSocket = inputSocketFactory.create(srcHint);
+ super(inputSocketFactory, outputSocketFactory);
this.typeSocket = inputSocketFactory.create(typeHint);
this.alphaSocket = inputSocketFactory.create(aHint);
this.betaSocket = inputSocketFactory.create(bHint);
-
- this.outputSocket = outputSocketFactory.create(dstHint);
}
@Override
public List getInputSockets() {
return ImmutableList.of(
- srcSocket,
+ inputSocket,
typeSocket,
alphaSocket,
- betaSocket
+ betaSocket,
+ gpuSocket
);
}
@@ -73,16 +71,21 @@ public List getOutputSockets() {
@Override
public void perform() {
- final Mat input = srcSocket.getValue().get();
- final Type type = typeSocket.getValue().get();
- final Number a = alphaSocket.getValue().get();
- final Number b = betaSocket.getValue().get();
-
- final Mat output = outputSocket.getValue().get();
-
- normalize(input, output, a.doubleValue(), b.doubleValue(), type.value, -1, null);
+ final MatWrapper input = inputSocket.getValue().get();
+ final int type = typeSocket.getValue().get().value;
+ final double a = alphaSocket.getValue().get().doubleValue();
+ final double b = betaSocket.getValue().get().doubleValue();
+
+ final MatWrapper output = outputSocket.getValue().get();
+
+ if (preferCuda() && input.channels() == 1) {
+ // CUDA normalize only works on single-channel images
+ opencv_cudaarithm.normalize(input.getGpu(), output.rawGpu(), a, b, type, -1);
+ } else {
+ opencv_core.normalize(input.getCpu(), output.rawCpu(), a, b, type, -1, null);
+ }
- outputSocket.setValue(output);
+ outputSocket.flagChanged();
}
private enum Type {
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/composite/PublishVideoOperation.java b/core/src/main/java/edu/wpi/grip/core/operations/composite/PublishVideoOperation.java
index 0cf13c9282..2131766fe9 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/composite/PublishVideoOperation.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/composite/PublishVideoOperation.java
@@ -2,6 +2,7 @@
import edu.wpi.grip.annotation.operation.Description;
import edu.wpi.grip.annotation.operation.OperationCategory;
+import edu.wpi.grip.core.MatWrapper;
import edu.wpi.grip.core.Operation;
import edu.wpi.grip.core.sockets.InputSocket;
import edu.wpi.grip.core.sockets.OutputSocket;
@@ -24,7 +25,6 @@
import java.util.logging.Level;
import java.util.logging.Logger;
-import static org.bytedeco.javacpp.opencv_core.Mat;
import static org.bytedeco.javacpp.opencv_imgcodecs.CV_IMWRITE_JPEG_QUALITY;
import static org.bytedeco.javacpp.opencv_imgcodecs.imencode;
@@ -51,7 +51,7 @@ public class PublishVideoOperation implements Operation {
private final Object imageLock = new Object();
private final BytePointer imagePointer = new BytePointer();
private final Thread serverThread;
- private final InputSocket inputSocket;
+ private final InputSocket inputSocket;
private final InputSocket qualitySocket;
@SuppressWarnings("PMD.SingularField")
private volatile boolean connected = false;
@@ -99,9 +99,9 @@ public class PublishVideoOperation implements Operation {
}
// Copy the image data into a pre-allocated buffer, growing it if necessary
- bufferSize = imagePointer.limit();
+ bufferSize = (int) imagePointer.limit();
if (bufferSize > buffer.length) {
- buffer = new byte[imagePointer.limit()];
+ buffer = new byte[(int) imagePointer.limit()];
}
imagePointer.get(buffer, 0, bufferSize);
hasImage = false;
@@ -144,8 +144,7 @@ public PublishVideoOperation(InputSocket.Factory inputSocketFactory) {
if (numSteps != 0) {
throw new IllegalStateException("Only one instance of PublishVideoOperation may exist");
}
- this.inputSocket = inputSocketFactory.create(SocketHints.Inputs.createMatSocketHint("Image",
- false));
+ this.inputSocket = inputSocketFactory.create(SocketHints.createImageSocketHint("Image"));
this.qualitySocket = inputSocketFactory.create(SocketHints.Inputs
.createNumberSliderSocketHint("Quality", 80, 0, 100));
numSteps++;
@@ -179,7 +178,7 @@ public void perform() {
}
synchronized (imageLock) {
- imencode(".jpeg", inputSocket.getValue().get(), imagePointer,
+ imencode(".jpeg", inputSocket.getValue().get().getCpu(), imagePointer,
new IntPointer(CV_IMWRITE_JPEG_QUALITY, qualitySocket.getValue().get().intValue()));
hasImage = true;
imageLock.notifyAll();
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/composite/RGBThresholdOperation.java b/core/src/main/java/edu/wpi/grip/core/operations/composite/RGBThresholdOperation.java
index 481327ebf8..43ecb57dd9 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/composite/RGBThresholdOperation.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/composite/RGBThresholdOperation.java
@@ -2,6 +2,7 @@
import edu.wpi.grip.annotation.operation.Description;
import edu.wpi.grip.annotation.operation.OperationCategory;
+import edu.wpi.grip.core.MatWrapper;
import edu.wpi.grip.core.Operation;
import edu.wpi.grip.core.sockets.InputSocket;
import edu.wpi.grip.core.sockets.OutputSocket;
@@ -30,7 +31,7 @@
public class RGBThresholdOperation extends ThresholdOperation {
private static final Logger logger = Logger.getLogger(RGBThresholdOperation.class.getName());
- private final SocketHint inputHint = SocketHints.Inputs.createMatSocketHint("Input", false);
+ private final SocketHint inputHint = SocketHints.createImageSocketHint("Input");
private final SocketHint> redHint = SocketHints.Inputs
.createNumberListRangeSocketHint("Red", 0.0, 255.0);
private final SocketHint> greenHint = SocketHints.Inputs
@@ -38,15 +39,15 @@ public class RGBThresholdOperation extends ThresholdOperation {
private final SocketHint> blueHint = SocketHints.Inputs
.createNumberListRangeSocketHint("Blue", 0.0, 255.0);
- private final SocketHint outputHint = SocketHints.Outputs.createMatSocketHint("Output");
+ private final SocketHint outputHint = SocketHints.createImageSocketHint("Output");
- private final InputSocket inputSocket;
+ private final InputSocket inputSocket;
private final InputSocket> redSocket;
private final InputSocket> greenSocket;
private final InputSocket> blueSocket;
- private final OutputSocket outputSocket;
+ private final OutputSocket outputSocket;
@Inject
@SuppressWarnings("JavadocMethod")
@@ -79,13 +80,13 @@ public List getOutputSockets() {
@Override
public void perform() {
- final Mat input = inputSocket.getValue().get();
+ final Mat input = inputSocket.getValue().get().getCpu();
if (input.channels() != 3) {
throw new IllegalArgumentException("RGB Threshold needs a 3-channel input");
}
- final Mat output = outputSocket.getValue().get();
+ final Mat output = outputSocket.getValue().get().rawCpu();
final List channel1 = redSocket.getValue().get();
final List channel2 = greenSocket.getValue().get();
final List channel3 = blueSocket.getValue().get();
@@ -106,7 +107,7 @@ public void perform() {
try {
inRange(input, low, high, output);
- outputSocket.setValue(output);
+ outputSocket.flagChanged();
} catch (RuntimeException e) {
logger.log(Level.WARNING, e.getMessage(), e);
}
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/composite/RectsReport.java b/core/src/main/java/edu/wpi/grip/core/operations/composite/RectsReport.java
index 0b33581057..1ed7405e10 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/composite/RectsReport.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/composite/RectsReport.java
@@ -1,13 +1,13 @@
package edu.wpi.grip.core.operations.composite;
import edu.wpi.grip.annotation.operation.PublishableObject;
+import edu.wpi.grip.core.MatWrapper;
import edu.wpi.grip.core.operations.network.PublishValue;
import edu.wpi.grip.core.operations.network.Publishable;
import edu.wpi.grip.core.sockets.NoSocketTypeLabel;
import com.google.common.collect.ImmutableList;
-import org.bytedeco.javacpp.opencv_core.Mat;
import org.bytedeco.javacpp.opencv_core.Rect;
import java.util.ArrayList;
@@ -20,12 +20,13 @@
@NoSocketTypeLabel
public class RectsReport implements Publishable {
- private final Mat image;
+ private final MatWrapper image;
private final List rectangles;
- public static final RectsReport NIL = new RectsReport(new Mat(), new ArrayList<>());
+ public static final RectsReport NIL
+ = new RectsReport(MatWrapper.emptyWrapper(), new ArrayList<>());
- public RectsReport(Mat image, List rectangles) {
+ public RectsReport(MatWrapper image, List rectangles) {
this.image = image;
this.rectangles = ImmutableList.copyOf(rectangles);
}
@@ -33,7 +34,7 @@ public RectsReport(Mat image, List rectangles) {
/**
* Gets the image the rectangles are for.
*/
- public Mat getImage() {
+ public MatWrapper getImage() {
return image;
}
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/composite/ResizeOperation.java b/core/src/main/java/edu/wpi/grip/core/operations/composite/ResizeOperation.java
index 20ea8817a3..45b59f9daa 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/composite/ResizeOperation.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/composite/ResizeOperation.java
@@ -2,6 +2,7 @@
import edu.wpi.grip.annotation.operation.Description;
import edu.wpi.grip.annotation.operation.OperationCategory;
+import edu.wpi.grip.core.MatWrapper;
import edu.wpi.grip.core.Operation;
import edu.wpi.grip.core.sockets.InputSocket;
import edu.wpi.grip.core.sockets.OutputSocket;
@@ -32,19 +33,18 @@
iconName = "resize")
public class ResizeOperation implements Operation {
- private final InputSocket inputSocket;
+ private final InputSocket inputSocket;
private final InputSocket widthSocket;
private final InputSocket heightSocket;
private final InputSocket interpolationSocket;
- private final OutputSocket outputSocket;
+ private final OutputSocket outputSocket;
@Inject
@SuppressWarnings("JavadocMethod")
public ResizeOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
outputSocketFactory) {
- this.inputSocket = inputSocketFactory.create(SocketHints.Inputs
- .createMatSocketHint("Input", false));
+ this.inputSocket = inputSocketFactory.create(SocketHints.createImageSocketHint("Input"));
this.widthSocket = inputSocketFactory.create(SocketHints.Inputs
.createNumberSpinnerSocketHint("Width", 640));
this.heightSocket = inputSocketFactory.create(SocketHints.Inputs
@@ -52,8 +52,7 @@ public ResizeOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Fact
this.interpolationSocket = inputSocketFactory
.create(SocketHints.createEnumSocketHint("Interpolation", Interpolation.CUBIC));
- this.outputSocket = outputSocketFactory.create(SocketHints.Outputs
- .createMatSocketHint("Output"));
+ this.outputSocket = outputSocketFactory.create(SocketHints.createImageSocketHint("Output"));
}
@Override
@@ -75,17 +74,17 @@ public List getOutputSockets() {
@Override
public void perform() {
- final Mat input = inputSocket.getValue().get();
+ final Mat input = inputSocket.getValue().get().getCpu();
final Number width = widthSocket.getValue().get();
final Number height = heightSocket.getValue().get();
final Interpolation interpolation = interpolationSocket.getValue().get();
- final Mat output = outputSocket.getValue().get();
+ final Mat output = outputSocket.getValue().get().rawCpu();
resize(input, output, new Size(width.intValue(), height.intValue()), 0.0, 0.0, interpolation
.value);
- outputSocket.setValue(output);
+ outputSocket.flagChanged();
}
private enum Interpolation {
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/composite/SaveImageOperation.java b/core/src/main/java/edu/wpi/grip/core/operations/composite/SaveImageOperation.java
index 6f6fc08e60..90b2f9f130 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/composite/SaveImageOperation.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/composite/SaveImageOperation.java
@@ -2,6 +2,7 @@
import edu.wpi.grip.annotation.operation.Description;
import edu.wpi.grip.core.FileManager;
+import edu.wpi.grip.core.MatWrapper;
import edu.wpi.grip.core.Operation;
import edu.wpi.grip.core.sockets.InputSocket;
import edu.wpi.grip.core.sockets.OutputSocket;
@@ -21,7 +22,6 @@
import java.util.Locale;
import java.util.concurrent.TimeUnit;
-import static org.bytedeco.javacpp.opencv_core.Mat;
import static org.bytedeco.javacpp.opencv_imgcodecs.CV_IMWRITE_JPEG_QUALITY;
import static org.bytedeco.javacpp.opencv_imgcodecs.imencode;
@@ -33,8 +33,8 @@
iconName = "publish-video")
public class SaveImageOperation implements Operation {
- private final SocketHint inputHint
- = SocketHints.Inputs.createMatSocketHint("Input", false);
+ private final SocketHint inputHint
+ = SocketHints.createImageSocketHint("Input");
private final SocketHint fileTypeHint
= SocketHints.createEnumSocketHint("File type", FileTypes.JPEG);
private final SocketHint qualityHint
@@ -44,15 +44,15 @@ public class SaveImageOperation implements Operation {
private final SocketHint activeHint
= SocketHints.Inputs.createCheckboxSocketHint("Active", false);
- private final SocketHint outputHint = SocketHints.Outputs.createMatSocketHint("Output");
+ private final SocketHint outputHint = SocketHints.createImageSocketHint("Output");
- private final InputSocket inputSocket;
+ private final InputSocket inputSocket;
private final InputSocket fileTypesSocket;
private final InputSocket qualitySocket;
private final InputSocket periodSocket;
private final InputSocket activeSocket;
- private final OutputSocket outputSocket;
+ private final OutputSocket outputSocket;
private final FileManager fileManager;
private final BytePointer imagePointer = new BytePointer();
@@ -61,7 +61,8 @@ public class SaveImageOperation implements Operation {
= DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss-SSS");
private enum FileTypes {
- JPEG, PNG;
+ JPEG,
+ PNG;
@Override
public String toString() {
@@ -117,12 +118,14 @@ public void perform() {
stopwatch.reset();
stopwatch.start();
- imencode("." + fileTypesSocket.getValue().get(), inputSocket.getValue().get(), imagePointer,
+ imencode("." + fileTypesSocket.getValue().get(),
+ inputSocket.getValue().get().getCpu(),
+ imagePointer,
new IntPointer(CV_IMWRITE_JPEG_QUALITY, qualitySocket.getValue().get().intValue()));
byte[] buffer = new byte[128 * 1024];
- int bufferSize = imagePointer.limit();
+ int bufferSize = (int) imagePointer.limit();
if (bufferSize > buffer.length) {
- buffer = new byte[imagePointer.limit()];
+ buffer = new byte[(int) imagePointer.limit()];
}
imagePointer.get(buffer, 0, bufferSize);
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/composite/ThresholdMoving.java b/core/src/main/java/edu/wpi/grip/core/operations/composite/ThresholdMoving.java
index 5831b6864d..ce9d79d193 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/composite/ThresholdMoving.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/composite/ThresholdMoving.java
@@ -1,6 +1,7 @@
package edu.wpi.grip.core.operations.composite;
import edu.wpi.grip.annotation.operation.Description;
+import edu.wpi.grip.core.MatWrapper;
import edu.wpi.grip.core.Operation;
import edu.wpi.grip.core.sockets.InputSocket;
import edu.wpi.grip.core.sockets.OutputSocket;
@@ -23,16 +24,16 @@
+ " previous and next image.")
public class ThresholdMoving implements Operation {
- private final InputSocket imageSocket;
- private final OutputSocket outputSocket;
+ private final InputSocket imageSocket;
+ private final OutputSocket outputSocket;
private final Mat lastImage;
@Inject
@SuppressWarnings("JavadocMethod")
public ThresholdMoving(InputSocket.Factory inputSocketFactory,
OutputSocket.Factory outputSocketFactory) {
- imageSocket = inputSocketFactory.create(SocketHints.Inputs.createMatSocketHint("image", false));
- outputSocket = outputSocketFactory.create(SocketHints.Outputs.createMatSocketHint("moved"));
+ imageSocket = inputSocketFactory.create(SocketHints.createImageSocketHint("image"));
+ outputSocket = outputSocketFactory.create(SocketHints.createImageSocketHint("moved"));
lastImage = new Mat();
}
@@ -52,14 +53,14 @@ public List getOutputSockets() {
@Override
public void perform() {
- final Mat input = imageSocket.getValue().get();
+ final Mat input = imageSocket.getValue().get().getCpu();
final Size lastSize = lastImage.size();
final Size inputSize = input.size();
if (!lastImage.empty() && lastSize.height() == inputSize.height()
&& lastSize.width() == inputSize.width()) {
- opencv_core.absdiff(input, lastImage, outputSocket.getValue().get());
+ opencv_core.absdiff(input, lastImage, outputSocket.getValue().get().rawCpu());
}
input.copyTo(lastImage);
- outputSocket.setValue(outputSocket.getValue().get());
+ outputSocket.flagChanged();
}
}
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/composite/WatershedOperation.java b/core/src/main/java/edu/wpi/grip/core/operations/composite/WatershedOperation.java
index 30a8f1a748..d84ebd835a 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/composite/WatershedOperation.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/composite/WatershedOperation.java
@@ -2,6 +2,7 @@
import edu.wpi.grip.annotation.operation.Description;
import edu.wpi.grip.annotation.operation.OperationCategory;
+import edu.wpi.grip.core.MatWrapper;
import edu.wpi.grip.core.Operation;
import edu.wpi.grip.core.sockets.InputSocket;
import edu.wpi.grip.core.sockets.OutputSocket;
@@ -47,7 +48,7 @@
iconName = "opencv")
public class WatershedOperation implements Operation {
- private final SocketHint srcHint = SocketHints.Inputs.createMatSocketHint("Input", false);
+ private final SocketHint srcHint = SocketHints.createImageSocketHint("Input");
private final SocketHint contoursHint =
new SocketHint.Builder<>(ContoursReport.class)
.identifier("Contours")
@@ -60,7 +61,7 @@ public class WatershedOperation implements Operation {
.initialValueSupplier(ContoursReport::new)
.build();
- private final InputSocket srcSocket;
+ private final InputSocket srcSocket;
private final InputSocket contoursSocket;
private final OutputSocket outputSocket;
@@ -100,7 +101,7 @@ public List getOutputSockets() {
@Override
public void perform() {
- final Mat input = srcSocket.getValue().get();
+ final Mat input = srcSocket.getValue().get().getCpu();
if (input.type() != CV_8UC3) {
throw new IllegalArgumentException("Watershed only works on 8-bit, 3-channel images");
}
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/network/networktables/NTManager.java b/core/src/main/java/edu/wpi/grip/core/operations/network/networktables/NTManager.java
index 4ffa01f351..ede2f88b09 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/network/networktables/NTManager.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/network/networktables/NTManager.java
@@ -30,6 +30,7 @@
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
+
import javax.inject.Inject;
import static com.google.common.base.Preconditions.checkNotNull;
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/network/ros/JavaToMessageConverter.java b/core/src/main/java/edu/wpi/grip/core/operations/network/ros/JavaToMessageConverter.java
index 85a8f14757..0cda4e8179 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/network/ros/JavaToMessageConverter.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/network/ros/JavaToMessageConverter.java
@@ -136,7 +136,7 @@ public void convert(J javaType, Message message, MessageFactory messageFactory)
}
private abstract static class SimpleConverter extends
- JavaToMessageConverter {
+ JavaToMessageConverter {
private final BiConsumer messageDataAssigner;
private SimpleConverter(String type, BiConsumer messageDataAssigner) {
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/opencv/MatFieldAccessor.java b/core/src/main/java/edu/wpi/grip/core/operations/opencv/MatFieldAccessor.java
index 2b8ba3c2fa..47311e2fa3 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/opencv/MatFieldAccessor.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/opencv/MatFieldAccessor.java
@@ -2,6 +2,7 @@
import edu.wpi.grip.annotation.operation.Description;
import edu.wpi.grip.annotation.operation.OperationCategory;
+import edu.wpi.grip.core.MatWrapper;
import edu.wpi.grip.core.sockets.InputSocket;
import edu.wpi.grip.core.sockets.OutputSocket;
import edu.wpi.grip.core.sockets.SocketHint;
@@ -22,7 +23,7 @@
public class MatFieldAccessor implements CVOperation {
private static final Mat defaultsMat = new Mat();
- private final SocketHint matHint = SocketHints.Inputs.createMatSocketHint("Input", false);
+ private final SocketHint matHint = SocketHints.createImageSocketHint("Input");
private final SocketHint sizeHint = SocketHints.Inputs.createSizeSocketHint("size", true);
private final SocketHint emptyHint = SocketHints.Outputs
.createBooleanSocketHint("empty", defaultsMat.empty());
@@ -36,7 +37,7 @@ public class MatFieldAccessor implements CVOperation {
.createNumberSocketHint("high value", defaultsMat.highValue());
- private final InputSocket inputSocket;
+ private final InputSocket inputSocket;
private final OutputSocket sizeSocket;
private final OutputSocket emptySocket;
@@ -80,13 +81,13 @@ public List getOutputSockets() {
@Override
public void perform() {
- final Mat inputMat = inputSocket.getValue().get();
+ MatWrapper wrapper = inputSocket.getValue().get();
- sizeSocket.setValue(inputMat.size());
- emptySocket.setValue(inputMat.empty());
- channelsSocket.setValue(inputMat.channels());
- colsSocket.setValue(inputMat.cols());
- rowsSocket.setValue(inputMat.rows());
- highValueSocket.setValue(inputMat.highValue());
+ sizeSocket.setValue(wrapper.size());
+ emptySocket.setValue(wrapper.empty());
+ channelsSocket.setValue(wrapper.channels());
+ colsSocket.setValue(wrapper.cols());
+ rowsSocket.setValue(wrapper.rows());
+ highValueSocket.setValue(wrapper.highValue());
}
}
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/opencv/MinMaxLoc.java b/core/src/main/java/edu/wpi/grip/core/operations/opencv/MinMaxLoc.java
index 84e364026a..5d975d46a1 100644
--- a/core/src/main/java/edu/wpi/grip/core/operations/opencv/MinMaxLoc.java
+++ b/core/src/main/java/edu/wpi/grip/core/operations/opencv/MinMaxLoc.java
@@ -2,6 +2,7 @@
import edu.wpi.grip.annotation.operation.Description;
import edu.wpi.grip.annotation.operation.OperationCategory;
+import edu.wpi.grip.core.MatWrapper;
import edu.wpi.grip.core.sockets.InputSocket;
import edu.wpi.grip.core.sockets.OutputSocket;
import edu.wpi.grip.core.sockets.SocketHint;
@@ -10,6 +11,7 @@
import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
+import org.bytedeco.javacpp.DoublePointer;
import org.bytedeco.javacpp.opencv_core;
import org.bytedeco.javacpp.opencv_core.Mat;
import org.bytedeco.javacpp.opencv_core.Point;
@@ -25,10 +27,8 @@
iconName = "opencv")
public class MinMaxLoc implements CVOperation {
- private final SocketHint srcInputHint = SocketHints.Inputs
- .createMatSocketHint("Image", false);
- private final SocketHint maskInputHint = SocketHints.Inputs
- .createMatSocketHint("Mask", true);
+ private final SocketHint srcInputHint = SocketHints.createImageSocketHint("Image");
+ private final SocketHint maskInputHint = SocketHints.createImageSocketHint("Mask");
private final SocketHint minValOutputHint = SocketHints.Outputs
.createNumberSocketHint("Min Val", 0);
@@ -40,8 +40,8 @@ public class MinMaxLoc implements CVOperation {
private final SocketHint maxLocOutputHint = SocketHints.Outputs
.createPointSocketHint("Max Loc");
- private final InputSocket srcSocket;
- private final InputSocket maskSocket;
+ private final InputSocket srcSocket;
+ private final InputSocket maskSocket;
private final OutputSocket minValSocket;
private final OutputSocket maxValSocket;
@@ -81,20 +81,20 @@ public List getOutputSockets() {
@Override
public void perform() {
- final Mat src = srcSocket.getValue().get();
- Mat mask = maskSocket.getValue().get();
+ final Mat src = srcSocket.getValue().get().getCpu();
+ Mat mask = maskSocket.getValue().get().getCpu();
if (mask.empty()) {
mask = null;
}
- final double[] minVal = new double[1];
- final double[] maxVal = new double[1];
+ DoublePointer minVal = new DoublePointer(0.0);
+ DoublePointer maxVal = new DoublePointer(0.0);
final Point minLoc = minLocSocket.getValue().get();
final Point maxLoc = maxLocSocket.getValue().get();
opencv_core.minMaxLoc(src, minVal, maxVal, minLoc, maxLoc, mask);
- minValSocket.setValue(minVal[0]);
- maxValSocket.setValue(maxVal[0]);
- minLocSocket.setValue(minLocSocket.getValue().get());
- maxLocSocket.setValue(maxLocSocket.getValue().get());
+ minValSocket.setValue(minVal.get());
+ maxValSocket.setValue(maxVal.get());
+ minLocSocket.flagChanged();
+ maxLocSocket.flagChanged();
}
}
diff --git a/core/src/main/java/edu/wpi/grip/core/operations/templated/FiveSourceOneDestinationCudaOperation.java b/core/src/main/java/edu/wpi/grip/core/operations/templated/FiveSourceOneDestinationCudaOperation.java
new file mode 100644
index 0000000000..4abb81ce7c
--- /dev/null
+++ b/core/src/main/java/edu/wpi/grip/core/operations/templated/FiveSourceOneDestinationCudaOperation.java
@@ -0,0 +1,79 @@
+package edu.wpi.grip.core.operations.templated;
+
+import edu.wpi.grip.core.operations.CudaOperation;
+import edu.wpi.grip.core.sockets.InputSocket;
+import edu.wpi.grip.core.sockets.OutputSocket;
+import edu.wpi.grip.core.sockets.SocketHint;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+final class FiveSourceOneDestinationCudaOperation extends CudaOperation {
+
+ private final InputSocket input1;
+ private final InputSocket input2;
+ private final InputSocket input3;
+ private final InputSocket input4;
+ private final InputSocket input5;
+ private final OutputSocket output;
+ private final Performer performer;
+
+ FiveSourceOneDestinationCudaOperation(InputSocket.Factory isf,
+ OutputSocket.Factory osf,
+ SocketHint t1SocketHint,
+ SocketHint t2SocketHint,
+ SocketHint t3SocketHint,
+ SocketHint t4SocketHint,
+ SocketHint t5SocketHint,
+ SocketHint rSocketHint,
+ Performer performer) {
+ super(isf, osf);
+ this.input1 = isf.create(t1SocketHint);
+ this.input2 = isf.create(t2SocketHint);
+ this.input3 = isf.create(t3SocketHint);
+ this.input4 = isf.create(t4SocketHint);
+ this.input5 = isf.create(t5SocketHint);
+ this.output = osf.create(rSocketHint);
+ this.performer = performer;
+ }
+
+ @Override
+ public List getInputSockets() {
+ return ImmutableList.of(
+ input1,
+ input2,
+ input3,
+ input4,
+ input5,
+ gpuSocket
+ );
+ }
+
+ @Override
+ public List