Skip to content

Commit 978b4a9

Browse files
SamCarlbergJLLeitschuh
authored andcommitted
Use annotations for operation descriptions (#851)
* 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
1 parent 316458a commit 978b4a9

File tree

87 files changed

+811
-517
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+811
-517
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package edu.wpi.grip.core;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
import static edu.wpi.grip.core.OperationDescription.Category;
9+
import static edu.wpi.grip.core.OperationDescription.Category.MISCELLANEOUS;
10+
11+
/**
12+
* Annotates an {@link Operation} subclass to describe it. This annotation gets transformed into a
13+
* {@link OperationDescription}. All operation classes with this annotation will be automatically
14+
* discovered and added to the palette at startup.
15+
*/
16+
@Target(ElementType.TYPE)
17+
@Retention(RetentionPolicy.RUNTIME)
18+
public @interface Description {
19+
20+
/**
21+
* The name of the operation being described.
22+
*/
23+
String name();
24+
25+
/**
26+
* A brief summary of the operation. In-depth descriptions, usage guides, and examples
27+
* should be on the wiki, not here.
28+
*/
29+
String summary();
30+
31+
/**
32+
* The category the operation belongs to. Defaults to
33+
* {@link OperationDescription.Category#MISCELLANEOUS MISCELLANEOUS}.
34+
*/
35+
Category category() default MISCELLANEOUS;
36+
37+
/**
38+
* All known aliases of the operation. If the name of the operation changes, the previous name
39+
* should be here. Defaults to an empty array.
40+
*/
41+
String[] aliases() default {};
42+
43+
/**
44+
* The name of the icon to use to display the operation. If empty ({@code ""}), no icon will be
45+
* shown. The icon should be located in {@code /edu/wpi/grip/ui/icons/}.
46+
*/
47+
String iconName() default "";
48+
49+
}

core/src/main/java/edu/wpi/grip/core/OperationDescription.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,33 @@ public class OperationDescription {
2727
private final Icon icon;
2828
private final ImmutableSet<String> aliases;
2929

30+
/**
31+
* Creates an operation description from a {@link Description @Description} annotation on
32+
* an operation subclass.
33+
*/
34+
public static OperationDescription from(Description description) {
35+
checkNotNull(description, "The description annotation cannot be null");
36+
String iconName = description.iconName();
37+
return builder()
38+
.name(description.name())
39+
.summary(description.summary())
40+
.category(description.category())
41+
.aliases(description.aliases())
42+
.icon(iconName.isEmpty() ? null : Icon.iconStream(iconName))
43+
.build();
44+
}
45+
46+
/**
47+
* Creates an operation description from a {@link Description @Description} annotation on
48+
* an operation subclass. The class is assumed to have the annotation; be careful when using this
49+
* method.
50+
*
51+
* @param clazz the class to generate a description for
52+
*/
53+
public static OperationDescription from(Class<? extends Operation> clazz) {
54+
return from(clazz.getAnnotation(Description.class));
55+
}
56+
3057
/**
3158
* Private constructor - use {@link #builder} to instantiate this class.
3259
*/
@@ -182,7 +209,7 @@ public Builder category(Category category) {
182209
}
183210

184211
/**
185-
* Sets the icon.
212+
* Sets the icon. If {@code null}, the operation will have no icon.
186213
*/
187214
public Builder icon(Icon icon) {
188215
this.icon = icon;

core/src/main/java/edu/wpi/grip/core/operations/CVOperations.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,8 @@ public class CVOperations {
366366
)),
367367

368368
new OperationMetaData(CVOperation.defaults("CV Threshold",
369-
"Apply a fixed-level threshold to each array element in an image."),
369+
"Apply a fixed-level threshold to each array element in an image.",
370+
"CV threshold"),
370371
templateFactory.create(
371372
SocketHints.Inputs.createMatSocketHint("src", false),
372373
SocketHints.Inputs.createNumberSpinnerSocketHint("thresh", 0),

core/src/main/java/edu/wpi/grip/core/operations/Operations.java

Lines changed: 222 additions & 161 deletions
Large diffs are not rendered by default.

core/src/main/java/edu/wpi/grip/core/operations/composite/BlurOperation.java

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
package edu.wpi.grip.core.operations.composite;
22

3+
import edu.wpi.grip.core.Description;
34
import edu.wpi.grip.core.Operation;
45
import edu.wpi.grip.core.OperationDescription;
56
import edu.wpi.grip.core.sockets.InputSocket;
67
import edu.wpi.grip.core.sockets.OutputSocket;
78
import edu.wpi.grip.core.sockets.SocketHint;
89
import edu.wpi.grip.core.sockets.SocketHints;
9-
import edu.wpi.grip.core.util.Icon;
1010

1111
import com.google.common.collect.ImmutableList;
12+
import com.google.inject.Inject;
1213

1314
import java.util.List;
1415

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

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

42+
@Inject
4743
@SuppressWarnings("JavadocMethod")
48-
public BlurOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
49-
outputSocketFactory) {
44+
public BlurOperation(InputSocket.Factory inputSocketFactory,
45+
OutputSocket.Factory outputSocketFactory) {
5046
this.inputSocket = inputSocketFactory.create(inputHint);
5147
this.typeSocket = inputSocketFactory.create(typeHint);
5248
this.radiusSocket = inputSocketFactory.create(radiusHint);

core/src/main/java/edu/wpi/grip/core/operations/composite/CascadeClassifierOperation.java

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
package edu.wpi.grip.core.operations.composite;
22

3+
import edu.wpi.grip.core.Description;
34
import edu.wpi.grip.core.Operation;
45
import edu.wpi.grip.core.OperationDescription;
56
import edu.wpi.grip.core.sockets.InputSocket;
67
import edu.wpi.grip.core.sockets.OutputSocket;
78
import edu.wpi.grip.core.sockets.SocketHint;
89
import edu.wpi.grip.core.sockets.SocketHints;
9-
import edu.wpi.grip.core.util.Icon;
1010

1111
import com.google.common.collect.ImmutableList;
12+
import com.google.inject.Inject;
1213

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

27-
public static final OperationDescription DESCRIPTION =
28-
OperationDescription.builder()
29-
.name("Cascade Classifier")
30-
.summary("Runs a cascade classifier on an image")
31-
.icon(Icon.iconStream("opencv"))
32-
.category(OperationDescription.Category.FEATURE_DETECTION)
33-
.build();
34-
3532
private final SocketHint<Mat> imageHint =
3633
SocketHints.Inputs.createMatSocketHint("Image", false);
3734
private final SocketHint<CascadeClassifier> classifierHint =
@@ -60,6 +57,7 @@ public class CascadeClassifierOperation implements Operation {
6057
private final InputSocket<Size> maxSizeSocket;
6158
private final OutputSocket<RectsReport> output;
6259

60+
@Inject
6361
@SuppressWarnings("JavadocMethod")
6462
public CascadeClassifierOperation(InputSocket.Factory isf, OutputSocket.Factory osf) {
6563
imageSocket = isf.create(imageHint);

core/src/main/java/edu/wpi/grip/core/operations/composite/ConvexHullsOperation.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package edu.wpi.grip.core.operations.composite;
22

3+
import edu.wpi.grip.core.Description;
34
import edu.wpi.grip.core.Operation;
45
import edu.wpi.grip.core.OperationDescription;
56
import edu.wpi.grip.core.sockets.InputSocket;
67
import edu.wpi.grip.core.sockets.OutputSocket;
78
import edu.wpi.grip.core.sockets.SocketHint;
89

910
import com.google.common.collect.ImmutableList;
11+
import com.google.inject.Inject;
1012

1113
import java.util.List;
1214

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

22-
public static final OperationDescription DESCRIPTION =
23-
OperationDescription.builder()
24-
.name("Convex Hulls")
25-
.summary("Compute the convex hulls of contours")
26-
.category(OperationDescription.Category.FEATURE_DETECTION)
27-
.build();
28-
2927
private final SocketHint<ContoursReport> contoursHint = new SocketHint.Builder<>(ContoursReport
3028
.class)
3129
.identifier("Contours").initialValueSupplier(ContoursReport::new).build();
3230

3331
private final InputSocket<ContoursReport> inputSocket;
3432
private final OutputSocket<ContoursReport> outputSocket;
3533

34+
@Inject
3635
@SuppressWarnings("JavadocMethod")
3736
public ConvexHullsOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
3837
outputSocketFactory) {

core/src/main/java/edu/wpi/grip/core/operations/composite/DesaturateOperation.java

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
package edu.wpi.grip.core.operations.composite;
22

3+
import edu.wpi.grip.core.Description;
34
import edu.wpi.grip.core.Operation;
45
import edu.wpi.grip.core.OperationDescription;
56
import edu.wpi.grip.core.sockets.InputSocket;
67
import edu.wpi.grip.core.sockets.OutputSocket;
78
import edu.wpi.grip.core.sockets.SocketHint;
89
import edu.wpi.grip.core.sockets.SocketHints;
9-
import edu.wpi.grip.core.util.Icon;
1010

1111
import com.google.common.collect.ImmutableList;
12+
import com.google.inject.Inject;
1213

1314
import java.util.List;
1415

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

25-
public static final OperationDescription DESCRIPTION =
26-
OperationDescription.builder()
27-
.name("Desaturate")
28-
.summary("Convert a color image into shades of gray.")
29-
.category(OperationDescription.Category.IMAGE_PROCESSING)
30-
.icon(Icon.iconStream("desaturate"))
31-
.build();
32-
3330
private final SocketHint<Mat> inputHint = SocketHints.Inputs.createMatSocketHint("Input", false);
3431
private final SocketHint<Mat> outputHint = SocketHints.Outputs.createMatSocketHint("Output");
3532

3633
private final InputSocket<Mat> inputSocket;
3734
private final OutputSocket<Mat> outputSocket;
3835

36+
@Inject
3937
@SuppressWarnings("JavadocMethod")
4038
public DesaturateOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
4139
outputSocketFactory) {

core/src/main/java/edu/wpi/grip/core/operations/composite/DistanceTransformOperation.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
package edu.wpi.grip.core.operations.composite;
22

3+
import edu.wpi.grip.core.Description;
34
import edu.wpi.grip.core.Operation;
45
import edu.wpi.grip.core.OperationDescription;
56
import edu.wpi.grip.core.sockets.InputSocket;
67
import edu.wpi.grip.core.sockets.OutputSocket;
78
import edu.wpi.grip.core.sockets.SocketHint;
89
import edu.wpi.grip.core.sockets.SocketHints;
9-
import edu.wpi.grip.core.util.Icon;
1010

1111
import com.google.common.collect.ImmutableList;
12+
import com.google.inject.Inject;
1213

1314
import org.bytedeco.javacpp.opencv_core.Mat;
1415

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

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

44+
@Inject
4645
@SuppressWarnings("JavadocMethod")
4746
public DistanceTransformOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
4847
outputSocketFactory) {

core/src/main/java/edu/wpi/grip/core/operations/composite/FilterContoursOperation.java

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
package edu.wpi.grip.core.operations.composite;
22

3+
import edu.wpi.grip.core.Description;
34
import edu.wpi.grip.core.Operation;
45
import edu.wpi.grip.core.OperationDescription;
56
import edu.wpi.grip.core.sockets.InputSocket;
67
import edu.wpi.grip.core.sockets.OutputSocket;
78
import edu.wpi.grip.core.sockets.SocketHint;
89
import edu.wpi.grip.core.sockets.SocketHints;
9-
import edu.wpi.grip.core.util.Icon;
1010

1111
import com.google.common.collect.ImmutableList;
12+
import com.google.inject.Inject;
1213

1314
import java.util.List;
1415

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

33-
public static final OperationDescription DESCRIPTION =
34-
OperationDescription.builder()
35-
.name("Filter Contours")
36-
.summary("Find contours matching certain criteria")
37-
.category(OperationDescription.Category.FEATURE_DETECTION)
38-
.icon(Icon.iconStream("find-contours"))
39-
.build();
40-
4138
private final SocketHint<ContoursReport> contoursHint = new SocketHint.Builder<>(ContoursReport
4239
.class)
4340
.identifier("Contours").initialValueSupplier(ContoursReport::new).build();
@@ -92,6 +89,7 @@ public class FilterContoursOperation implements Operation {
9289

9390
private final OutputSocket<ContoursReport> outputSocket;
9491

92+
@Inject
9593
@SuppressWarnings("JavadocMethod")
9694
public FilterContoursOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory
9795
outputSocketFactory) {

0 commit comments

Comments
 (0)