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..511cbf5a99 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 @@ -17,6 +17,7 @@ import edu.wpi.grip.generated.opencv_imgproc.enumeration.ColorConversionCodesEnum; import edu.wpi.grip.generated.opencv_imgproc.enumeration.ColormapTypesEnum; import edu.wpi.grip.generated.opencv_imgproc.enumeration.InterpolationFlagsEnum; +import edu.wpi.grip.generated.opencv_imgproc.enumeration.MorphTypesEnum; import edu.wpi.grip.generated.opencv_imgproc.enumeration.ThresholdTypesEnum; import com.google.common.annotations.VisibleForTesting; @@ -309,7 +310,28 @@ public class CVOperations { } )), - new OperationMetaData(CVOperation.defaults("CV rectangle", + new OperationMetaData(CVOperation.defaults("CV morphologyEx", + "Performs advanced morphological transformations."), + templateFactory.create( + SocketHints.Inputs.createMatSocketHint("src", false), + SocketHints.createEnumSocketHint("op", MorphTypesEnum.MORPH_OPEN), + SocketHints.Inputs.createMatSocketHint("kernel", true), + 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"), + (src, op, kernel, anchor, iterations, borderType, borderValue, dst) -> { + opencv_imgproc.morphologyEx(src, dst, op.value, kernel, anchor, + iterations.intValue(), borderType.value, borderValue); + } + )), + + + + new OperationMetaData(CVOperation.defaults("CV rectangle", "Draw a rectangle (outline or filled) on an image."), templateFactory.create( SocketHints.Inputs.createMatSocketHint("src", false), @@ -426,6 +448,20 @@ public enum CVBorderTypesEnum { } } + public enum CVMorphTypesEnum { + MORPH_OPEN(MorphTypesEnum.MORPH_OPEN.value), + MORPH_CLOSE(MorphTypesEnum.MORPH_CLOSE.value), + MORPH_GRADIENT(MorphTypesEnum.MORPH_GRADIENT.value), + MORPH_TOPHAT(MorphTypesEnum.MORPH_TOPHAT.value), + MORPH_BLACKHAT(MorphTypesEnum.MORPH_BLACKHAT.value); + + public final int value; + + CVMorphTypesEnum(int value) { + this.value = value; + } + } + /** * All of the operations that this list supplies. diff --git a/core/src/main/java/edu/wpi/grip/core/operations/opencv/NewKernelOperation.java b/core/src/main/java/edu/wpi/grip/core/operations/opencv/NewKernelOperation.java new file mode 100644 index 0000000000..17fdcf7825 --- /dev/null +++ b/core/src/main/java/edu/wpi/grip/core/operations/opencv/NewKernelOperation.java @@ -0,0 +1,89 @@ +package edu.wpi.grip.core.operations.opencv; + + +import edu.wpi.grip.core.Description; +import edu.wpi.grip.core.OperationDescription; +import edu.wpi.grip.core.sockets.InputSocket; +import edu.wpi.grip.core.sockets.OutputSocket; +import edu.wpi.grip.core.sockets.SocketHint; +import edu.wpi.grip.core.sockets.SocketHints; + +import com.google.inject.Inject; + +import org.bytedeco.javacpp.opencv_core.Mat; +import org.bytedeco.javacpp.opencv_core.Size; +import org.bytedeco.javacpp.opencv_imgproc; +import org.python.google.common.collect.ImmutableList; + +import java.util.List; + +@Description(name = "New Kernel", + summary = "Create a kernel of custom size", + category = OperationDescription.Category.OPENCV, + iconName = "kernel") +public class NewKernelOperation implements CVOperation { + + private final SocketHint typeHint = SocketHints.createEnumSocketHint("kernelType", + KernelEnum.MORPH_RECT); + private final SocketHint widthHint = SocketHints.Inputs + .createNumberSpinnerSocketHint("width", 1, 1, Integer.MAX_VALUE); + private final SocketHint heightHint = SocketHints.Inputs + .createNumberSpinnerSocketHint("height", 1, 1, Integer.MAX_VALUE); + private final SocketHint outputHint = SocketHints.Outputs.createMatSocketHint("kernel"); + + + private final InputSocket widthSocket; + private final InputSocket heightSocket; + private final InputSocket typeSocket; + + private final OutputSocket outputSocket; + + @Inject + @SuppressWarnings("JavadocMethod") + public NewKernelOperation(InputSocket.Factory inputSocketFactory, + OutputSocket.Factory outputSocketFactory) { + this.typeSocket = inputSocketFactory.create(typeHint); + this.widthSocket = inputSocketFactory.create(widthHint); + this.heightSocket = inputSocketFactory.create(heightHint); + this.outputSocket = outputSocketFactory.create(outputHint); + } + + @Override + public List getInputSockets() { + return ImmutableList.of( + typeSocket, + widthSocket, + heightSocket + ); + } + + @Override + public List getOutputSockets() { + return ImmutableList.of( + outputSocket + ); + } + + @Override + public void perform() { + final int widthValue = widthSocket.getValue().get().intValue(); + final int heightValue = heightSocket.getValue().get().intValue(); + final int kernelType = typeSocket.getValue().get().value; + + outputSocket.setValue(opencv_imgproc.getStructuringElement(kernelType, new Size(widthValue, + heightValue))); + } + + public enum KernelEnum { + MORPH_RECT(0), + MORPH_CROSS(1), + MORPH_ELLIPSE(2); + + public final int value; + + KernelEnum(int value) { + this.value = value; + } + } +} + diff --git a/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/cpp/operations/CV_morphologyEx.vm b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/cpp/operations/CV_morphologyEx.vm new file mode 100644 index 0000000000..68dc4cf4f8 --- /dev/null +++ b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/cpp/operations/CV_morphologyEx.vm @@ -0,0 +1,14 @@ + /** + * Performs advanced morphology functions. + * @param src the Image to morph. + * @param op the morph operation + * @param kernel the kernel for morphing. + * @param anchor the center of the kernel. + * @param iterations the number of times to perform the morph. + * @param borderType pixel extrapolation method. + * @param borderValue value to be used for a constant border. + * @param dst Output Image. + */ + void $className::#func($step ["src", "op", "kernel", "anchor", "iterations", "borderType", "borderValue", "dst"]) { + cv::morphologyEx(src, dst, op, kernel, anchor, (int)iterations, borderType, borderValue); + } diff --git a/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/cpp/operations/New_Kernel.vm b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/cpp/operations/New_Kernel.vm new file mode 100644 index 0000000000..7779d54664 --- /dev/null +++ b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/cpp/operations/New_Kernel.vm @@ -0,0 +1,8 @@ + /** + * Creates kernel of given shape and size + * @param shape the kernels MorphShape. + * @param size the size of the kernel. + */ + void $className::#func($step ["shape", "size"]) { + return cv::getStructuringElement(shape, size)); + } diff --git a/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/java/operations/CV_morphologyEx.vm b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/java/operations/CV_morphologyEx.vm new file mode 100644 index 0000000000..5a0126f299 --- /dev/null +++ b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/java/operations/CV_morphologyEx.vm @@ -0,0 +1,24 @@ + /** + * Performs advanced morphology functions. + * @param src the Image to morph. + * @param op the operation to perform. + * @param kernel the kernel for morphing. + * @param anchor the center of the kernel. + * @param iterations the number of times to perform the morph. + * @param borderType pixel extrapolation method. + * @param borderValue value to be used for a constant border. + * @param dst Output Image. + */ + private void $tMeth.name($step.name())(Mat src, MorphType op, Mat kernel, Point anchor, double iterations, + int borderType, Scalar borderValue, Mat dst) { + if (kernel == null) { + kernel = new Mat(); + } + if (anchor == null) { + anchor = new Point(-1,-1); + } + if (borderValue == null) { + borderValue = new Scalar(-1); + } + Imgproc.morphologyEx(src, dst, op, kernel, anchor, (int)iterations, borderType, borderValue); + } \ No newline at end of file diff --git a/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/java/operations/New_Kernel.vm b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/java/operations/New_Kernel.vm new file mode 100644 index 0000000000..11c0a4ee04 --- /dev/null +++ b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/java/operations/New_Kernel.vm @@ -0,0 +1,8 @@ + /** + * Creates a kernel of given shape and size. + * @param shape the kernels MorphShape. + * @param size the size of the kernel. + */ + private void $tMeth.name($step.name())(Mat shape, Size size) { + Imgproc.getStructuringElement(shape, size); + } \ No newline at end of file diff --git a/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/Pipeline.vm b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/Pipeline.vm index 0d54343509..912fccdfb4 100644 --- a/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/Pipeline.vm +++ b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/Pipeline.vm @@ -20,14 +20,15 @@ class $className: #foreach($input in $step.getInputs()) #if (!$input.value().matches("source[0-9]+")) #newInput($input) -#end - -#end -#foreach($output in $step.getOutputs()) - #output($output) = None +#end#end#if ($step.name() == "New_Kernel" || $step.name() == "New_Point" || $step.name() == "New_Size") + #parse("$vmLoc/step.vm") +#else #foreach($output in $step.getOutputs()) + #output($output) = None #end +#end + #end def process(self#foreach($source in $pipeline.getSources()), $source.value()#end): @@ -35,29 +36,32 @@ class $className: Runs the pipeline and sets all outputs to new values. """ #foreach($step in $pipeline.getSteps()) -#parse("$vmLoc/step.vm") - + #if ($step.name() != "New_Kernel" && $step.name() != "New_Point" && $step.name() != "New_Size") + #parse("$vmLoc/step.vm") +#end #end #foreach($step in $pipeline.getSteps()) -#if($step.name() == "Switch" || $step.name() == "Valve") -#set($boolInp = $step.getInput(0)) -#set($boolName = "$tMeth.pyName($boolInp.name())") -#if($boolInp.hasValue()) - def set$tMeth.pyName($step.name())$step.num()(self, value): + #if($step.name() == "Switch" || $step.name() == "Valve") + #set($boolInp = $step.getInput(0)) + #set($boolName = "$tMeth.pyName($boolInp.name())") + #if($boolInp.hasValue()) + def set$tMeth.pyName($step.name())$step.num()(self, value): 'This method is a generated setter for the condition of $step.name()' assert isinstance(value, bool) , "Source must be of type bool" self.__$tMeth.pyName($boolName) = value -#end + #end -#end + #end #end #foreach($step in $pipeline.getUniqueSteps()) #set($toParse = "$vmLoc/operations/" + $step.name()) #set($toParse = $toParse + ".vm") +#if ($step.name() != "New_Kernel" && $step.name() != "New_Point" && $step.name() != "New_Size") #parse($toParse) +#end #end diff --git a/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/operations/CV_morphologyEx.vm b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/operations/CV_morphologyEx.vm new file mode 100644 index 0000000000..affb3cfe40 --- /dev/null +++ b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/operations/CV_morphologyEx.vm @@ -0,0 +1,14 @@ + @staticmethod + def $tMeth.name($step.name())(src, op, kernel, anchor, iterations, border_type, border_value): + """Expands area of lower value in an image. + Args: + src: A numpy.ndarray. + kernel: The kernel for erosion. A numpy.ndarray. + iterations: the number of times to erode. + border_type: Opencv enum that represents a border type. + border_value: value to be used for a constant border. + Returns: + A numpy.ndarray after erosion. + """ + return cv2.morphologyEx(src, op, kernel, anchor, iterations = (int) (iterations +0.5), + borderType = border_type, borderValue = border_value) \ No newline at end of file diff --git a/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/operations/New_Kernel.vm b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/operations/New_Kernel.vm new file mode 100644 index 0000000000..b27a7bfa66 --- /dev/null +++ b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/operations/New_Kernel.vm @@ -0,0 +1,10 @@ + @staticmethod + def $tMeth.name($step.name())(shape, width, height): + """Creates kernel of given shape and size. + Args: + shape: The kernel MorphShape + size: Size of kernel as a tuple + Returns: + A numpy.ndarray representing the kernel. + """ + return cv2.getStructuringElement(shape, (int(width), int(height))) \ No newline at end of file