Last active
March 31, 2016 19:39
-
-
Save SamCarlberg/fcb16a1a738b7f29c5a3add3e8bd10ca to your computer and use it in GitHub Desktop.
GRIP operation refactoring
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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