Skip to content

Instantly share code, notes, and snippets.

@SamCarlberg
Last active March 31, 2016 19:39
Show Gist options
  • Save SamCarlberg/fcb16a1a738b7f29c5a3add3e8bd10ca to your computer and use it in GitHub Desktop.
Save SamCarlberg/fcb16a1a738b7f29c5a3add3e8bd10ca to your computer and use it in GitHub Desktop.
GRIP operation refactoring
package edu.wpi.grip.core.operations.composite;
import edu.wpi.grip.core.Operation;
import edu.wpi.grip.core.OperationDescriptor;
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.Icons;
import java.util.function.BiFunction;
import static org.bytedeco.javacpp.opencv_core.Mat;
import static org.bytedeco.javacpp.opencv_core.Size;
import static org.bytedeco.javacpp.opencv_imgproc.GaussianBlur;
import static org.bytedeco.javacpp.opencv_imgproc.bilateralFilter;
import static org.bytedeco.javacpp.opencv_imgproc.blur;
import static org.bytedeco.javacpp.opencv_imgproc.medianBlur;
/**
* An {@link Operation} that softens an image using one of several different filters
*/
public class BlurOperation implements Operation {
/**
* Describes this operation. This is used by the 'Operations' class to add operations to GRIP.
*/
public static final BiFunction<InputSocket.Factory, OutputSocket.Factory, OperationDescriptor>
DESCRIPTOR_FUNCTION =
(isf, osf) -> new OperationDescriptor<>(
() -> new BlurOperation(isf, osf),
"Blur",
"Soften the details of an image to remove noise",
OperationDescriptor.Category.IMAGE_PROCESSING,
Icons.iconStream("blur"));
private enum Type {
BOX("Box Blur"), GAUSSIAN("Gaussian Blur"), MEDIAN("Median Filter"), BILATERAL_FILTER("Bilateral Filter");
private final String label;
Type(String label) {
this.label = label;
}
@Override
public String toString() {
return this.label;
}
}
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.createNumberSliderSocketHint("Radius", 0.0, 0.0, 100.0);
private final SocketHint<Mat> outputHint = SocketHints.Inputs.createMatSocketHint("Output", true);
private final InputSocket<Mat> inputSocket;
private final InputSocket<Type> typeSocket;
private final InputSocket<Number> radiusSocket;
private final OutputSocket<Mat> outputSocket;
public BlurOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory outputSocketFactory) {
this.inputSocket = inputSocketFactory.create(inputHint);
this.typeSocket = inputSocketFactory.create(typeHint);
this.radiusSocket = inputSocketFactory.create(radiusHint);
this.outputSocket = outputSocketFactory.create(outputHint);
}
@Override
public InputSocket<?>[] createInputSockets() {
return new InputSocket<?>[]{
inputSocket,
typeSocket,
radiusSocket
};
}
@Override
public OutputSocket<?>[] createOutputSockets() {
return new OutputSocket<?>[]{
outputSocket
};
}
@Override
public void perform() {
final Mat input = inputSocket.getValue().get();
final Type type = typeSocket.getValue().get();
final Number radius = radiusSocket.getValue().get();
final Mat output = outputSocket.getValue().get();
int kernelSize;
switch (type) {
case BOX:
// Box filter kernels must have an odd size
kernelSize = 2 * radius.intValue() + 1;
blur(input, output, new Size(kernelSize, kernelSize));
break;
case GAUSSIAN:
// A Gaussian blur radius is a standard deviation, so a kernel that extends three radii in either direction
// from the center should account for 99.7% of the theoretical influence on each pixel.
kernelSize = 6 * radius.intValue() + 1;
GaussianBlur(input, output, new Size(kernelSize, kernelSize), radius.doubleValue());
break;
case MEDIAN:
kernelSize = 2 * radius.intValue() + 1;
medianBlur(input, output, kernelSize);
break;
case BILATERAL_FILTER:
bilateralFilter(input, output, -1, radius.doubleValue(), radius.doubleValue());
break;
default:
throw new IllegalArgumentException("Illegal blur type: " + type);
}
outputSocket.setValue(output);
}
}
package edu.wpi.grip.core;
import com.google.common.collect.ImmutableSet;
import java.io.InputStream;
import java.util.Optional;
import java.util.function.Supplier;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* An interface describing how an operation should be displayed in the {@link Palette} to the user.
*
* @param <O> the type of operation that this entry describes
*/
public final class OperationDescriptor<O extends Operation> {
private final Supplier<O> operationSupplier;
private final String name;
private final String description;
private final Category category;
private final Optional<InputStream> icon;
private final ImmutableSet<String> aliases;
public OperationDescriptor(Supplier<O> operationSupplier,
String name,
String description,
Category category,
InputStream iconStream,
String... aliases) {
this.operationSupplier = checkNotNull(operationSupplier);
this.name = checkNotNull(name);
this.description = checkNotNull(description);
this.category = checkNotNull(category);
this.icon = Optional.ofNullable(iconStream);
this.aliases = ImmutableSet.copyOf(checkNotNull(aliases));
}
/**
* The categories that entries can be in.
*/
public enum Category {
IMAGE_PROCESSING,
FEATURE_DETECTION,
NETWORK,
LOGICAL,
OPENCV,
MISCELLANEOUS,
}
/**
* @return The unique user-facing name of the operation, such as "Gaussian Blur"
*/
public String getName() {
return name;
}
/**
* @return A description of the operation.
*/
public String getDescription() {
return description;
}
/**
* @return What category the operation falls under. This is used to organize them in the GUI
*/
public Category getCategory() {
return category;
}
/**
* @return An {@link InputStream} of a 128x128 image to show the user as a representation of the operation.
*/
public Optional<InputStream> getIcon() {
return icon;
}
/**
* @return Any old unique user-facing names of the operation. This is used to preserve compatibility with
* old versions of GRIP if the operation name changes.
*/
public ImmutableSet<String> getAliases() {
return aliases;
}
/**
* Creates a new instance of the operation described by this descriptor.
*/
public O create() {
return operationSupplier.get();
}
}
@Singleton
public class Operations {
private final EventBus eventBus;
private final ImmutableList<BiFunction<InputSocket.Factory, OutputSocket.Factory, OperationDescriptor>> operations;
private final InputSocket.Factory inputSocketFactory;
private final OutputSocket.Factory outputSocketFactory;
@Inject
@SuppressWarnings("unchecked")
Operations(EventBus eventBus,
@Named("ntManager") MapNetworkPublisherFactory ntPublisherFactory,
@Named("rosManager") ROSNetworkPublisherFactory rosPublishFactory,
InputSocket.Factory inputSocketFactory,
OutputSocket.Factory outputSocketFactory) {
this.eventBus = checkNotNull(eventBus, "EventBus cannot be null");
checkNotNull(ntPublisherFactory, "ntPublisherFactory cannot be null");
checkNotNull(rosPublishFactory, "rosPublishFactory cannot be null");
this.inputSocketFactory = checkNotNull(inputSocketFactory, "inputSocketFactory cannot be null");
this.outputSocketFactory = checkNotNull(outputSocketFactory, "outputSocketFactory cannot be null");
this.operations = ImmutableList.of(
BlurOperation.DESCRIPTOR_FUNCTION,
ConvexHullsOperation.DESCRIPTOR_FUNCTION
);
}
public void addOperations() {
operations.stream()
.map(f -> f.apply(inputSocketFactory, outputSocketFactory))
.map(OperationAddedEvent::new)
.forEach(eventBus::post);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment