Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d3cc497
Add operation to save image snapshots to local disk.
PeterJohnson Apr 23, 2016
745ec6e
Fix up SaveImageOperation based on PR comments.
PeterJohnson May 7, 2016
2eb2d38
Merge remote-tracking branch 'origin/master' into image-snapshots
AustinShalit May 28, 2016
d5a7d9d
Add FileManager
AustinShalit Jun 9, 2016
e40464a
Update GRIP log directory
AustinShalit Jun 9, 2016
625710f
Update SaveImageOperation to use FileManager
AustinShalit Jun 9, 2016
aae316c
Fix failing tests
AustinShalit Jun 9, 2016
0e95afb
Merge remote-tracking branch 'origin/master' into image-snapshots
AustinShalit Jun 9, 2016
cfe1ffa
Remove deprecated objects
AustinShalit Jun 9, 2016
7a38983
Add documentation
AustinShalit Jun 9, 2016
5d37d7f
Use Milliseconds
AustinShalit Jun 9, 2016
f48fcc6
Update Log Directory
AustinShalit Jun 9, 2016
b352d9a
Add SaveImageOperationTests
AustinShalit Jun 23, 2016
411718e
Merge remote-tracking branch 'origin/master' into image-snapshots
AustinShalit Jun 23, 2016
532bb07
Update field scope
AustinShalit Jun 25, 2016
fb8590b
Reverse commit "Update field scope"
AustinShalit Jun 25, 2016
3365bf5
add filetype selector
AustinShalit Jun 25, 2016
cfbe494
Merge remote-tracking branch 'origin/master' into image-snapshots
AustinShalit Jun 25, 2016
3dbb57c
Changes based on comments
AustinShalit Jun 27, 2016
31d56ba
Merge remote-tracking branch 'origin/master' into image-snapshots
AustinShalit Jul 1, 2016
2a13575
Remove injection from test
AustinShalit Jul 1, 2016
67fe91f
Change enum styling
AustinShalit Jul 1, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions core/src/main/java/edu/wpi/grip/core/FileManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package edu.wpi.grip.core;

public interface FileManager {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation

void saveImage(byte[] image, String fileName);
}
2 changes: 1 addition & 1 deletion core/src/main/java/edu/wpi/grip/core/GRIPCoreModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public class GRIPCoreModule extends AbstractModule {
globalLogger.removeHandler(handler);
}

final Handler fileHandler = new FileHandler("%h/GRIP.log");//Log to the file "GRIPlogger.log"
final Handler fileHandler = new FileHandler("%t/GRIP.log");//Log to the file "GRIPlogger.log"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you changing the log file directory?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never actually finished what I was doing... Going to move the log file into the GRIP directory.


//Set level to handler and logger
fileHandler.setLevel(Level.FINE);
Expand Down
42 changes: 42 additions & 0 deletions core/src/main/java/edu/wpi/grip/core/GripFileManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package edu.wpi.grip.core;

import com.google.common.io.Files;
import com.google.inject.Singleton;

import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import static com.google.common.base.Preconditions.checkNotNull;

@Singleton
public class GripFileManager implements FileManager {

private static final Logger logger = Logger.getLogger(GripFileManager.class.getName());

private static final File gripDirectory = new File(System.getProperty("user.home") + File.separator + "GRIP");
private static final File imageDirectory = new File(gripDirectory, "images");

public GripFileManager() {
gripDirectory.mkdirs();
imageDirectory.mkdirs();
}

@Override
public void saveImage(byte[] image, String fileName) {
checkNotNull(image);
checkNotNull(fileName);

File file = new File(imageDirectory, fileName);
Runnable runnable = () -> {
try {
imageDirectory.mkdirs(); // If the user deletes the directory
Files.write(image, file);
} catch (IOException ex) {
logger.log(Level.WARNING, ex.getMessage(), ex);
}
};
new Thread(runnable).start();
}
}
10 changes: 10 additions & 0 deletions core/src/main/java/edu/wpi/grip/core/GripFileModule.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package edu.wpi.grip.core;

import com.google.inject.AbstractModule;

