Skip to content

Commit

Permalink
Use annotations for operation descriptions (#851)
Browse files Browse the repository at this point in the history
* Autodiscover operations at startup. Use annotations for descriptions

Re-enable CompatibilityTest. Fix some backwards compatbility issues.

Not really a fan of injecting the Injector into the Operations class. Need to see if there's a better solution

* Cleanup from review comments
  • Loading branch information
SamCarlberg authored and JLLeitschuh committed Dec 15, 2017
1 parent 316458a commit 978b4a9
Show file tree
Hide file tree
Showing 87 changed files with 811 additions and 517 deletions.
49 changes: 49 additions & 0 deletions core/src/main/java/edu/wpi/grip/core/Description.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package edu.wpi.grip.core;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static edu.wpi.grip.core.OperationDescription.Category;
import static edu.wpi.grip.core.OperationDescription.Category.MISCELLANEOUS;

/**
* Annotates an {@link Operation} subclass to describe it. This annotation gets transformed into a
* {@link OperationDescription}. All operation classes with this annotation will be automatically
* discovered and added to the palette at startup.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Description {

/**
* The name of the operation being described.
*/
String name();

/**
* A brief summary of the operation. In-depth descriptions, usage guides, and examples
* should be on the wiki, not here.
*/
String summary();

/**
* The category the operation belongs to. Defaults to
* {@link OperationDescription.Category#MISCELLANEOUS MISCELLANEOUS}.
*/
Category category() default MISCELLANEOUS;

/**
* All known aliases of the operation. If the name of the operation changes, the previous name
* should be here. Defaults to an empty array.
*/
String[] aliases() default {};

/**
* The name of the icon to use to display the operation. If empty ({@code ""}), no icon will be
* shown. The icon should be located in {@code /edu/wpi/grip/ui/icons/}.
*/
String iconName() default "";

}
29 changes: 28 additions & 1 deletion core/src/main/java/edu/wpi/grip/core/OperationDescription.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,33 @@ public class OperationDescription {
private final Icon icon;
private final ImmutableSet<String> aliases;

/**
* Creates an operation description from a {@link Description @Description} annotation on
* an operation subclass.
*/
public static OperationDescription from(Description description) {
checkNotNull(description, "The description annotation cannot be null");
String iconName = description.iconName();
return builder()
.name(description.name())
.summary(description.summary())
.category(description.category())
.aliases(description.aliases())
.icon(iconName.isEmpty() ? null : Icon.iconStream(iconName))
.build();
}

/**
* Creates an operation description from a {@link Description @Description} annotation on
* an operation subclass. The class is assumed to have the annotation; be careful when using this
* method.
*
* @param clazz the class to generate a description for
*/
public static OperationDescription from(Class<? extends Operation> clazz) {
return from(clazz.getAnnotation(Description.class));
}

/**
* Private constructor - use {@link #builder} to instantiate this class.
*/
Expand Down Expand Up @@ -182,7 +209,7 @@ public Builder category(Category category) {
}

/**
* Sets the icon.
* Sets the icon. If {@code null}, the operation will have no icon.
*/
public Builder icon(Icon icon) {
this.icon = icon;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,8 @@ public class CVOperations {
)),

new OperationMetaData(CVOperation.defaults("CV Threshold",
"Apply a fixed-level threshold to each array element in an image."),
"Apply a fixed-level threshold to each array element in an image.",
"CV threshold"),
templateFactory.create(
SocketHints.Inputs.createMatSocketHint("src", false),
SocketHints.Inputs.createNumberSpinnerSocketHint("thresh", 0),
Expand Down
383 changes: 222 additions & 161 deletions core/src/main/java/edu/wpi/grip/core/operations/Operations.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package edu.wpi.grip.core.operations.composite;

import edu.wpi.grip.core.Description;
import edu.wpi.grip.core.Operation;
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 edu.wpi.grip.core.util.Icon;

import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;

import java.util.List;

Expand All @@ -22,18 +23,12 @@
/**
* An {@link Operation} that softens an image using one of several different filters.
*/
@Description(name = "Blur",
summary = "Blurs an image to remove noise",
category = OperationDescription.Category.IMAGE_PROCESSING,
iconName = "blur")
public class BlurOperation implements Operation {

/**
* Describes this operation. This is used by the 'Operations' class to add operations to GRIP.
*/
public static final OperationDescription DESCRIPTION =
OperationDescription.builder()
.name("Blur")
.summary("Blurs an image to remove noise")
.category(OperationDescription.Category.IMAGE_PROCESSING)
.icon(Icon.iconStream("blur"))
.build();
private final SocketHint<Mat> inputHint = SocketHints.Inputs.createMatSocketHint("Input", false);
private final SocketHint<Type> typeHint = SocketHints.createEnumSocketHint("Type", Type.BOX);
private final SocketHint<Number> radiusHint = SocketHints.Inputs
Expand All @@ -44,9 +39,10 @@ public class BlurOperation implements Operation {
private final InputSocket<Number> radiusSocket;
private final OutputSocket<Mat> outputSocket;

@Inject
@SuppressWarnings("JavadocMethod")
public BlurOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
outputSocketFactory) {
public BlurOperation(InputSocket.Factory inputSocketFactory,
OutputSocket.Factory outputSocketFactory) {
this.inputSocket = inputSocketFactory.create(inputHint);
this.typeSocket = inputSocketFactory.create(typeHint);
this.radiusSocket = inputSocketFactory.create(radiusHint);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package edu.wpi.grip.core.operations.composite;

import edu.wpi.grip.core.Description;
import edu.wpi.grip.core.Operation;
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 edu.wpi.grip.core.util.Icon;

import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;

import org.bytedeco.javacpp.opencv_core.Mat;
import org.bytedeco.javacpp.opencv_core.Rect;
Expand All @@ -22,16 +23,12 @@
/**
* Operation for identifying parts of an image with a cascade classifier.
*/
@Description(name = "Cascade Cassifier",
summary = "Runs a Haar cascade classifier on an image",
category = OperationDescription.Category.FEATURE_DETECTION,
iconName = "opencv")
public class CascadeClassifierOperation implements Operation {

public static final OperationDescription DESCRIPTION =
OperationDescription.builder()
.name("Cascade Classifier")
.summary("Runs a cascade classifier on an image")
.icon(Icon.iconStream("opencv"))
.category(OperationDescription.Category.FEATURE_DETECTION)
.build();

private final SocketHint<Mat> imageHint =
SocketHints.Inputs.createMatSocketHint("Image", false);
private final SocketHint<CascadeClassifier> classifierHint =
Expand Down Expand Up @@ -60,6 +57,7 @@ public class CascadeClassifierOperation implements Operation {
private final InputSocket<Size> maxSizeSocket;
private final OutputSocket<RectsReport> output;

@Inject
@SuppressWarnings("JavadocMethod")
public CascadeClassifierOperation(InputSocket.Factory isf, OutputSocket.Factory osf) {
imageSocket = isf.create(imageHint);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package edu.wpi.grip.core.operations.composite;

import edu.wpi.grip.core.Description;
import edu.wpi.grip.core.Operation;
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 com.google.common.collect.ImmutableList;
import com.google.inject.Inject;

import java.util.List;

Expand All @@ -17,22 +19,19 @@
* An {@link Operation} that finds the convex hull of each of a list of contours. This can help
* remove holes in detected shapes, making them easier to analyze.
*/
@Description(name = "Convex Hulls",
summary = "Compute the convex hulls of contours",
category = OperationDescription.Category.FEATURE_DETECTION)
public class ConvexHullsOperation implements Operation {

public static final OperationDescription DESCRIPTION =
OperationDescription.builder()
.name("Convex Hulls")
.summary("Compute the convex hulls of contours")
.category(OperationDescription.Category.FEATURE_DETECTION)
.build();

private final SocketHint<ContoursReport> contoursHint = new SocketHint.Builder<>(ContoursReport
.class)
.identifier("Contours").initialValueSupplier(ContoursReport::new).build();

private final InputSocket<ContoursReport> inputSocket;
private final OutputSocket<ContoursReport> outputSocket;

@Inject
@SuppressWarnings("JavadocMethod")
public ConvexHullsOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
outputSocketFactory) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package edu.wpi.grip.core.operations.composite;

import edu.wpi.grip.core.Description;
import edu.wpi.grip.core.Operation;
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 edu.wpi.grip.core.util.Icon;

import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;

import java.util.List;

Expand All @@ -20,22 +21,19 @@
/**
* An {@link Operation} that converts a color image into shades of gray.
*/
@Description(name = "Desaturate",
summary = "Convert a color image into shades of gray",
category = OperationDescription.Category.IMAGE_PROCESSING,
iconName = "desaturate")
public class DesaturateOperation implements Operation {

public static final OperationDescription DESCRIPTION =
OperationDescription.builder()
.name("Desaturate")
.summary("Convert a color image into shades of gray.")
.category(OperationDescription.Category.IMAGE_PROCESSING)
.icon(Icon.iconStream("desaturate"))
.build();

private final SocketHint<Mat> inputHint = SocketHints.Inputs.createMatSocketHint("Input", false);
private final SocketHint<Mat> outputHint = SocketHints.Outputs.createMatSocketHint("Output");

private final InputSocket<Mat> inputSocket;
private final OutputSocket<Mat> outputSocket;

@Inject
@SuppressWarnings("JavadocMethod")
public DesaturateOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
outputSocketFactory) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package edu.wpi.grip.core.operations.composite;

import edu.wpi.grip.core.Description;
import edu.wpi.grip.core.Operation;
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 edu.wpi.grip.core.util.Icon;

import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;

import org.bytedeco.javacpp.opencv_core.Mat;

Expand All @@ -23,16 +24,13 @@
/**
* GRIP {@link Operation} for {@link org.bytedeco.javacpp.opencv_imgproc#distanceTransform}.
*/
@Description(name = "Distance Transform",
summary = "Sets the values of pixels in a binary image to their distance to"
+ " the nearest black pixel",
category = OperationDescription.Category.IMAGE_PROCESSING,
iconName = "opencv")
public class DistanceTransformOperation implements Operation {

public static final OperationDescription DESCRIPTION =
OperationDescription.builder()
.name("Distance Transform")
.summary("Sets the values of pixels in a binary image to their distance to the nearest "
+ "black pixel.")
.category(OperationDescription.Category.IMAGE_PROCESSING)
.icon(Icon.iconStream("opencv"))
.build();
private final SocketHint<Mat> srcHint = SocketHints.Inputs.createMatSocketHint("Input", false);
private final SocketHint<Type> typeHint = SocketHints.createEnumSocketHint("Type", Type.DIST_L2);
private final SocketHint<MaskSize> maskSizeHint = SocketHints.createEnumSocketHint("Mask size",
Expand All @@ -43,6 +41,7 @@ public class DistanceTransformOperation implements Operation {
private final InputSocket<MaskSize> maskSizeSocket;
private final OutputSocket<Mat> outputSocket;

@Inject
@SuppressWarnings("JavadocMethod")
public DistanceTransformOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
outputSocketFactory) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package edu.wpi.grip.core.operations.composite;

import edu.wpi.grip.core.Description;
import edu.wpi.grip.core.Operation;
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 edu.wpi.grip.core.util.Icon;

import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;

import java.util.List;

Expand All @@ -28,16 +29,12 @@
* small objects, as well as contours that do not meet the expected characteristics of the feature
* we're actually looking for. So, this operation can help narrow them down.
*/
@Description(name = "Filter Contours",
summary = "Find contours matching certain criteria",
category = OperationDescription.Category.FEATURE_DETECTION,
iconName = "find-contours")
public class FilterContoursOperation implements Operation {

public static final OperationDescription DESCRIPTION =
OperationDescription.builder()
.name("Filter Contours")
.summary("Find contours matching certain criteria")
.category(OperationDescription.Category.FEATURE_DETECTION)
.icon(Icon.iconStream("find-contours"))
.build();

private final SocketHint<ContoursReport> contoursHint = new SocketHint.Builder<>(ContoursReport
.class)
.identifier("Contours").initialValueSupplier(ContoursReport::new).build();
Expand Down Expand Up @@ -92,6 +89,7 @@ public class FilterContoursOperation implements Operation {

private final OutputSocket<ContoursReport> outputSocket;

@Inject
@SuppressWarnings("JavadocMethod")
public FilterContoursOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
outputSocketFactory) {
Expand Down
Loading

0 comments on commit 978b4a9

Please sign in to comment.