From c78b49c6029c8ab4d2023e8aa45d4d3d21cefea3 Mon Sep 17 00:00:00 2001 From: Tim Winters Date: Wed, 27 Dec 2017 19:22:33 -0500 Subject: [PATCH 1/5] Add morphology operation --- .../grip/core/operations/CVOperations.java | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) 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..0124b59259 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 @@ -309,7 +309,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.Inputs.createMatSocketHint("kernel", true), + SocketHints.createEnumSocketHint("op", CVMorphologyTypesEnum.MORPH_OPEN), + 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, kernel, op, 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 +447,21 @@ public enum CVBorderTypesEnum { } } + public enum CVMorphologyTypesEnum { + MORPH_OPEN(2), + MORPH_CLOSE(3), + MORPH_GRADIENT(4), + MORPH_TOPHAT(5), + MORPH_BLACKHAT(6), + MORPH_HITMISS(7); + + public final int value; + + CVMorphologyTypesEnum(int value) { + this.value = value; + } + } + /** * All of the operations that this list supplies. From 72d99197d3ac9cc4e60ee403efc0d7ab8e0e7623 Mon Sep 17 00:00:00 2001 From: Tim Winters Date: Thu, 28 Dec 2017 15:26:14 -0500 Subject: [PATCH 2/5] Add kernel operation and add both kernel and morphology to code generation --- .../grip/core/operations/CVOperations.java | 20 ++--- .../operations/opencv/NewKernelOperation.java | 89 +++++++++++++++++++ .../cpp/operations/CV_morphologyEx.vm | 14 +++ .../cpp/operations/New_Kernel.vm | 8 ++ .../java/operations/CV_morphologyEx.vm | 24 +++++ .../java/operations/New_Kernel.vm | 8 ++ .../python/operations/CV_morphologyEx.vm | 14 +++ .../python/operations/New_Kernel.vm | 10 +++ 8 files changed, 177 insertions(+), 10 deletions(-) create mode 100644 core/src/main/java/edu/wpi/grip/core/operations/opencv/NewKernelOperation.java create mode 100644 ui/src/main/resources/edu/wpi/grip/ui/codegeneration/cpp/operations/CV_morphologyEx.vm create mode 100644 ui/src/main/resources/edu/wpi/grip/ui/codegeneration/cpp/operations/New_Kernel.vm create mode 100644 ui/src/main/resources/edu/wpi/grip/ui/codegeneration/java/operations/CV_morphologyEx.vm create mode 100644 ui/src/main/resources/edu/wpi/grip/ui/codegeneration/java/operations/New_Kernel.vm create mode 100644 ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/operations/CV_morphologyEx.vm create mode 100644 ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/operations/New_Kernel.vm 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 0124b59259..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; @@ -313,8 +314,8 @@ public class CVOperations { "Performs advanced morphological transformations."), templateFactory.create( SocketHints.Inputs.createMatSocketHint("src", false), + SocketHints.createEnumSocketHint("op", MorphTypesEnum.MORPH_OPEN), SocketHints.Inputs.createMatSocketHint("kernel", true), - SocketHints.createEnumSocketHint("op", CVMorphologyTypesEnum.MORPH_OPEN), new SocketHint.Builder<>(Point.class).identifier("anchor").initialValueSupplier( () -> new Point(-1, -1)).build(), SocketHints.Inputs.createNumberSpinnerSocketHint("iterations", 1), @@ -322,7 +323,7 @@ public class CVOperations { new SocketHint.Builder<>(Scalar.class).identifier("borderValue") .initialValueSupplier(opencv_imgproc::morphologyDefaultBorderValue).build(), SocketHints.Outputs.createMatSocketHint("dst"), - (src, kernel, op, anchor, iterations, borderType, borderValue, dst) -> { + (src, op, kernel, anchor, iterations, borderType, borderValue, dst) -> { opencv_imgproc.morphologyEx(src, dst, op.value, kernel, anchor, iterations.intValue(), borderType.value, borderValue); } @@ -447,17 +448,16 @@ public enum CVBorderTypesEnum { } } - public enum CVMorphologyTypesEnum { - MORPH_OPEN(2), - MORPH_CLOSE(3), - MORPH_GRADIENT(4), - MORPH_TOPHAT(5), - MORPH_BLACKHAT(6), - MORPH_HITMISS(7); + 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; - CVMorphologyTypesEnum(int value) { + CVMorphTypesEnum(int value) { this.value = value; } } 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/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 From 5bbadde53e63db67f5bf4fd1992991be11f05247 Mon Sep 17 00:00:00 2001 From: Tim Winters Date: Fri, 29 Dec 2017 11:28:09 -0500 Subject: [PATCH 3/5] Don't reallocate Point, Size, or Kernel --- .../grip/ui/codegeneration/python/Pipeline.vm | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) 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..e3effb5541 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,9 +36,10 @@ 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") From bd1c4f4c5c0474f31b12a93e25082d9db116a26c Mon Sep 17 00:00:00 2001 From: Tim Winters Date: Mon, 1 Jan 2018 22:40:49 -0500 Subject: [PATCH 4/5] Add morph function to python code --- .../grip/ui/codegeneration/python/Pipeline.vm | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) 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 e3effb5541..6e41d16845 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 @@ -42,24 +42,25 @@ class $className: #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") + #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 From 5cfc0ea65a3dd1fb3855589a6040b36970c6e40c Mon Sep 17 00:00:00 2001 From: Tim Winters Date: Tue, 2 Jan 2018 10:14:26 -0500 Subject: [PATCH 5/5] Fix python generated code format --- .../edu/wpi/grip/ui/codegeneration/python/Pipeline.vm | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 6e41d16845..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 @@ -56,10 +56,11 @@ class $className: #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") +#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