Files
PhotonVision/Main/src/main/java/com/chameleonvision/vision/VisionProcess.java

294 lines
11 KiB
Java
Raw Normal View History

package com.chameleonvision.vision;
import com.chameleonvision.Debug;
import com.chameleonvision.config.ConfigManager;
import com.chameleonvision.util.LoopingRunnable;
import com.chameleonvision.vision.camera.CameraCapture;
import com.chameleonvision.vision.camera.CameraStreamer;
import com.chameleonvision.vision.pipeline.*;
import com.chameleonvision.web.SocketHandler;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.wpi.cscore.VideoMode;
2019-11-23 20:13:01 -08:00
import edu.wpi.first.networktables.*;
import edu.wpi.first.wpiutil.CircularBuffer;
import org.apache.commons.lang3.tuple.Pair;
import org.opencv.core.Mat;
import java.util.ArrayList;
2019-11-22 14:20:23 -08:00
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
2019-11-26 22:18:29 -08:00
public class VisionProcess {
private final CameraCapture cameraCapture;
private final CameraStreamerRunnable streamRunnable;
private final VisionProcessRunnable visionRunnable;
2019-11-23 04:05:37 -05:00
public final CameraStreamer cameraStreamer;
2019-11-29 21:41:12 -08:00
public PipelineManager pipelineManager;
2019-11-21 21:49:25 -08:00
private volatile CVPipelineResult lastPipelineResult;
2019-11-26 23:03:07 -05:00
private BlockingQueue<Mat> streamFrameQueue = new LinkedBlockingDeque<>(1);
2019-11-22 14:20:23 -08:00
// network table stuff
private final NetworkTable defaultTable;
2019-11-23 04:05:37 -05:00
private NetworkTableEntry ntPipelineEntry;
private NetworkTableEntry ntDriverModeEntry;
2019-11-22 14:20:23 -08:00
private int ntDriveModeListenerID;
private int ntPipelineListenerID;
private NetworkTableEntry ntYawEntry;
private NetworkTableEntry ntPitchEntry;
private NetworkTableEntry ntAuxListEntry;
2019-11-23 04:05:37 -05:00
private NetworkTableEntry ntAreaEntry;
2019-11-22 14:20:23 -08:00
private NetworkTableEntry ntTimeStampEntry;
private NetworkTableEntry ntValidEntry;
private ObjectMapper objectMapper = new ObjectMapper();
2019-11-22 14:20:23 -08:00
VisionProcess(CameraCapture cameraCapture, String name, List<CVPipelineSettings> loadedPipelineSettings) {
this.cameraCapture = cameraCapture;
2019-11-29 23:13:57 -05:00
pipelineManager = new PipelineManager(this, loadedPipelineSettings);
// Thread to put frames on the dashboard
this.cameraStreamer = new CameraStreamer(cameraCapture, name);
this.streamRunnable = new CameraStreamerRunnable(30, cameraStreamer);
// Thread to process vision data
this.visionRunnable = new VisionProcessRunnable();
2019-11-23 20:13:01 -08:00
// network table
2019-11-30 23:31:09 +02:00
defaultTable = NetworkTableInstance.getDefault().getTable("/chameleon-vision/" + cameraCapture.getProperties().getNickname());
}
public void start() {
System.out.println("Starting NetworkTables.");
2019-11-23 20:13:01 -08:00
initNT(defaultTable);
2019-11-29 23:13:57 -05:00
System.out.println("Starting vision thread.");
2019-11-29 23:13:57 -05:00
var visionThread = new Thread(visionRunnable);
visionThread.setName(getCamera().getProperties().name + " - Vision Thread");
visionThread.start();
System.out.println("Starting stream thread.");
2019-11-29 23:13:57 -05:00
var streamThread = new Thread(streamRunnable);
streamThread.setName(getCamera().getProperties().name + " - Stream Thread");
streamThread.start();
}
2019-11-22 14:20:23 -08:00
/**
* Removes the old value change listeners
* calls {@link #initNT}
*
* @param newTable passed to {@link #initNT}
*/
public void resetNT(NetworkTable newTable) {
ntDriverModeEntry.removeListener(ntDriveModeListenerID);
ntPipelineEntry.removeListener(ntPipelineListenerID);
initNT(newTable);
}
2019-11-30 11:02:10 -08:00
public void setCameraName(String newName) {
var newTable = NetworkTableInstance.getDefault().getTable("/chameleon-vision/" + newName);
resetNT(newTable);
pipelineManager.ntIndexEntry = ntPipelineEntry;
}
2019-11-22 14:20:23 -08:00
private void initNT(NetworkTable newTable) {
ntPipelineEntry = newTable.getEntry("pipeline");
ntDriverModeEntry = newTable.getEntry("driver_mode");
ntPitchEntry = newTable.getEntry("pitch");
ntYawEntry = newTable.getEntry("yaw");
2019-11-23 04:05:37 -05:00
ntAreaEntry = newTable.getEntry("area");
2019-11-22 14:20:23 -08:00
ntTimeStampEntry = newTable.getEntry("timestamp");
ntValidEntry = newTable.getEntry("is_valid");
2019-11-24 20:57:48 -08:00
ntAuxListEntry = newTable.getEntry("aux_targets");
2019-11-22 14:20:23 -08:00
ntDriveModeListenerID = ntDriverModeEntry.addListener(this::setDriverMode, EntryListenerFlags.kUpdate);
ntPipelineListenerID = ntPipelineEntry.addListener(this::setPipeline, EntryListenerFlags.kUpdate);
ntDriverModeEntry.setBoolean(false);
2019-11-29 23:13:57 -05:00
ntPipelineEntry.setNumber(pipelineManager.getCurrentPipelineIndex());
2019-11-29 21:41:12 -08:00
pipelineManager.ntIndexEntry = ntPipelineEntry;
2019-11-22 14:20:23 -08:00
}
2019-11-23 04:05:37 -05:00
private void setDriverMode(EntryNotification driverModeEntryNotification) {
setDriverMode(driverModeEntryNotification.value.getBoolean());
}
public void setDriverMode(boolean driverMode) {
2019-11-29 23:13:57 -05:00
pipelineManager.setDriverMode(driverMode);
2019-11-22 14:20:23 -08:00
}
/**
* Method called by the nt entry listener to update the next pipeline.
* @param notification the notification
*/
private void setPipeline(EntryNotification notification) {
var wantedPipelineIndex = (int) notification.value.getDouble();
2019-11-29 21:41:12 -08:00
pipelineManager.setCurrentPipeline(wantedPipelineIndex);
2019-11-22 14:20:23 -08:00
}
private void updateUI(CVPipelineResult data) {
if(cameraCapture.getProperties().name.equals(ConfigManager.settings.currentCamera)) {
2019-11-22 14:20:23 -08: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 (data.hasTarget) {
if(data instanceof CVPipeline2d.CVPipeline2dResult) {
CVPipeline2d.CVPipeline2dResult result = (CVPipeline2d.CVPipeline2dResult) data;
2019-11-23 04:05:37 -05:00
CVPipeline2d.Target2d bestTarget = result.targets.get(0);
2019-11-22 14:20:23 -08:00
center.add(bestTarget.rawPoint.center.x);
center.add(bestTarget.rawPoint.center.y);
calculated.put("pitch", bestTarget.pitch);
calculated.put("yaw", bestTarget.yaw);
calculated.put("area", bestTarget.area);
2019-11-23 04:05:37 -05:00
} else if (data instanceof CVPipeline3d.CVPipeline3dResult) {
// TODO: (2.1) 3d stuff in UI
2019-11-22 14:20:23 -08:00
} else {
center.add(0.0);
center.add(0.0);
calculated.put("pitch", 0);
calculated.put("yaw", 0);
}
} else {
center.add(0.0);
center.add(0.0);
calculated.put("pitch", 0);
calculated.put("yaw", 0);
}
point.put("fps", visionRunnable.fps);
point.put("calculated", calculated);
point.put("rawPoint", center);
WebSend.put("point", point);
SocketHandler.broadcastMessage(WebSend);
2019-11-22 14:20:23 -08:00
}
}
private void updateNetworkTableData(CVPipelineResult data) {
ntValidEntry.setBoolean(data.hasTarget);
if(data.hasTarget && !(data instanceof DriverVisionPipeline.DriverPipelineResult)) {
if(data instanceof CVPipeline2d.CVPipeline2dResult) {
//noinspection unchecked
2019-11-23 04:05:37 -05:00
List<CVPipeline2d.Target2d> targets = (List<CVPipeline2d.Target2d>) data.targets;
ntTimeStampEntry.setDouble(data.imageTimestamp);
2019-11-22 14:20:23 -08:00
ntPitchEntry.setDouble(targets.get(0).pitch);
ntYawEntry.setDouble(targets.get(0).yaw);
2019-11-23 04:05:37 -05:00
ntAreaEntry.setDouble(targets.get(0).area);
try {
2019-11-28 08:31:59 -08:00
ntAuxListEntry.setString(objectMapper.writeValueAsString(targets));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
2019-11-23 04:05:37 -05:00
} else if (data instanceof CVPipeline3d.CVPipeline3dResult) {
// TODO: (2.1) 3d stuff...
2019-11-22 14:20:23 -08:00
}
} else {
ntPitchEntry.setDouble(0.0);
ntYawEntry.setDouble(0.0);
2019-11-23 04:05:37 -05:00
ntAreaEntry.setDouble(0.0);
2019-11-22 14:20:23 -08:00
ntTimeStampEntry.setDouble(0.0);
ntAuxListEntry.setString("");
2019-11-22 14:20:23 -08:00
}
}
public void setVideoMode(VideoMode newMode) {
cameraCapture.setVideoMode(newMode);
cameraStreamer.setNewVideoMode(newMode);
}
2019-11-27 21:11:05 -08:00
public VideoMode getCurrentVideoMode() {
return cameraCapture.getCurrentVideoMode();
}
public List<VideoMode> getPossibleVideoModes() {
return cameraCapture.getProperties().videoModes;
}
public CameraCapture getCamera() {
return cameraCapture;
2019-11-23 04:05:37 -05:00
}
public CVPipelineSettings getDriverModeSettings() {
2019-11-29 23:13:57 -05:00
return pipelineManager.driverModePipeline.settings;
2019-11-23 04:05:37 -05:00
}
/**
* VisionProcessRunnable will process images as quickly as possible
*/
private class VisionProcessRunnable implements Runnable {
2019-11-23 08:23:49 -08:00
volatile Double fps = 0.0;
private CircularBuffer fpsAveragingBuffer = new CircularBuffer(7);
@Override
public void run() {
var lastUpdateTimeNanos = System.nanoTime();
while(!Thread.interrupted()) {
2019-11-21 22:04:17 -08:00
// blocking call, will block until camera has a new frame.
Pair<Mat, Long> camData = cameraCapture.getFrame();
2019-11-21 22:04:17 -08:00
Mat camFrame = camData.getLeft();
if (camFrame.cols() > 0 && camFrame.rows() > 0) {
2019-11-29 23:13:57 -05:00
CVPipelineResult result = pipelineManager.getCurrentPipeline().runPipeline(camFrame);
2019-11-22 14:20:23 -08:00
if (result != null) {
result.setTimestamp(camData.getRight());
lastPipelineResult = result;
updateNetworkTableData(lastPipelineResult);
updateUI(lastPipelineResult);
}
}
try {
streamFrameQueue.clear();
streamFrameQueue.add(lastPipelineResult.outputMat);
} catch (Exception e) {
Debug.printInfo("Vision running faster than stream.");
}
2019-11-27 14:59:26 -08:00
var deltaTimeNanos = System.nanoTime() - lastUpdateTimeNanos;
fpsAveragingBuffer.addFirst(1.0 / (deltaTimeNanos * 1E-09));
lastUpdateTimeNanos = System.nanoTime();
fps = getAverageFPS();
}
}
double getAverageFPS() {
var temp = 0.0;
for(int i = 0; i < 7; i++) {
temp += fpsAveragingBuffer.get(i);
}
temp /= 7.0;
return temp;
}
}
2019-11-26 23:03:07 -05:00
private class CameraStreamerRunnable extends LoopingRunnable {
final CameraStreamer streamer;
private CameraStreamerRunnable(int cameraFPS, CameraStreamer streamer) {
// add 2 FPS to allow for a bit of overhead
super(1000L/(cameraFPS + 2));
this.streamer = streamer;
}
@Override
protected void process() {
if (!streamFrameQueue.isEmpty()) {
try {
streamer.runStream(streamFrameQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
2019-11-26 23:03:07 -05:00
}
}
}
}
}