From 40cdbc1dc59b823df77a4b06f6ba111ec9776ebe Mon Sep 17 00:00:00 2001 From: Vasista Vovveti Date: Mon, 17 Aug 2020 14:29:10 -0400 Subject: [PATCH] Added crop functionality (#926) --- .../operations/composite/CropOperation.java | 129 ++++++++++++++++++ .../wpi/grip/ui/codegeneration/cpp/macros.vm | 20 ++- .../ui/codegeneration/cpp/operations/Crop.vm | 40 ++++++ .../wpi/grip/ui/codegeneration/java/macros.vm | 2 +- .../ui/codegeneration/java/operations/Crop.vm | 62 +++++++++ .../ui/codegeneration/python/enums/Origin.vm | 1 + .../grip/ui/codegeneration/python/macros.vm | 2 +- .../codegeneration/python/operations/Crop.vm | 38 ++++++ .../resources/edu/wpi/grip/ui/icons/crop.png | Bin 0 -> 26214 bytes 9 files changed, 289 insertions(+), 5 deletions(-) create mode 100644 core/src/main/java/edu/wpi/grip/core/operations/composite/CropOperation.java create mode 100644 ui/src/main/resources/edu/wpi/grip/ui/codegeneration/cpp/operations/Crop.vm create mode 100644 ui/src/main/resources/edu/wpi/grip/ui/codegeneration/java/operations/Crop.vm create mode 100644 ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/enums/Origin.vm create mode 100644 ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/operations/Crop.vm create mode 100644 ui/src/main/resources/edu/wpi/grip/ui/icons/crop.png diff --git a/core/src/main/java/edu/wpi/grip/core/operations/composite/CropOperation.java b/core/src/main/java/edu/wpi/grip/core/operations/composite/CropOperation.java new file mode 100644 index 0000000000..d432526273 --- /dev/null +++ b/core/src/main/java/edu/wpi/grip/core/operations/composite/CropOperation.java @@ -0,0 +1,129 @@ +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.MatWrapper; +import edu.wpi.grip.core.Operation; +import edu.wpi.grip.core.sockets.InputSocket; +import edu.wpi.grip.core.sockets.OutputSocket; +import edu.wpi.grip.core.sockets.SocketHints; + +import com.google.common.collect.ImmutableList; +import com.google.inject.Inject; + +import java.util.List; + +import static org.bytedeco.javacpp.opencv_core.Rect; + + +/** + * Crop an image to an exact width and height using one of several origin modes. Cropping + * images down can be a useful optimization. + */ +@Description(name = "Crop", + summary = "Crop an image to an exact size", + category = OperationCategory.IMAGE_PROCESSING, + iconName = "crop") +public class CropOperation implements Operation { + + private final InputSocket inputSocket; + private final InputSocket xSocket; + private final InputSocket ySocket; + + private final InputSocket widthSocket; + private final InputSocket heightSocket; + private final InputSocket originSocket; + + private final OutputSocket outputSocket; + + @Inject + @SuppressWarnings("JavadocMethod") + public CropOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory + outputSocketFactory) { + this.inputSocket = inputSocketFactory.create(SocketHints + .createImageSocketHint("Input")); + this.xSocket = inputSocketFactory.create(SocketHints.Inputs + .createNumberSpinnerSocketHint("X", 100)); + this.ySocket = inputSocketFactory.create(SocketHints.Inputs + .createNumberSpinnerSocketHint("Y", 100)); + this.widthSocket = inputSocketFactory.create(SocketHints.Inputs + .createNumberSpinnerSocketHint("Width", 50)); + this.heightSocket = inputSocketFactory.create(SocketHints.Inputs + .createNumberSpinnerSocketHint("Height", 50)); + this.originSocket = inputSocketFactory + .create(SocketHints.createEnumSocketHint("Origin", Origin.CENTER)); + + this.outputSocket = outputSocketFactory.create(SocketHints + .createImageSocketHint("Output")); + } + + @Override + public List getInputSockets() { + return ImmutableList.of( + inputSocket, + xSocket, + ySocket, + widthSocket, + heightSocket, + originSocket + ); + } + + @Override + public List getOutputSockets() { + return ImmutableList.of( + outputSocket + ); + } + + @Override + public void perform() { + final MatWrapper input = inputSocket.getValue().get(); + final MatWrapper output = outputSocket.getValue().get(); + final Number x = xSocket.getValue().get(); + final Number y = ySocket.getValue().get(); + final Number width = widthSocket.getValue().get(); + final Number height = heightSocket.getValue().get(); + + final Origin origin = originSocket.getValue().get(); + + final Rect regionOfInterest = new Rect( + x.intValue() + (int) (origin.xOffsetMultiplier * width.intValue()), + y.intValue() + (int) (origin.yOffsetMultiplier * height.intValue()), + width.intValue(), + height.intValue() + ); + + //apply() returns a sub-matrix; It does not modify the input Mat: https://github.com/WPIRoboticsProjects/GRIP/pull/926 + if (input.isCpu()) { + output.set(input.getCpu().apply(regionOfInterest)); + } else { + output.set(input.getGpu().apply(regionOfInterest)); + } + + outputSocket.setValue(output); + } + + private enum Origin { + TOP_LEFT("Top Left", 0, 0), + TOP_RIGHT("Top Right", -1, 0), + BOTTOM_LEFT("Bottom Left", 0, -1), + BOTTOM_RIGHT("Bottom Right", -1, -1), + CENTER("Center", -.5, -.5); + + final String label; + final double xOffsetMultiplier; + final double yOffsetMultiplier; + + Origin(String label, double xOffsetMultiplier, double yOffsetMultiplier) { + this.label = label; + this.xOffsetMultiplier = xOffsetMultiplier; + this.yOffsetMultiplier = yOffsetMultiplier; + } + + @Override + public String toString() { + return label; + } + } +} diff --git a/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/cpp/macros.vm b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/cpp/macros.vm index aa0660d0b3..c7e74cdf86 100644 --- a/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/cpp/macros.vm +++ b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/cpp/macros.vm @@ -14,7 +14,8 @@ int#elseif($type.equalsIgnoreCase("MaskSize")) std::string#elseif($type.equalsIgnoreCase("Interpolation")) int#elseif($type.equalsIgnoreCase("FlipCode")) FlipCode#elseif($type.equalsIgnoreCase("List")) -List#else +List#elseif($type.equalsIgnoreCase("Origin")) +Origin#else cv::$type#end#end #macro(funPassType $baseType) @@ -68,7 +69,7 @@ $name(#foreach($inp in $step.getInputs())#param($inp $names[$count])#set($count int ${tMeth.name($input.name())} = $input.value(); // ENUM $input.type() #elseif ($input.type().equals("MaskSize") ) std::string ${tMeth.name($input.name())} = "$input.value()"; -#elseif ($input.type().equals("FlipCode") || $input.type().equals("BlurType") ) +#elseif ($input.type().equals("FlipCode") || $input.type().equals("BlurType") || $input.type().equals("Origin")) $input.type() ${tMeth.name($input.name())} = $input.type()::#cvVal($input.value()); #elseif ($input.type().contains("Type") || $input.type().equals("Interpolation")) int ${tMeth.name($input.name())} = #cvVal($input.value()); @@ -100,7 +101,12 @@ cv::INTER_AREA#elseif($value.equals("Box Blur")) BOX#elseif($value.equals("Gaussian Blur")) GAUSSIAN#elseif($value.equals("Median Filter")) MEDIAN#elseif($value.equals("Bilateral Filter")) -BILATERAL#else +BILATERAL#elseif($value.equals("Top Left")) +TOP_LEFT#elseif($value.equals("Top Right")) +TOP_RIGHT#elseif($value.equals("Bottom Left")) +BOTTOM_LEFT#elseif($value.equals("Bottom Right")) +BOTTOM_RIGHT#elseif($value.equals("Center")) +CENTER#else $value#end#end #macro(enumType $uniStep) @@ -113,6 +119,14 @@ $value#end#end enum BlurType { BOX, GAUSSIAN, MEDIAN, BILATERAL }; +#elseif($uniStep.name().equalsIgnoreCase("Crop")) +/** +* A representation of the different origins that can be used. +* +*/ +enum Origin { + TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, CENTER +}; #elseif($name.contains("Flip")) /** * Code used for CV_flip. diff --git a/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/cpp/operations/Crop.vm b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/cpp/operations/Crop.vm new file mode 100644 index 0000000000..9facbc2a8a --- /dev/null +++ b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/cpp/operations/Crop.vm @@ -0,0 +1,40 @@ + /** + * Crops an image. + * + * @param input The image on which to perform the crop. + * @param x The x (horiontal) location of the crop. + * @param y The y (vertical) location of the crop. + * @param width The width(horizontal length) of the crop. + * @param height The height(vertical length) of the crop. + * @param origin The Origin of the crop. + * @param output The image in which to store the output. + */ + void $className::#func($step, ["input", "x", "y", "width", "height", "origin", "output"]) { + double xOffsetMultiplier = 0; + double yOffsetMultiplier = 0; + switch(origin) { + case TOP_RIGHT: + xOffsetMultiplier = -1; + break; + case BOTTOM_LEFT: + yOffsetMultiplier = -1; + break; + case BOTTOM_RIGHT: + xOffsetMultiplier = -1; + yOffsetMultiplier = -1; + break; + case CENTER: + xOffsetMultiplier = -.5; + yOffsetMultiplier = -.5; + break; + default: //origin == TOP_LEFT + break; + } + cv::Rect regionOfInterest = cv::Rect( + (int) (x + xOffsetMultiplier * width), + (int) (y + yOffsetMultiplier * height), + (int) width, + (int) height + ); + output = input(regionOfInterest); + } \ No newline at end of file diff --git a/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/java/macros.vm b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/java/macros.vm index 918731f12e..d84c6caf68 100644 --- a/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/java/macros.vm +++ b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/java/macros.vm @@ -24,7 +24,7 @@ Ref<$obj>#end int ${tMeth.name($input.name())} = Imgproc.$input.value(); #elseif ($input.type().equals("MaskSize")) int ${tMeth.name($input.name())} = $input.value().substring(0,1); -#elseif ($input.type().equals("BlurType")) +#elseif ($input.type().equals("BlurType") || $input.type().equals("Origin")) $input.type() ${tMeth.name($input.name())} = ${input.type()}.get("${input.value()}"); #elseif ($input.type().contains("Type") || $input.type().equals("Interpolation")) int ${tMeth.name($input.name())} = #enum($input.value()); diff --git a/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/java/operations/Crop.vm b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/java/operations/Crop.vm new file mode 100644 index 0000000000..a17d16a5c6 --- /dev/null +++ b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/java/operations/Crop.vm @@ -0,0 +1,62 @@ + /** + * An indication of which type of which point on the box is used as the origin. + * Choices are TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, CENTER + */ + enum Origin { + TOP_LEFT("Top Left", 0, 0), + TOP_RIGHT("Top Right", -1, 0), + BOTTOM_LEFT("Bottom Left", 0, -1), + BOTTOM_RIGHT("Bottom Right", -1, -1), + CENTER("Center", -.5, -.5); + + private final String label; + private final double xOffsetMultiplier; + private final double yOffsetMultiplier; + + Origin(String label, double xOffsetMultiplier, double yOffsetMultiplier) { + this.label = label; + this.xOffsetMultiplier = xOffsetMultiplier; + this.yOffsetMultiplier = yOffsetMultiplier; + } + + public static Origin get(String label){ + switch(label){ + case "Top Left": + return TOP_LEFT; + case "Top Right": + return TOP_RIGHT; + case "Bottom Left": + return BOTTOM_LEFT; + case "Bottom Right": + return BOTTOM_RIGHT; + default: + return CENTER; + } + } + + @Override + public String toString() { + return label; + } + } + + /** + * Crops an image. + * @param input The image on which to perform the crop. + * @param x The x (horiontal) location of the crop. + * @param y The y (vertical) location of the crop. + * @param width The width(horizontal length) of the crop. + * @param height The height(vertical length) of the crop. + * @param origin The Origin of the crop. + * @param output The image in which to store the output. + */ + private void $tMeth.name($step.name())(Mat input, double x, double y, double width, double height, Origin origin, Mat output) { + + Rect regionOfInterest = new Rect( + (int) (x + origin.xOffsetMultiplier * width), + (int) (y + origin.yOffsetMultiplier * height), + (int) width, + (int) height + ); + input.submat(regionOfInterest).copyTo(output); + } \ No newline at end of file diff --git a/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/enums/Origin.vm b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/enums/Origin.vm new file mode 100644 index 0000000000..fe39261d86 --- /dev/null +++ b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/enums/Origin.vm @@ -0,0 +1 @@ +Origin = Enum('Origin', 'Top_Left Top_Right Bottom_Left Bottom_Right Center') diff --git a/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/macros.vm b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/macros.vm index 691963d192..8fb88d3037 100644 --- a/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/macros.vm +++ b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/macros.vm @@ -4,7 +4,7 @@ self.$tMeth.name($inp.name())#end #macro(newInput $inp) #if($inp.hasValue()) -#if($inp.type().equals("BlurType") || $inp.type().equals("FlipCode")) +#if($inp.type().equals("BlurType") || $inp.type().equals("FlipCode") || $inp.type().equals("Origin")) #input($inp) = ${inp.type()}.$inp.value().replaceAll(' ','_')#elseif($inp.type().equals("MaskSize")) #input($inp) = $inp.value().substring(0,1)#elseif($inp.value().contains("source")) #input($inp) = $inp.value()#elseif ($inp.type().contains("Enum") || diff --git a/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/operations/Crop.vm b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/operations/Crop.vm new file mode 100644 index 0000000000..00bad13c09 --- /dev/null +++ b/ui/src/main/resources/edu/wpi/grip/ui/codegeneration/python/operations/Crop.vm @@ -0,0 +1,38 @@ +#needs("Origin") + @staticmethod + def $tMeth.name($step.name())(src, x, y, width, height, origin): + """Crops an image. + Args: + src: The source mat (numpy.ndarray). + x: The x (horiontal) location of the crop. + y: The y (vertical) location of the crop. + width: The width(horizontal length) of the crop. + height: The height(vertical length) of the crop. + origin: The Origin of the crop. + Returns: + A numpy.ndarray that has been cropped. + """ + + # origin is Top_Left + x_offset_multiplier = 0 + y_offset_multiplier = 0 + + if origin is Origin.Top_Right: + x_offset_multiplier = -1 + elif origin is Origin.Bottom_Left: + y_offset_multiplier = -1 + elif origin is Origin.Bottom_Right: + x_offset_multiplier = -1 + y_offset_multiplier = -1 + elif origin is Origin.Center: + x_offset_multiplier = -.5 + y_offset_multiplier = -.5 + + # Flooring to keep consistency with Java + x_start = int(x + x_offset_multiplier * width) + x_end = int(x_start + width) + + y_start = int(y + y_offset_multiplier * height) + y_end = int(y_start + height) + + return src[y_start: y_end, x_start : x_end] diff --git a/ui/src/main/resources/edu/wpi/grip/ui/icons/crop.png b/ui/src/main/resources/edu/wpi/grip/ui/icons/crop.png new file mode 100644 index 0000000000000000000000000000000000000000..beb3a1010165ce4dff6d2f203d8ba4219d767be3 GIT binary patch literal 26214 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