2019-09-15 15:03:44 -04:00
|
|
|
package com.chameleonvision.vision.process;
|
2019-09-10 23:47:06 +03:00
|
|
|
|
2019-11-10 11:47:56 -05:00
|
|
|
import com.chameleonvision.classabstraction.pipeline.CVPipeline2d;
|
2019-11-15 16:01:50 -05:00
|
|
|
import com.chameleonvision.classabstraction.pipeline.CVPipeline2dSettings;
|
2019-11-10 11:47:56 -05:00
|
|
|
import com.chameleonvision.classabstraction.pipeline.DriverVisionPipeline;
|
2019-09-21 13:30:09 -04:00
|
|
|
import com.chameleonvision.settings.SettingsManager;
|
|
|
|
|
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-11-10 11:47:56 -05:00
|
|
|
import static com.chameleonvision.classabstraction.pipeline.CVPipeline2d.*;
|
|
|
|
|
|
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;
|
2019-11-10 11:47:56 -05:00
|
|
|
|
|
|
|
|
private CVPipeline2d cvPipeline2d;
|
|
|
|
|
private DriverVisionPipeline driverVisionPipeline;
|
|
|
|
|
|
2019-10-20 10:13:07 +03:00
|
|
|
// 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 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-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-11-10 11:47:56 -05:00
|
|
|
private CVPipeline2dSettings pipelineTo2dSettings(Pipeline pipeline) {
|
|
|
|
|
CVPipeline2dSettings settings = new CVPipeline2dSettings();
|
|
|
|
|
settings.hue = pipeline.hue;
|
|
|
|
|
settings.saturation = pipeline.saturation;
|
|
|
|
|
settings.value = pipeline.value;
|
|
|
|
|
settings.erode = pipeline.erode;
|
|
|
|
|
settings.dilate = pipeline.dilate;
|
|
|
|
|
settings.area = pipeline.area;
|
|
|
|
|
settings.ratio = pipeline.ratio;
|
|
|
|
|
settings.extent = pipeline.extent;
|
|
|
|
|
settings.speckle = pipeline.speckle;
|
|
|
|
|
settings.isBinary = pipeline.isBinary;
|
|
|
|
|
settings.sortMode = pipeline.sortMode;
|
|
|
|
|
settings.targetGroup = pipeline.targetGroup;
|
|
|
|
|
settings.targetIntersection = pipeline.targetIntersection;
|
|
|
|
|
settings.point = pipeline.point;
|
|
|
|
|
settings.calibrationMode = pipeline.calibrationMode;
|
|
|
|
|
settings.nickname = pipeline.nickname;
|
|
|
|
|
settings.exposure = pipeline.exposure;
|
|
|
|
|
settings.brightness = pipeline.brightness;
|
|
|
|
|
settings.dualTargetCalibrationM = pipeline.m;
|
|
|
|
|
settings.dualTargetCalibrationB = pipeline.b;
|
|
|
|
|
|
|
|
|
|
return settings;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-20 10:13:07 +03:00
|
|
|
private PipelineResult runVisionProcess(Mat inputImage, Mat outputImage) {
|
2019-11-10 11:47:56 -05:00
|
|
|
|
|
|
|
|
if (cvPipeline2d == null) {
|
2019-11-23 04:05:37 -05:00
|
|
|
cvPipeline2d = new CVPipeline2d(pipelineTo2dSettings(currentPipeline));
|
2019-11-10 11:47:56 -05:00
|
|
|
}
|
|
|
|
|
CVPipeline2dResult result = cvPipeline2d.runPipeline(inputImage);
|
|
|
|
|
result.outputMat.copyTo(outputImage);
|
|
|
|
|
|
|
|
|
|
PipelineResult pipeResult = new PipelineResult();
|
|
|
|
|
pipeResult.IsValid = result.hasTarget;
|
|
|
|
|
if (!pipeResult.IsValid) {
|
|
|
|
|
pipeResult.CalibratedX = 0;
|
|
|
|
|
pipeResult.CalibratedY = 0;
|
|
|
|
|
pipeResult.Pitch = 0;
|
|
|
|
|
pipeResult.Yaw = 0;
|
|
|
|
|
pipeResult.Area = 0;
|
|
|
|
|
} else {
|
2019-11-23 04:05:37 -05:00
|
|
|
Target2d t = result.targets.get(0);
|
2019-11-10 11:47:56 -05:00
|
|
|
pipeResult.CalibratedX = t.calibratedX;
|
|
|
|
|
pipeResult.CalibratedY = t.calibratedY;
|
|
|
|
|
pipeResult.Pitch = t.pitch;
|
|
|
|
|
pipeResult.Yaw = t.yaw;
|
|
|
|
|
pipeResult.Area = t.area;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pipeResult;
|
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
|
|
|
}
|