3d, camera calibration, backend settings hookup (#80)

* Implement new UI backend stuff

* Kinda partially add resolution accuracy list

* camera calibration go brrrrrrrr

* ayyyy calibration works

* Maybe fix grouping

* Reorganize camera view

* Fix settings not getting sent

* Make pretty (#4)

* Reorganize camera view

* Apply some cosmetic layout changes to the cameras page

* Fix pipeline rollback bug when starting on non-dashboard pages

Co-authored-by: Matt <matthew.morley.ca@gmail.com>

* Fix naming mismatch

* Mostly make stuff work

* rename robot-relative pose to camera-relative pose

* SolvePNP memes, fix isFovConfigurable

* Change config path to photonvision_config

* netmask go poof, fix zip download?

* Update index.js

* Fix multi cam stuff?

* Use LinearFilter instead

* Fix multicam

* aaa

* start adding restart device and restart program, fix square size bug

* Add some debug stuff

* oop

* Start fixing tests

* Fix tests

* Make target box proportinal

* run spotless

* Make crosshair h o t p i n k

* Address review comments

* Address review 2 electric booaloo

* Possibly implement vendor FOV?

* Make centroid crosshair gren

* actually use FOV

* Fix tests

* actually fix tests

Co-authored-by: Declan Freeman-Gleason <declanfreemangleason@gmail.com>
This commit is contained in:
Matt
2020-08-14 12:39:21 -07:00
committed by GitHub
parent 86ea661ed9
commit b3436765e1
86 changed files with 2106 additions and 1173 deletions

View File

@@ -28,31 +28,31 @@ import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.LogLevel;
import org.photonvision.common.logging.Logger;
import org.photonvision.common.util.TestUtils;
import org.photonvision.common.util.file.JacksonUtils;
import org.photonvision.vision.pipeline.ColoredShapePipelineSettings;
import org.photonvision.vision.pipeline.ReflectivePipelineSettings;
import org.photonvision.vision.target.TargetModel;
public class ConfigTest {
private static final ConfigManager configMgr;
private static final CameraConfiguration cameraConfig =
new CameraConfiguration("TestCamera", "/dev/video420");
private static final ReflectivePipelineSettings REFLECTIVE_PIPELINE_SETTINGS =
new ReflectivePipelineSettings();
private static final ColoredShapePipelineSettings COLORED_SHAPE_PIPELINE_SETTINGS =
new ColoredShapePipelineSettings();
static {
TestUtils.loadLibraries();
configMgr = new ConfigManager(Path.of("testconfigdir"));
}
private static ConfigManager configMgr;
private static final CameraConfiguration cameraConfig =
new CameraConfiguration("TestCamera", "/dev/video420");
private static ReflectivePipelineSettings REFLECTIVE_PIPELINE_SETTINGS;
private static ColoredShapePipelineSettings COLORED_SHAPE_PIPELINE_SETTINGS;
@BeforeAll
public static void init() {
TestUtils.loadLibraries();
configMgr = new ConfigManager(Path.of("testconfigdir"));
Logger.setLevel(LogGroup.General, LogLevel.TRACE);
REFLECTIVE_PIPELINE_SETTINGS = new ReflectivePipelineSettings();
COLORED_SHAPE_PIPELINE_SETTINGS = new ColoredShapePipelineSettings();
REFLECTIVE_PIPELINE_SETTINGS.pipelineNickname = "2019Tape";
REFLECTIVE_PIPELINE_SETTINGS.targetModel = TargetModel.get2019Target();
@@ -67,7 +67,6 @@ public class ConfigTest {
@Order(1)
public void serializeConfig() throws IOException {
TestUtils.loadLibraries();
JacksonUtils.serializer(Path.of("settings.json"), REFLECTIVE_PIPELINE_SETTINGS);
Logger.setLevel(LogGroup.General, LogLevel.TRACE);
configMgr.getConfig().addCameraConfig(cameraConfig);
@@ -75,16 +74,16 @@ public class ConfigTest {
var camConfDir =
new File(
Path.of(configMgr.rootFolder.toString(), "cameras", "TestCamera")
Path.of(configMgr.configDirectoryFile.toString(), "cameras", "TestCamera")
.toAbsolutePath()
.toString());
Assertions.assertTrue(camConfDir.exists(), "TestCamera config folder not found!");
Assertions.assertTrue(
Files.exists(Path.of(configMgr.rootFolder.toString(), "hardwareConfig.json")),
Files.exists(Path.of(configMgr.configDirectoryFile.toString(), "hardwareConfig.json")),
"hardwareConfig.json file not found!");
Assertions.assertTrue(
Files.exists(Path.of(configMgr.rootFolder.toString(), "networkSettings.json")),
Files.exists(Path.of(configMgr.configDirectoryFile.toString(), "networkSettings.json")),
"networkSettings.json file not found!");
}
@@ -116,7 +115,7 @@ public class ConfigTest {
e.printStackTrace();
}
FileUtils.cleanDirectory(configMgr.rootFolder);
configMgr.rootFolder.delete();
FileUtils.cleanDirectory(configMgr.configDirectoryFile);
configMgr.configDirectoryFile.delete();
}
}

View File

@@ -29,7 +29,7 @@ import org.photonvision.common.util.TestUtils;
public class HardwareManagerTest {
@Test
public void ManagementTest() throws IOException {
public void managementTest() throws IOException {
var config =
new ObjectMapper().readValue(TestUtils.getHardwareConfigJson(), HardwareConfig.class);

View File

@@ -24,33 +24,33 @@ import org.photonvision.common.hardware.GPIO.CustomGPIO;
import org.photonvision.common.hardware.GPIO.GPIOBase;
import org.photonvision.common.hardware.GPIO.PiGPIO;
import org.photonvision.common.hardware.Platform;
import org.photonvision.common.hardware.metrics.CPU;
import org.photonvision.common.hardware.metrics.GPU;
import org.photonvision.common.hardware.metrics.RAM;
import org.photonvision.common.hardware.metrics.CPUMetrics;
import org.photonvision.common.hardware.metrics.GPUMetrics;
import org.photonvision.common.hardware.metrics.RAMMetrics;
public class HardwareTest {
@Test
public void testHardware() {
CPU cpu = CPU.getInstance();
RAM ram = RAM.getInstance();
GPU gpu = GPU.getInstance();
CPUMetrics cpuMetrics = new CPUMetrics();
RAMMetrics ramMetrics = new RAMMetrics();
GPUMetrics gpuMetrics = new GPUMetrics();
if (!Platform.isRaspberryPi()) return;
System.out.println("Testing on platform: " + Platform.CurrentPlatform);
System.out.println("Printing CPU Info:");
System.out.println("Memory: " + cpu.getMemory() + "MB");
System.out.println("Temperature: " + cpu.getTemp() + "C");
System.out.println("Utilization: : " + cpu.getUtilization() + "%");
System.out.println("Memory: " + cpuMetrics.getMemory() + "MB");
System.out.println("Temperature: " + cpuMetrics.getTemp() + "C");
System.out.println("Utilization: : " + cpuMetrics.getUtilization() + "%");
System.out.println("Printing GPU Info:");
System.out.println("Memory: " + gpu.getMemory() + "MB");
System.out.println("Temperature: " + gpu.getTemp() + "C");
System.out.println("Memory: " + gpuMetrics.getMemory() + "MB");
System.out.println("Temperature: " + gpuMetrics.getTemp() + "C");
System.out.println("Printing RAM Info: ");
System.out.println("Used RAM: : " + ram.getUsedRam() + "MB");
System.out.println("Used RAM: : " + ramMetrics.getUsedRam() + "MB");
}
@Test

View File

@@ -17,12 +17,15 @@
package org.photonvision.vision.pipeline;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import edu.wpi.first.wpilibj.geometry.Rotation2d;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang3.tuple.Triple;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.opencv.core.Mat;
@@ -53,13 +56,19 @@ public class Calibrate3dPipeTest {
FindBoardCornersPipe findBoardCornersPipe = new FindBoardCornersPipe();
findBoardCornersPipe.setParams(
new FindBoardCornersPipe.FindCornersPipeParams(11, 4, false, 15));
var findBoardCornersPipeOutput = findBoardCornersPipe.run(frames);
new FindBoardCornersPipe.FindCornersPipeParams(
11, 4, UICalibrationData.BoardType.DOTBOARD, 15));
List<Triple<Size, Mat, Mat>> foundCornersList = new ArrayList<>();
for (var f : frames) {
foundCornersList.add(findBoardCornersPipe.run(f).output);
}
Calibrate3dPipe calibrate3dPipe = new Calibrate3dPipe();
calibrate3dPipe.setParams(new Calibrate3dPipe.CalibratePipeParams(new Size(640, 480)));
var calibrate3dPipeOutput = calibrate3dPipe.run(findBoardCornersPipeOutput.output);
var calibrate3dPipeOutput = calibrate3dPipe.run(foundCornersList);
assertTrue(calibrate3dPipeOutput.output.perViewErrors.length > 0);
System.out.println(
"Per View Errors: " + Arrays.toString(calibrate3dPipeOutput.output.perViewErrors));
@@ -71,10 +80,10 @@ public class Calibrate3dPipeTest {
File dir = new File(TestUtils.getDotBoardImagesPath().toAbsolutePath().toString());
File[] directoryListing = dir.listFiles();
Calibration3dPipeline calibration3dPipeline = new Calibration3dPipeline();
Calibrate3dPipeline calibration3dPipeline = new Calibrate3dPipeline(20);
calibration3dPipeline.getSettings().boardHeight = 11;
calibration3dPipeline.getSettings().boardWidth = 4;
calibration3dPipeline.getSettings().isUsingChessboard = false;
calibration3dPipeline.getSettings().boardType = UICalibrationData.BoardType.DOTBOARD;
calibration3dPipeline.getSettings().gridSize = 15;
calibration3dPipeline.getSettings().resolution = new Size(640, 480);
@@ -84,28 +93,35 @@ public class Calibrate3dPipeTest {
calibration3dPipeline.run(
new Frame(
new CVMat(Imgcodecs.imread(file.getAbsolutePath())),
new FrameStaticProperties(640, 480, 60)));
TestUtils.showImage(output.outputFrame.image.getMat());
new FrameStaticProperties(640, 480, 60, new Rotation2d(), null)));
// TestUtils.showImage(output.outputFrame.image.getMat());
}
assertTrue(
calibration3dPipeline.foundCornersList.stream()
.map(Triple::getRight)
.allMatch(it -> it.width() > 0 && it.height() > 0));
calibration3dPipeline.removeSnapshot(0);
calibration3dPipeline.startCalibration();
calibration3dPipeline.run(
new Frame(
new CVMat(Imgcodecs.imread(directoryListing[0].getAbsolutePath())),
new FrameStaticProperties(640, 480, 60)));
new FrameStaticProperties(640, 480, 60, new Rotation2d(), null)));
assertTrue(
calibration3dPipeline.foundCornersList.stream()
.map(Triple::getRight)
.allMatch(it -> it.width() > 0 && it.height() > 0));
var cal = calibration3dPipeline.tryCalibration();
calibration3dPipeline.finishCalibration();
System.out.println(
"Per View Errors: " + Arrays.toString(calibration3dPipeline.perViewErrors()));
System.out.println(
"Camera Intrinsics : "
+ calibration3dPipeline.cameraCalibrationCoefficients().cameraIntrinsics.toString());
System.out.println(
"Camera Extrinsics : "
+ calibration3dPipeline.cameraCalibrationCoefficients().cameraExtrinsics.toString());
System.out.println(
"Standard Deviation: "
+ calibration3dPipeline.cameraCalibrationCoefficients().standardDeviation);
assertNotNull(cal);
assertNotNull(cal.perViewErrors);
System.out.println("Per View Errors: " + Arrays.toString(cal.perViewErrors));
System.out.println("Camera Intrinsics : " + cal.cameraIntrinsics.toString());
System.out.println("Camera Extrinsics : " + cal.cameraExtrinsics.toString());
System.out.println("Standard Deviation: " + cal.standardDeviation);
System.out.println(
"Mean: " + Arrays.stream(calibration3dPipeline.perViewErrors()).average().toString());
}

View File

@@ -57,7 +57,7 @@ public class CirclePNPTest {
}
private CameraCalibrationCoefficients getCoeffs(String filename) {
var cameraCalibration = TestUtils.getCoeffs(filename, false);
var cameraCalibration = TestUtils.getCoeffs(filename, true);
checkCameraCoefficients(cameraCalibration);
return cameraCalibration;
}
@@ -162,7 +162,7 @@ public class CirclePNPTest {
System.out.println(
"Found targets at "
+ pipelineResult.targets.stream()
.map(TrackedTarget::getRobotRelativePose)
.map(TrackedTarget::getCameraToTarget)
.collect(Collectors.toList()));
}
}

View File

@@ -21,10 +21,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import edu.wpi.first.wpilibj.geometry.Rotation2d;
import edu.wpi.first.wpilibj.util.Units;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.opencv.imgcodecs.Imgcodecs;
import org.photonvision.common.util.TestUtils;
import org.photonvision.vision.calibration.CameraCalibrationCoefficients;
import org.photonvision.vision.frame.Frame;
@@ -58,7 +60,7 @@ public class SolvePNPTest {
}
private CameraCalibrationCoefficients getCoeffs(String filename) {
var cameraCalibration = TestUtils.getCoeffs(filename, false);
var cameraCalibration = TestUtils.getCoeffs(filename, true);
checkCameraCoefficients(cameraCalibration);
return cameraCalibration;
}
@@ -97,12 +99,13 @@ public class SolvePNPTest {
pipeline.getSettings().contourIntersection = ContourIntersectionDirection.Up;
pipeline.getSettings().cornerDetectionUseConvexHulls = true;
pipeline.getSettings().targetModel = TargetModel.get2019Target();
pipeline.getSettings().cameraCalibration = getCoeffs(LIFECAM_240P_CAL_FILE);
var frameProvider =
new FileFrameProvider(
TestUtils.getWPIImagePath(TestUtils.WPI2019Image.kCargoStraightDark48in, false),
TestUtils.WPI2019Image.FOV);
TestUtils.WPI2019Image.FOV,
new Rotation2d(),
TestUtils.get2019LifeCamCoeffs(true));
CVPipelineResult pipelineResult;
@@ -110,12 +113,13 @@ public class SolvePNPTest {
printTestResults(pipelineResult);
// these numbers are not *accurate*, but they are known and expected
var pose = pipelineResult.targets.get(0).getRobotRelativePose();
Assertions.assertEquals(41.96, pose.getTranslation().getX(), 0.05);
Assertions.assertEquals(-1.03, pose.getTranslation().getY(), 0.05);
Assertions.assertEquals(1.46, pose.getRotation().getDegrees(), 0.05);
var pose = pipelineResult.targets.get(0).getCameraToTarget();
Assertions.assertEquals(1.1, pose.getTranslation().getX(), 0.05);
Assertions.assertEquals(0.0, pose.getTranslation().getY(), 0.05);
Assertions.assertEquals(1, pose.getRotation().getDegrees(), 1);
TestUtils.showImage(pipelineResult.outputFrame.image.getMat(), "Pipeline output", 1000 * 90);
Imgcodecs.imwrite("D:\\out.jpg", pipelineResult.outputFrame.image.getMat());
TestUtils.showImage(pipelineResult.outputFrame.image.getMat(), "Pipeline output", 999999);
}
@Test
@@ -129,23 +133,23 @@ public class SolvePNPTest {
pipeline.getSettings().solvePNPEnabled = true;
pipeline.getSettings().cornerDetectionAccuracyPercentage = 4;
pipeline.getSettings().cornerDetectionUseConvexHulls = true;
pipeline.getSettings().cameraCalibration = getCoeffs(LIFECAM_480P_CAL_FILE);
pipeline.getSettings().targetModel = TargetModel.get2020Target(36);
pipeline.getSettings().cameraPitch = Rotation2d.fromDegrees(0.0);
pipeline.getSettings().targetModel = TargetModel.get2020Target();
var frameProvider =
new FileFrameProvider(
TestUtils.getWPIImagePath(TestUtils.WPI2020Image.kBlueGoal_224in_Left, false),
TestUtils.WPI2020Image.FOV);
TestUtils.WPI2020Image.FOV,
new Rotation2d(),
TestUtils.get2020LifeCamCoeffs(true));
CVPipelineResult pipelineResult = pipeline.run(frameProvider.get());
printTestResults(pipelineResult);
// these numbers are not *accurate*, but they are known and expected
var pose = pipelineResult.targets.get(0).getRobotRelativePose();
Assertions.assertEquals(260.26, pose.getTranslation().getX(), 0.05);
Assertions.assertEquals(64.26, pose.getTranslation().getY(), 0.05);
Assertions.assertEquals(36.88, pose.getRotation().getDegrees(), 0.05);
var pose = pipelineResult.targets.get(0).getCameraToTarget();
Assertions.assertEquals(Units.inchesToMeters(240.26), pose.getTranslation().getX(), 0.05);
Assertions.assertEquals(Units.inchesToMeters(35), pose.getTranslation().getY(), 0.05);
Assertions.assertEquals(42, pose.getRotation().getDegrees(), 1);
TestUtils.showImage(pipelineResult.outputFrame.image.getMat(), "Pipeline output", 999999);
}
@@ -193,7 +197,7 @@ public class SolvePNPTest {
System.out.println(
"Found targets at "
+ pipelineResult.targets.stream()
.map(TrackedTarget::getRobotRelativePose)
.map(TrackedTarget::getCameraToTarget)
.collect(Collectors.toList()));
}
}

View File

@@ -18,6 +18,7 @@
package org.photonvision.vision.processes;
import edu.wpi.cscore.VideoMode;
import edu.wpi.first.wpilibj.geometry.Rotation2d;
import java.util.HashMap;
import java.util.List;
import org.junit.jupiter.api.*;
@@ -78,8 +79,9 @@ public class VisionModuleManagerTest {
}
@Override
public void setCurrentVideoMode(VideoMode videoMode) {
this.frameStaticProperties = new FrameStaticProperties(getCurrentVideoMode(), getFOV());
public void setVideoModeInternal(VideoMode videoMode) {
this.frameStaticProperties =
new FrameStaticProperties(getCurrentVideoMode(), getFOV(), new Rotation2d(), null);
}
@Override

View File

@@ -19,6 +19,7 @@ package org.photonvision.vision.target;
import static org.junit.jupiter.api.Assertions.assertEquals;
import edu.wpi.first.wpilibj.geometry.Rotation2d;
import org.apache.commons.math3.util.FastMath;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -35,7 +36,8 @@ public class TargetCalculationsTest {
private static final double diagFOV = Math.toRadians(70.0);
private static final FrameStaticProperties props =
new FrameStaticProperties((int) imageSize.width, (int) imageSize.height, diagFOV);
new FrameStaticProperties(
(int) imageSize.width, (int) imageSize.height, diagFOV, new Rotation2d(), null);
private static final TrackedTarget.TargetCalculationParameters params =
new TrackedTarget.TargetCalculationParameters(
true,