mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-22 01:11:40 +00:00
Add solvePNP, 3d tab on the UI, and some other misc bug fixes (#35)
* Rebase solvePNP on master * added 3D tab minimap and csv reader * More solvePNP * Create draw pipe for pnp data * SolvePNP piping work * Move sorting into solvepnppipe * Create calibration pipeline * Update CalibrateSolvePNPPipeline.java * add camera tilt angle * Add calibration slider and snapshot button to 3D view * Mirror updates in the socket handler * add 3d calibration mode to the pipeline manager * created calibration functions in ui and backend * Start plumbing calibration * Add snapshot and other handling to the RequestHandler * added select resolution before starting calibration * Rename solvePNPPipe to bounding box solve pnp pipe * Update BoundingBoxSolvePNPPipe.java * Add Mat serializer and CameraCalibrationConfig * Begun calibration saving, fixed UI/Backend snapshot count mismatch * Add (unplumbed) option to set checkerboard size This will allow users to change the units their calibration is in * Create chessboard.png * Fix calibration NPE * changed string serialization to a json send * bug fixed cancellation button * Fix spelling of snapshot in 3d.vue * Plumb resolution change * Set resolution during config, start on config serialization * Update .gitignore * Config fixes * Start transition away from cvpipeline3d * fix NPE on uncalibrated cameras * clear list on fail * Fix video mode index error * ignore getters in camera calibration config * Create json constructor for jsonmat * get solvePNP mostly returning sane values * Fix solvePNP bug and add unit test * FIx calibration mat truncation * added capture amount model upload and minimap data * Standardize on meters in calibration and bounding box * fix json out of bounds and handle null calibration more gracefully * don't put text on calibrate image, go back to inches * convert distance to meters this means calibration will need to be in inches * Actually save raw contor * Update GroupContoursPipe.java * Add all calibration return to camera capture * hard code 2019 target * bugfixed draw2d added fail calib popup, merge end and cancel added the res index to the calib start * Clarify error message and draw more fancy rectangles * Cleanup memory in solvepnp * re did minimap component * fix npe if left/right is null * remove references to 2d * try-catch running the current pipeline * Add method to find corners using the harris corner detector * Possibly fix left/right missmatch * Fix 3D Tab error * FIx file permissions, mat serializer adjustments * fixed mini map for field coordinates * mini map changes fov * Update SolvePNPPipe.java * get rid of target corners * some memory leak fixes * fixed mini map location * added position under minimap * changed player fov look * put all targets in the web send * re did target send to ui added target tables, bugfix calibration * fixed y position * Add tilt angle to capture properties * maybe fix y axis in minimap * Add square size to onCalibrationEnding * Possibly add square size to UI * fix NPE with pitch * Fix bug with sending multiple targets * Only instantiate 3d stuff if we are in 3d mode * Fix array list exceptions * Fix bug in sort contors list was truncated too early * added download chess, tilt setting and ordinal tilt, * added square size connection * removed unused code * Update pom version to 2.1-RELEASE * Send camera calibrations to UI * Stream pose list to a LIst * Only stream necessary parts of the aux list entry * Make broadcastMessage synchronized to prevent ConcurrentModificationExceptions * added fps counter changed squaresize steps bug fixes in tables * bugfix camera settings cam wont change Authored-by: oriagranat9 <oriagranat9@gmail.com>
This commit is contained in:
@@ -4,12 +4,20 @@ import com.chameleonvision.config.ConfigManager;
|
||||
import com.chameleonvision.network.NetworkIPMode;
|
||||
import com.chameleonvision.vision.VisionManager;
|
||||
import com.chameleonvision.vision.VisionProcess;
|
||||
import com.chameleonvision.vision.camera.CameraCapture;
|
||||
import com.chameleonvision.vision.camera.USBCameraCapture;
|
||||
import com.chameleonvision.vision.pipeline.CVPipelineSettings;
|
||||
import com.chameleonvision.vision.pipeline.PipelineManager;
|
||||
import com.chameleonvision.vision.pipeline.impl.Calibrate3dPipeline;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import edu.wpi.cscore.VideoMode;
|
||||
import edu.wpi.first.wpilibj.geometry.Rotation2d;
|
||||
import io.javalin.http.Context;
|
||||
import io.javalin.http.Handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class RequestHandler {
|
||||
@@ -35,6 +43,43 @@ public class RequestHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public static void onDuplicatePipeline(Context ctx) {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
try {
|
||||
Map newPipelineData = objectMapper.readValue(ctx.body(), Map.class);
|
||||
|
||||
int newCam = -1;
|
||||
try {
|
||||
newCam = (Integer) newPipelineData.get("camera");
|
||||
} catch (Exception e) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
var pipeline = (CVPipelineSettings) newPipelineData.get("pipeline");
|
||||
|
||||
if (newCam == -1) {
|
||||
if (VisionManager.getCurrentCameraPipelineNicknames().contains(pipeline.nickname)) {
|
||||
ctx.status(400); // BAD REQUEST
|
||||
} else {
|
||||
VisionManager.getCurrentUIVisionProcess().pipelineManager.addPipeline(pipeline);
|
||||
ctx.status(200);
|
||||
}
|
||||
} else {
|
||||
var cam = VisionManager.getVisionProcessByIndex(newCam);
|
||||
if (cam != null && cam.pipelineManager.pipelines.stream().anyMatch(c -> c.settings.nickname.equals(pipeline.nickname))) {
|
||||
ctx.status(400); // BAD REQUEST
|
||||
} else {
|
||||
cam.pipelineManager.addPipeline(pipeline);
|
||||
ctx.status(200);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (JsonProcessingException e) {
|
||||
ctx.status(500);
|
||||
}
|
||||
}
|
||||
|
||||
public static void onCameraSettings(Context ctx) {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
try {
|
||||
@@ -43,13 +88,19 @@ public class RequestHandler {
|
||||
VisionProcess currentVisionProcess = VisionManager.getCurrentUIVisionProcess();
|
||||
USBCameraCapture currentCamera = currentVisionProcess.getCamera();
|
||||
|
||||
double newFOV;
|
||||
double newFOV, tilt;
|
||||
try {
|
||||
newFOV = (Double) camSettings.get("fov");
|
||||
} catch (Exception ignored) {
|
||||
newFOV = (Integer) camSettings.get("fov");
|
||||
}
|
||||
try {
|
||||
tilt = (Double) camSettings.get("tilt");
|
||||
} catch (Exception ignored) {
|
||||
tilt = (Integer) camSettings.get("tilt");
|
||||
}
|
||||
currentCamera.getProperties().setFOV(newFOV);
|
||||
currentCamera.getProperties().setTilt(Rotation2d.fromDegrees(tilt));
|
||||
VisionManager.saveCurrentCameraSettings();
|
||||
SocketHandler.sendFullSettings();
|
||||
ctx.status(200);
|
||||
@@ -58,4 +109,57 @@ public class RequestHandler {
|
||||
ctx.status(500);
|
||||
}
|
||||
}
|
||||
|
||||
public static void onCalibrationStart(Context ctx) throws JsonProcessingException {
|
||||
PipelineManager pipeManager = VisionManager.getCurrentUIVisionProcess().pipelineManager;
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
var data = objectMapper.readValue(ctx.body(), Map.class);
|
||||
int resolutionIndex = (Integer) data.get("resolution");
|
||||
double squareSize;
|
||||
try {
|
||||
squareSize = (Double) data.get("squareSize");
|
||||
} catch (Exception e) {
|
||||
squareSize = (Integer) data.get("squareSize");
|
||||
}
|
||||
// convert from mm to meters
|
||||
pipeManager.calib3dPipe.setSquareSize(squareSize / 1000d);
|
||||
VisionManager.getCurrentUIVisionProcess().pipelineManager.calib3dPipe.settings.videoModeIndex = resolutionIndex;
|
||||
VisionManager.getCurrentUIVisionProcess().pipelineManager.setCalibrationMode(true);
|
||||
VisionManager.getCurrentUIVisionProcess().getCamera().setVideoMode(resolutionIndex);
|
||||
}
|
||||
|
||||
public static void onSnapshot(Context ctx) {
|
||||
Calibrate3dPipeline calPipe = VisionManager.getCurrentUIVisionProcess().pipelineManager.calib3dPipe;
|
||||
|
||||
calPipe.takeSnapshot();
|
||||
|
||||
HashMap<String, Object> toSend = new HashMap<>();
|
||||
toSend.put("snapshotCount", calPipe.getSnapshotCount());
|
||||
toSend.put("hasEnough", calPipe.hasEnoughSnapshots());
|
||||
|
||||
ctx.json(toSend);
|
||||
ctx.status(200);
|
||||
}
|
||||
|
||||
public static void onCalibrationEnding(Context ctx) throws JsonProcessingException {
|
||||
PipelineManager pipeManager = VisionManager.getCurrentUIVisionProcess().pipelineManager;
|
||||
System.out.println("Finishing Cal");
|
||||
if (pipeManager.calib3dPipe.hasEnoughSnapshots()) {
|
||||
if (pipeManager.calib3dPipe.tryCalibration()) {
|
||||
ctx.status(200);
|
||||
} else {
|
||||
System.err.println("CALFAIL");
|
||||
ctx.status(500);
|
||||
}
|
||||
}
|
||||
pipeManager.setCalibrationMode(false);
|
||||
ctx.status(200);
|
||||
}
|
||||
|
||||
public static void onPnpModel(Context ctx) throws JsonProcessingException {
|
||||
System.out.println(ctx.body());
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
List points = objectMapper.readValue(ctx.body(), List.class);
|
||||
System.out.println(points);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,10 @@ public class Server {
|
||||
});
|
||||
app.post("/api/settings/general", RequestHandler::onGeneralSettings);
|
||||
app.post("/api/settings/camera", RequestHandler::onCameraSettings);
|
||||
app.post("/api/settings/startCalibration", RequestHandler::onCalibrationStart);
|
||||
app.post("/api/settings/snapshot", RequestHandler::onSnapshot);
|
||||
app.post("/api/settings/endCalibration", RequestHandler::onCalibrationEnding);
|
||||
app.post("/api/vision/pnpModel", RequestHandler::onPnpModel);
|
||||
app.start(port);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.chameleonvision.web;
|
||||
|
||||
import com.chameleonvision.config.CameraCalibrationConfig;
|
||||
import com.chameleonvision.config.ConfigManager;
|
||||
import com.chameleonvision.vision.VisionManager;
|
||||
import com.chameleonvision.vision.VisionProcess;
|
||||
@@ -9,7 +10,7 @@ import com.chameleonvision.vision.camera.USBCameraCapture;
|
||||
import com.chameleonvision.vision.enums.ImageRotationMode;
|
||||
import com.chameleonvision.vision.enums.StreamDivisor;
|
||||
import com.chameleonvision.vision.pipeline.CVPipeline;
|
||||
import com.chameleonvision.vision.pipeline.impl.CVPipeline2d;
|
||||
import com.chameleonvision.vision.pipeline.impl.StandardCVPipeline;
|
||||
import com.chameleonvision.vision.pipeline.CVPipelineSettings;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
@@ -26,6 +27,7 @@ import java.lang.reflect.Field;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
public class SocketHandler {
|
||||
@@ -33,6 +35,8 @@ public class SocketHandler {
|
||||
private static List<WsContext> users;
|
||||
private static ObjectMapper objectMapper;
|
||||
|
||||
private static final Object broadcastLock = new Object();
|
||||
|
||||
SocketHandler() {
|
||||
users = new ArrayList<>();
|
||||
objectMapper = new ObjectMapper(new MessagePackFactory());
|
||||
@@ -107,8 +111,7 @@ public class SocketHandler {
|
||||
// HashMap<String, Object> data = (HashMap<String, Object>) entry.getValue();
|
||||
String pipeName = (String) entry.getValue();
|
||||
// TODO: add to UI selection for new 2d/3d
|
||||
boolean is3d = false;
|
||||
currentProcess.pipelineManager.addNewPipeline(is3d, pipeName);
|
||||
currentProcess.pipelineManager.addNewPipeline(pipeName);
|
||||
sendFullSettings();
|
||||
VisionManager.saveCurrentCameraPipelines();
|
||||
break;
|
||||
@@ -134,11 +137,22 @@ public class SocketHandler {
|
||||
sendFullSettings();
|
||||
break;
|
||||
}
|
||||
case "is3D": {
|
||||
VisionManager.getCurrentUIVisionProcess().setIs3d((Boolean) entry.getValue());
|
||||
break;
|
||||
}
|
||||
case "currentPipeline": {
|
||||
currentProcess.pipelineManager.setCurrentPipeline((Integer) entry.getValue());
|
||||
sendFullSettings();
|
||||
break;
|
||||
}
|
||||
case "isPNPCalibration": {
|
||||
currentProcess.pipelineManager.setCalibrationMode((Boolean) entry.getValue());
|
||||
break;
|
||||
}
|
||||
case "takeCalibrationSnapshot": {
|
||||
currentProcess.pipelineManager.calib3dPipe.takeSnapshot();
|
||||
}
|
||||
default: {
|
||||
|
||||
switch (entry.getKey()) {//Pre field value set
|
||||
@@ -156,8 +170,8 @@ public class SocketHandler {
|
||||
}
|
||||
prop = currentCamera.getProperties().getStaticProperties();
|
||||
currentProcess.cameraStreamer.recalculateDivision();
|
||||
if (currentPipeline instanceof CVPipeline2d)
|
||||
((CVPipeline2d) currentPipeline).settings.point = Arrays.asList(prop.mode.width / 2, prop.mode.height / 2);//Reset Crosshair in single point calib
|
||||
if (currentPipeline instanceof StandardCVPipeline)
|
||||
((StandardCVPipeline) currentPipeline).settings.point = Arrays.asList(prop.mode.width / 2, prop.mode.height / 2);//Reset Crosshair in single point calib
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -181,8 +195,8 @@ public class SocketHandler {
|
||||
break;
|
||||
}
|
||||
case "videoModeIndex": {
|
||||
if (currentPipeline instanceof CVPipeline2d)
|
||||
((CVPipeline2d) currentPipeline).settings.point = new ArrayList<>();//This will reset the calibration
|
||||
if (currentPipeline instanceof StandardCVPipeline)
|
||||
((StandardCVPipeline) currentPipeline).settings.point = new ArrayList<>();//This will reset the calibration
|
||||
currentCamera.setVideoMode((Integer) entry.getValue());
|
||||
currentProcess.cameraStreamer.recalculateDivision();
|
||||
break;
|
||||
@@ -217,18 +231,22 @@ public class SocketHandler {
|
||||
}
|
||||
|
||||
private static void broadcastMessage(Object obj, WsContext userToSkip) {
|
||||
if (users != null)
|
||||
for (WsContext user : users) {
|
||||
if (userToSkip != null && user.getSessionId().equals(userToSkip.getSessionId())) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
ByteBuffer b = ByteBuffer.wrap(objectMapper.writeValueAsBytes(obj));
|
||||
user.send(b);
|
||||
} catch (JsonProcessingException e) {
|
||||
e.printStackTrace();
|
||||
synchronized (broadcastLock) {
|
||||
if (users != null) {
|
||||
var userList = users;
|
||||
for (WsContext user : userList) {
|
||||
if (userToSkip != null && user.getSessionId().equals(userToSkip.getSessionId())) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
ByteBuffer b = ByteBuffer.wrap(objectMapper.writeValueAsBytes(obj));
|
||||
user.send(b);
|
||||
} catch (JsonProcessingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void broadcastMessage(Object obj) {
|
||||
@@ -267,9 +285,16 @@ public class SocketHandler {
|
||||
HashMap<String, Object> tmp = new HashMap<>();
|
||||
VisionProcess currentVisionProcess = VisionManager.getCurrentUIVisionProcess();
|
||||
USBCameraCapture currentCamera = VisionManager.getCurrentUIVisionProcess().getCamera();
|
||||
|
||||
tmp.put("fov", currentCamera.getProperties().getFOV());
|
||||
tmp.put("streamDivisor", currentVisionProcess.cameraStreamer.getDivisor().ordinal());
|
||||
tmp.put("resolution", currentVisionProcess.getCamera().getProperties().getCurrentVideoModeIndex());
|
||||
tmp.put("tilt", currentVisionProcess.getCamera().getProperties().getTilt().getDegrees());
|
||||
|
||||
List<CameraCalibrationConfig.UICameraCalibrationConfig> calibrations = currentCamera.getAllCalibrationData().stream()
|
||||
.map(CameraCalibrationConfig.UICameraCalibrationConfig::new).collect(Collectors.toList());
|
||||
tmp.put("calibration", calibrations);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user