2019-09-15 15:03:44 -04:00
|
|
|
package com.chameleonvision.vision.process;
|
2019-09-10 23:47:06 +03:00
|
|
|
|
2019-09-21 13:30:09 -04:00
|
|
|
import com.chameleonvision.settings.SettingsManager;
|
2019-10-12 03:38:42 +03:00
|
|
|
import com.chameleonvision.vision.Orientation;
|
2019-09-21 13:30:09 -04:00
|
|
|
import com.chameleonvision.vision.Pipeline;
|
|
|
|
|
import com.chameleonvision.web.ServerHandler;
|
2019-09-25 10:32:22 -07:00
|
|
|
import edu.wpi.cscore.VideoException;
|
2019-09-21 13:30:09 -04:00
|
|
|
import edu.wpi.first.networktables.*;
|
2019-09-14 17:14:49 +03:00
|
|
|
import org.opencv.core.*;
|
|
|
|
|
|
2019-09-21 13:30:09 -04:00
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.List;
|
2019-09-14 17:14:49 +03:00
|
|
|
|
2019-09-21 13:30:09 -04:00
|
|
|
public class VisionProcess implements Runnable {
|
2019-09-15 19:07:58 -04:00
|
|
|
|
2019-10-20 10:13:07 +03:00
|
|
|
private final String cameraName;
|
2019-11-02 03:34:51 -04:00
|
|
|
public final CameraProcess cameraProcess;
|
2019-10-20 10:13:07 +03:00
|
|
|
// NetworkTables
|
|
|
|
|
public NetworkTableEntry ntPipelineEntry;
|
|
|
|
|
public NetworkTableEntry ntDriverModeEntry;
|
2019-10-29 15:10:43 +02:00
|
|
|
private int ntDriveModeListenerID;
|
|
|
|
|
private int ntPipelineListenerID;
|
2019-10-20 10:13:07 +03:00
|
|
|
private NetworkTableEntry ntYawEntry;
|
|
|
|
|
private NetworkTableEntry ntPitchEntry;
|
|
|
|
|
private NetworkTableEntry ntDistanceEntry;
|
|
|
|
|
private NetworkTableEntry ntTimeStampEntry;
|
|
|
|
|
private NetworkTableEntry ntValidEntry;
|
|
|
|
|
// chameleon specific
|
|
|
|
|
private Pipeline currentPipeline;
|
|
|
|
|
private CVProcess cvProcess;
|
|
|
|
|
// pipeline process items
|
2019-11-02 13:03:00 -07:00
|
|
|
// private List<MatOfPoint> foundContours = new ArrayList<>();
|
|
|
|
|
// private List<MatOfPoint> filteredContours = new ArrayList<>();
|
|
|
|
|
// private List<MatOfPoint> deSpeckledContours = new ArrayList<>();
|
|
|
|
|
// private List<RotatedRect> groupedContours = new ArrayList<>();
|
2019-10-20 10:13:07 +03:00
|
|
|
private Mat cameraInputMat = new Mat();
|
|
|
|
|
private Mat hsvThreshMat = new Mat();
|
|
|
|
|
private Mat streamOutputMat = new Mat();
|
|
|
|
|
private long timeStamp = 0;
|
2019-09-22 02:49:30 -04:00
|
|
|
|
2019-11-02 10:10:02 -07:00
|
|
|
public VisionProcess(CameraProcess cameraProcess) {
|
2019-09-22 02:49:30 -04:00
|
|
|
|
2019-11-02 10:10:02 -07:00
|
|
|
// USBCamera settings
|
2019-11-02 13:03:00 -07:00
|
|
|
cvProcess = new StandardCVProcess(cameraProcess.getCamVals());
|
2019-11-02 10:10:02 -07:00
|
|
|
this.cameraProcess = cameraProcess; // new USBCameraProcess(cameraProcess);
|
2019-09-22 02:49:30 -04:00
|
|
|
|
2019-11-02 10:10:02 -07:00
|
|
|
this.cameraName = cameraProcess.getCamName();
|
|
|
|
|
|
|
|
|
|
initNT(NetworkTableInstance.getDefault().getTable("/chameleon-vision/" + cameraProcess.getNickname()));
|
2019-10-20 10:13:07 +03:00
|
|
|
}
|
2019-09-22 02:49:30 -04:00
|
|
|
|
2019-10-20 10:13:07 +03:00
|
|
|
private void driverModeListener(EntryNotification entryNotification) {
|
2019-11-02 10:10:02 -07:00
|
|
|
cameraProcess.setDriverMode(entryNotification.value.getBoolean());
|
2019-10-20 10:13:07 +03:00
|
|
|
}
|
2019-09-22 02:49:30 -04:00
|
|
|
|
2019-10-20 10:13:07 +03:00
|
|
|
private void pipelineListener(EntryNotification entryNotification) {
|
|
|
|
|
var ntPipelineIndex = (int) entryNotification.value.getDouble();
|
2019-11-02 10:10:02 -07:00
|
|
|
if (ntPipelineIndex >= cameraProcess.getPipelines().size()) {
|
|
|
|
|
ntPipelineEntry.setNumber(cameraProcess.getCurrentPipelineIndex());
|
2019-10-29 15:10:43 +02:00
|
|
|
} else {
|
2019-11-02 10:10:02 -07:00
|
|
|
var pipeline = cameraProcess.getCurrentPipeline();
|
|
|
|
|
cameraProcess.setCurrentPipelineIndex(ntPipelineIndex);
|
2019-10-20 10:13:07 +03:00
|
|
|
try {
|
2019-11-02 10:10:02 -07:00
|
|
|
cameraProcess.setExposure(pipeline.exposure);
|
2019-10-20 10:13:07 +03:00
|
|
|
} catch (VideoException e) {
|
|
|
|
|
System.err.println(e.toString());
|
|
|
|
|
}
|
2019-11-02 10:10:02 -07:00
|
|
|
cameraProcess.setBrightness(pipeline.brightness);
|
|
|
|
|
if (SettingsManager.generalSettings.currentCamera.equals(cameraName)) {
|
|
|
|
|
SettingsManager.generalSettings.currentPipeline = ntPipelineIndex;
|
2019-10-20 10:13:07 +03:00
|
|
|
HashMap<String, Object> pipeChange = new HashMap<>();
|
|
|
|
|
pipeChange.put("currentPipeline", ntPipelineIndex);
|
|
|
|
|
ServerHandler.broadcastMessage(pipeChange);
|
|
|
|
|
ServerHandler.sendFullSettings();
|
2019-10-15 21:00:18 +03:00
|
|
|
|
2019-10-20 10:13:07 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-09-22 02:49:30 -04:00
|
|
|
|
2019-10-20 10:13:07 +03:00
|
|
|
private void updateNetworkTables(PipelineResult pipelineResult) {
|
|
|
|
|
if (pipelineResult.IsValid) {
|
|
|
|
|
ntValidEntry.setBoolean(true);
|
|
|
|
|
ntYawEntry.setNumber(pipelineResult.Yaw);
|
|
|
|
|
ntPitchEntry.setNumber(pipelineResult.Pitch);
|
|
|
|
|
ntDistanceEntry.setNumber(pipelineResult.Area);
|
|
|
|
|
ntTimeStampEntry.setNumber(timeStamp);
|
|
|
|
|
NetworkTableInstance.getDefault().flush();
|
|
|
|
|
} else {
|
|
|
|
|
ntYawEntry.setNumber(0.0);
|
|
|
|
|
ntPitchEntry.setNumber(0.0);
|
|
|
|
|
ntDistanceEntry.setNumber(0.0);
|
|
|
|
|
ntTimeStampEntry.setNumber(timeStamp);
|
|
|
|
|
ntValidEntry.setBoolean(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-09-22 02:49:30 -04:00
|
|
|
|
2019-10-20 10:13:07 +03:00
|
|
|
private PipelineResult runVisionProcess(Mat inputImage, Mat outputImage) {
|
2019-11-02 12:46:36 -07:00
|
|
|
return cvProcess.runPipeline(
|
2019-11-02 12:38:42 -07:00
|
|
|
currentPipeline,
|
|
|
|
|
inputImage,
|
|
|
|
|
outputImage,
|
|
|
|
|
cameraProcess.getCamVals(),
|
|
|
|
|
currentPipeline.orientation.equals(Orientation.Inverted),
|
|
|
|
|
cameraProcess.getDriverMode()
|
|
|
|
|
);
|
2019-10-20 10:13:07 +03:00
|
|
|
}
|
2019-09-22 02:49:30 -04:00
|
|
|
|
2019-10-20 10:13:07 +03:00
|
|
|
@Override
|
|
|
|
|
public void run() {
|
|
|
|
|
// processing time tracking
|
|
|
|
|
long startTime;
|
|
|
|
|
long fpsLastTime = 0;
|
|
|
|
|
double processTimeMs;
|
|
|
|
|
double fps = 0;
|
|
|
|
|
double uiFps = 0;
|
2019-11-02 10:10:02 -07:00
|
|
|
int maxFps = cameraProcess.getVideoMode().fps;
|
2019-09-22 02:49:30 -04:00
|
|
|
|
2019-10-20 10:13:07 +03:00
|
|
|
new Thread(cameraProcess).start();
|
2019-09-22 02:49:30 -04:00
|
|
|
|
2019-10-20 10:13:07 +03:00
|
|
|
long lastFrameEndNanosec = 0;
|
2019-09-22 02:49:30 -04:00
|
|
|
|
2019-10-20 10:13:07 +03:00
|
|
|
while (!Thread.interrupted()) {
|
|
|
|
|
startTime = System.nanoTime();
|
2019-11-02 10:10:02 -07:00
|
|
|
if ((startTime - lastFrameEndNanosec) * 1e-6 >= 1000.0 / (maxFps + 3)) { // 3 additional fps to allow for overhead
|
2019-11-02 13:03:00 -07:00
|
|
|
// foundContours.clear();
|
|
|
|
|
// filteredContours.clear();
|
|
|
|
|
// groupedContours.clear();
|
|
|
|
|
// deSpeckledContours.clear();
|
2019-09-22 02:49:30 -04:00
|
|
|
|
2019-10-20 10:13:07 +03:00
|
|
|
// update FPS for ui only every 0.5 seconds
|
|
|
|
|
if ((startTime - fpsLastTime) * 1e-6 >= 500) {
|
|
|
|
|
if (fps >= maxFps) {
|
|
|
|
|
uiFps = maxFps;
|
|
|
|
|
} else {
|
|
|
|
|
uiFps = fps;
|
|
|
|
|
}
|
|
|
|
|
fpsLastTime = System.nanoTime();
|
|
|
|
|
}
|
2019-09-22 02:49:30 -04:00
|
|
|
|
2019-11-02 10:10:02 -07:00
|
|
|
currentPipeline = cameraProcess.getCurrentPipeline();
|
2019-10-20 10:13:07 +03:00
|
|
|
// start fps counter right before grabbing input frame
|
|
|
|
|
timeStamp = cameraProcess.getLatestFrame(cameraInputMat);
|
|
|
|
|
if (cameraInputMat.cols() == 0 && cameraInputMat.rows() == 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2019-09-22 02:49:30 -04:00
|
|
|
|
2019-10-20 10:13:07 +03:00
|
|
|
// get vision data
|
|
|
|
|
var pipelineResult = runVisionProcess(cameraInputMat, streamOutputMat);
|
|
|
|
|
updateNetworkTables(pipelineResult);
|
2019-11-02 10:10:02 -07:00
|
|
|
if (cameraName.equals(SettingsManager.generalSettings.currentCamera)) {
|
2019-10-20 10:13:07 +03:00
|
|
|
HashMap<String, Object> WebSend = new HashMap<>();
|
|
|
|
|
HashMap<String, Object> point = new HashMap<>();
|
|
|
|
|
HashMap<String, Object> calculated = new HashMap<>();
|
|
|
|
|
List<Double> center = new ArrayList<>();
|
|
|
|
|
if (pipelineResult.IsValid) {
|
|
|
|
|
center.add(pipelineResult.RawPoint.center.x);
|
|
|
|
|
center.add(pipelineResult.RawPoint.center.y);
|
|
|
|
|
calculated.put("pitch", pipelineResult.Pitch);
|
|
|
|
|
calculated.put("yaw", pipelineResult.Yaw);
|
|
|
|
|
} else {
|
|
|
|
|
center.add(0.0);
|
|
|
|
|
center.add(0.0);
|
|
|
|
|
calculated.put("pitch", 0);
|
|
|
|
|
calculated.put("yaw", 0);
|
|
|
|
|
}
|
|
|
|
|
point.put("fps", uiFps);
|
|
|
|
|
point.put("calculated", calculated);
|
|
|
|
|
point.put("rawPoint", center);
|
|
|
|
|
WebSend.put("point", point);
|
2019-11-01 18:02:48 +02:00
|
|
|
ServerHandler.broadcastMessage(WebSend);
|
2019-10-20 10:13:07 +03:00
|
|
|
}
|
2019-09-22 02:49:30 -04:00
|
|
|
|
2019-10-20 10:13:07 +03:00
|
|
|
cameraProcess.updateFrame(streamOutputMat);
|
2019-09-22 02:49:30 -04:00
|
|
|
|
2019-10-20 10:13:07 +03:00
|
|
|
cameraInputMat.release();
|
|
|
|
|
hsvThreshMat.release();
|
2019-09-22 02:49:30 -04:00
|
|
|
|
2019-10-20 10:13:07 +03:00
|
|
|
// calculate FPS
|
|
|
|
|
lastFrameEndNanosec = System.nanoTime();
|
|
|
|
|
processTimeMs = (lastFrameEndNanosec - startTime) * 1e-6;
|
|
|
|
|
fps = 1000 / processTimeMs;
|
|
|
|
|
//please dont enable if you are not debugging
|
|
|
|
|
// System.out.printf("%s - Process time: %-5.2fms, FPS: %-5.2f, FoundContours: %d, FilteredContours: %d, GroupedContours: %d\n", cameraName, processTimeMs, fps, FoundContours.size(), FilteredContours.size(), GroupedContours.size());
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-10-29 15:10:43 +02:00
|
|
|
|
2019-11-01 11:38:58 +02:00
|
|
|
}
|
|
|
|
|
|
2019-10-29 15:10:43 +02:00
|
|
|
/**
|
2019-11-01 11:38:58 +02:00
|
|
|
* Removes the old value change listeners
|
|
|
|
|
* calls {@link #initNT}
|
2019-10-29 15:10:43 +02:00
|
|
|
*
|
2019-11-01 11:38:58 +02:00
|
|
|
* @param newTable passed to {@link #initNT}
|
2019-10-29 15:10:43 +02:00
|
|
|
*/
|
2019-11-01 11:38:58 +02:00
|
|
|
public void resetNT(NetworkTable newTable) {
|
2019-10-29 15:10:43 +02:00
|
|
|
ntDriverModeEntry.removeListener(ntDriveModeListenerID);
|
|
|
|
|
ntPipelineEntry.removeListener(ntPipelineListenerID);
|
|
|
|
|
initNT(newTable);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2019-11-01 11:38:58 +02:00
|
|
|
* Rebases the writing location for the vision process - pipeline output
|
|
|
|
|
*
|
2019-10-29 15:10:43 +02:00
|
|
|
* @param newTable the new writing location
|
|
|
|
|
*/
|
2019-11-01 11:38:58 +02:00
|
|
|
private void initNT(NetworkTable newTable) {
|
2019-10-29 15:10:43 +02:00
|
|
|
ntPipelineEntry = newTable.getEntry("pipeline");
|
|
|
|
|
ntDriverModeEntry = newTable.getEntry("driver_mode");
|
|
|
|
|
ntPitchEntry = newTable.getEntry("pitch");
|
|
|
|
|
ntYawEntry = newTable.getEntry("yaw");
|
|
|
|
|
ntDistanceEntry = newTable.getEntry("distance");
|
|
|
|
|
ntTimeStampEntry = newTable.getEntry("timestamp");
|
|
|
|
|
ntValidEntry = newTable.getEntry("is_valid");
|
|
|
|
|
ntDriveModeListenerID = ntDriverModeEntry.addListener(this::driverModeListener, EntryListenerFlags.kUpdate);
|
|
|
|
|
ntPipelineListenerID = ntPipelineEntry.addListener(this::pipelineListener, EntryListenerFlags.kUpdate);
|
|
|
|
|
ntDriverModeEntry.setBoolean(false);
|
2019-11-02 10:10:02 -07:00
|
|
|
ntPipelineEntry.setNumber(cameraProcess.getCurrentPipelineIndex());
|
2019-10-20 10:13:07 +03:00
|
|
|
}
|
2019-09-10 23:47:06 +03:00
|
|
|
}
|