public class GripFileModule extends AbstractModule {
@Override
protected void configure() {
bind(FileManager.class).to(GripFileManager.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import edu.wpi.grip.core.FileManager;
import edu.wpi.grip.core.OperationMetaData;
import edu.wpi.grip.core.events.OperationAddedEvent;
import edu.wpi.grip.core.operations.composite.*;
Expand Down Expand Up @@ -39,11 +40,13 @@ public class Operations {
Operations(EventBus eventBus,
@Named("ntManager") MapNetworkPublisherFactory ntPublisherFactory,
@Named("rosManager") ROSNetworkPublisherFactory rosPublishFactory,
FileManager fileManager,
InputSocket.Factory isf,
OutputSocket.Factory osf) {
this.eventBus = checkNotNull(eventBus, "EventBus cannot be null");
checkNotNull(ntPublisherFactory, "ntPublisherFactory cannot be null");
checkNotNull(rosPublishFactory, "rosPublishFactory cannot be null");
checkNotNull(fileManager,"fileManager cannot be null");
this.operations = ImmutableList.of(
// Composite operations
new OperationMetaData(BlurOperation.DESCRIPTION, () -> new BlurOperation(isf, osf)),
Expand All @@ -62,6 +65,7 @@ public class Operations {
new OperationMetaData(PublishVideoOperation.DESCRIPTION, () -> new PublishVideoOperation(isf)),
new OperationMetaData(ResizeOperation.DESCRIPTION, () -> new ResizeOperation(isf, osf)),
new OperationMetaData(RGBThresholdOperation.DESCRIPTION, () -> new RGBThresholdOperation(isf, osf)),
new OperationMetaData(SaveImageOperation.DESCRIPTION, () -> new SaveImageOperation(isf, osf, fileManager)),
new OperationMetaData(SwitchOperation.DESCRIPTION, () -> new SwitchOperation(isf, osf)),
new OperationMetaData(ValveOperation.DESCRIPTION, () -> new ValveOperation(isf, osf)),
new OperationMetaData(WatershedOperation.DESCRIPTION, () -> new WatershedOperation(isf, osf)),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package edu.wpi.grip.core.operations.composite;

import com.google.common.base.Stopwatch;
import com.google.common.collect.ImmutableList;
import edu.wpi.grip.core.FileManager;
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 org.bytedeco.javacpp.BytePointer;
import org.bytedeco.javacpp.IntPointer;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static org.bytedeco.javacpp.opencv_core.Mat;
import static org.bytedeco.javacpp.opencv_imgcodecs.CV_IMWRITE_JPEG_QUALITY;
import static org.bytedeco.javacpp.opencv_imgcodecs.imencode;

/**
* Save JPEG files periodically to the local disk.
*/
public class SaveImageOperation implements Operation {

public static final OperationDescription DESCRIPTION =
OperationDescription.builder()
.name("Save Images to Disk")
.summary("Save image periodically to local disk")
.category(OperationDescription.Category.MISCELLANEOUS)
.icon(Icon.iconStream("publish-video"))
.build();

private final SocketHint<Mat> inputHint = SocketHints.Inputs.createMatSocketHint("Input", false);
private final SocketHint<Number> qualityHint = SocketHints.Inputs.createNumberSliderSocketHint("Quality", 90, 0, 100);
private final SocketHint<Number> periodHint = SocketHints.Inputs.createNumberSpinnerSocketHint("Period", 0.1);
private final SocketHint<Boolean> activeHint = SocketHints.Inputs.createCheckboxSocketHint("Active", false);

private final SocketHint<Mat> outputHint = SocketHints.Outputs.createMatSocketHint("Output");

private final InputSocket<Mat> inputSocket;
private final InputSocket<Number> qualitySocket;
private final InputSocket<Number> periodSocket;
private final InputSocket<Boolean> activeSocket;

private final OutputSocket<Mat> outputSocket;

private final FileManager fileManager;
private final BytePointer imagePointer = new BytePointer();
private final Stopwatch stopwatch = Stopwatch.createStarted();
private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");

public SaveImageOperation(InputSocket.Factory inputSocketFactory, OutputSocket.Factory outputSocketFactory, FileManager fileManager) {
this.fileManager = fileManager;

inputSocket = inputSocketFactory.create(inputHint);
qualitySocket = inputSocketFactory.create(qualityHint);
periodSocket = inputSocketFactory.create(periodHint);
activeSocket = inputSocketFactory.create(activeHint);

outputSocket = outputSocketFactory.create(outputHint);
}

@Override
public List<InputSocket> getInputSockets() {
return ImmutableList.of(
inputSocket,
qualitySocket,
periodSocket,
activeSocket
);
}

@Override
public List<OutputSocket> getOutputSockets() {
return ImmutableList.of(
outputSocket
);
}

@Override
public void perform() {
if (!activeSocket.getValue().orElse(false)) {
return;
}

// don't save new image until period expires
if (stopwatch.elapsed(TimeUnit.NANOSECONDS) < periodSocket.getValue().get().doubleValue()*1000000000L) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you using nanoseconds? There's no real point if the file name has only millisecond precision.

return;
}
stopwatch.reset();
stopwatch.start();

imencode(".jpeg", inputSocket.getValue().get(), imagePointer, new IntPointer(CV_IMWRITE_JPEG_QUALITY,
qualitySocket.getValue().get().intValue()));
byte[] buffer = new byte[128 * 1024];
int bufferSize = imagePointer.limit();
if (bufferSize > buffer.length) {
buffer = new byte[imagePointer.limit()];
}
imagePointer.get(buffer, 0, bufferSize);

fileManager.saveImage(buffer, LocalDateTime.now().format(formatter) + ".jpeg");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@


import com.google.common.eventbus.EventBus;
import edu.wpi.grip.core.FileManager;
import edu.wpi.grip.core.operations.network.MapNetworkPublisherFactory;
import edu.wpi.grip.core.operations.network.MockMapNetworkPublisher;
import edu.wpi.grip.core.operations.network.ros.JavaToMessageConverter;
Expand All @@ -11,6 +12,7 @@
import edu.wpi.grip.core.sockets.MockInputSocketFactory;
import edu.wpi.grip.core.sockets.MockOutputSocketFactory;
import edu.wpi.grip.core.sockets.OutputSocket;
import edu.wpi.grip.core.util.MockFileManager;

import java.util.Optional;

Expand Down Expand Up @@ -38,17 +40,16 @@ public void close() {
}

public static Operations create(EventBus eventBus) {

return create(eventBus, MockMapNetworkPublisher::new, MockROSMessagePublisher::new,
new MockInputSocketFactory(eventBus), new MockOutputSocketFactory(eventBus));
return create(eventBus, MockMapNetworkPublisher::new, MockROSMessagePublisher::new, new MockFileManager(), new MockInputSocketFactory(eventBus), new MockOutputSocketFactory(eventBus));
}

public static Operations create(EventBus eventBus,
MapNetworkPublisherFactory mapFactory,
ROSNetworkPublisherFactory rosFactory,
FileManager fileManager,
InputSocket.Factory isf,
OutputSocket.Factory osf) {
return new Operations(eventBus, mapFactory, rosFactory, isf, osf);
return new Operations(eventBus, mapFactory, rosFactory, fileManager, isf, osf);
}

public static CVOperations createCV(EventBus eventBus) {
Expand Down
10 changes: 10 additions & 0 deletions core/src/test/java/edu/wpi/grip/core/util/MockFileManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package edu.wpi.grip.core.util;

import edu.wpi.grip.core.FileManager;

public class MockFileManager implements FileManager {
@Override
public void saveImage(byte[] image, String fileName) {
// No body here because this is for testing only.
}
}
3 changes: 3 additions & 0 deletions core/src/test/java/edu/wpi/grip/util/GRIPCoreTestModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@


import com.google.common.eventbus.SubscriberExceptionContext;
import edu.wpi.grip.core.FileManager;
import edu.wpi.grip.core.GRIPCoreModule;
import edu.wpi.grip.core.sources.CameraSource;
import edu.wpi.grip.core.sources.MockFrameGrabberFactory;
import edu.wpi.grip.core.util.MockFileManager;

import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -71,6 +73,7 @@ public void tearDown() {
protected void configure() {
assert setUp : "The GRIPCoreTestModule handler was not set up. Call 'setUp' before passing the injector";
bind(CameraSource.FrameGrabberFactory.class).to(MockFrameGrabberFactory.class);
bind(FileManager.class).to(MockFileManager.class);
super.configure();
}

Expand Down
5 changes: 3 additions & 2 deletions ui/src/main/java/edu/wpi/grip/ui/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import edu.wpi.grip.core.operations.network.GRIPNetworkModule;
import edu.wpi.grip.core.serialization.Project;
import edu.wpi.grip.core.sources.GRIPSourcesHardwareModule;
import edu.wpi.grip.core.GripFileModule;
import edu.wpi.grip.core.util.SafeShutdown;
import edu.wpi.grip.ui.util.DPIUtility;
import javafx.application.Application;
Expand Down Expand Up @@ -64,13 +65,13 @@ public void start(Stage stage) throws Exception {

if (parameters.contains("--headless")) {
// If --headless was specified on the command line, run in headless mode (only use the core module)
injector = Guice.createInjector(new GRIPCoreModule(), new GRIPNetworkModule(), new GRIPSourcesHardwareModule());
injector = Guice.createInjector(new GRIPCoreModule(), new GripFileModule(), new GRIPNetworkModule(), new GRIPSourcesHardwareModule());
injector.injectMembers(this);

parameters.remove("--headless");
} else {
// Otherwise, run with both the core and UI modules, and show the JavaFX stage
injector = Guice.createInjector(Modules.override(new GRIPCoreModule(), new GRIPNetworkModule(), new GRIPSourcesHardwareModule()).with(new GRIPUIModule()));
injector = Guice.createInjector(Modules.override(new GRIPCoreModule(), new GripFileModule(), new GRIPNetworkModule(), new GRIPSourcesHardwareModule()).with(new GRIPUIModule()));
injector.injectMembers(this);

System.setProperty("prism.lcdtext", "false");
Expand Down