Split photon-server and photon-core (#211)

Uses multiple Gradle projects to support the split.
This commit is contained in:
Banks T
2021-01-14 21:45:26 -05:00
committed by GitHub
parent dc0f8cf296
commit 58b39f47aa
531 changed files with 310 additions and 1317 deletions

View File

@@ -1,338 +0,0 @@
/*
* Copyright (C) Photon Vision.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
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 edu.wpi.first.wpilibj.util.Units;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.photonvision.common.util.TestUtils;
import org.photonvision.vision.calibration.CameraCalibrationCoefficients;
import org.photonvision.vision.camera.QuirkyCamera;
import org.photonvision.vision.frame.Frame;
import org.photonvision.vision.frame.FrameStaticProperties;
import org.photonvision.vision.opencv.CVMat;
import org.photonvision.vision.pipe.impl.Calibrate3dPipe;
import org.photonvision.vision.pipe.impl.FindBoardCornersPipe;
public class Calibrate3dPipeTest {
@BeforeEach
public void Init() {
TestUtils.loadLibraries();
}
@Test
public void perViewErrorsTest() {
List<Mat> frames = new ArrayList<>();
File dir = new File(TestUtils.getDotBoardImagesPath().toAbsolutePath().toString());
File[] directoryListing = dir.listFiles();
for (var file : directoryListing) {
frames.add(Imgcodecs.imread(file.getAbsolutePath()));
}
FindBoardCornersPipe findBoardCornersPipe = new FindBoardCornersPipe();
findBoardCornersPipe.setParams(
new FindBoardCornersPipe.FindCornersPipeParams(
11, 4, UICalibrationData.BoardType.DOTBOARD, 15));
List<Triple<Size, Mat, Mat>> foundCornersList = new ArrayList<>();
for (var f : frames) {
var copy = new Mat();
f.copyTo(copy);
foundCornersList.add(findBoardCornersPipe.run(Pair.of(f, copy)).output);
}
Calibrate3dPipe calibrate3dPipe = new Calibrate3dPipe();
calibrate3dPipe.setParams(new Calibrate3dPipe.CalibratePipeParams(new Size(640, 480)));
var calibrate3dPipeOutput = calibrate3dPipe.run(foundCornersList);
assertTrue(calibrate3dPipeOutput.output.perViewErrors.length > 0);
System.out.println(
"Per View Errors: " + Arrays.toString(calibrate3dPipeOutput.output.perViewErrors));
}
@Test
public void calibrationPipelineTest() {
int startMatCount = CVMat.getMatCount();
File dir = new File(TestUtils.getDotBoardImagesPath().toAbsolutePath().toString());
File[] directoryListing = dir.listFiles();
Calibrate3dPipeline calibration3dPipeline = new Calibrate3dPipeline(20);
calibration3dPipeline.getSettings().boardHeight = 11;
calibration3dPipeline.getSettings().boardWidth = 4;
calibration3dPipeline.getSettings().boardType = UICalibrationData.BoardType.DOTBOARD;
calibration3dPipeline.getSettings().gridSize = 15;
calibration3dPipeline.getSettings().resolution = new Size(640, 480);
for (var file : directoryListing) {
calibration3dPipeline.takeSnapshot();
var output =
calibration3dPipeline.run(
new Frame(
new CVMat(Imgcodecs.imread(file.getAbsolutePath())),
new FrameStaticProperties(640, 480, 60, new Rotation2d(), null)),
QuirkyCamera.DefaultCamera);
// TestUtils.showImage(output.outputFrame.image.getMat());
output.release();
}
assertTrue(
calibration3dPipeline.foundCornersList.stream()
.map(Triple::getRight)
.allMatch(it -> it.width() > 0 && it.height() > 0));
calibration3dPipeline.removeSnapshot(0);
calibration3dPipeline
.run(
new Frame(
new CVMat(Imgcodecs.imread(directoryListing[0].getAbsolutePath())),
new FrameStaticProperties(640, 480, 60, new Rotation2d(), null)),
QuirkyCamera.DefaultCamera)
.release();
assertTrue(
calibration3dPipeline.foundCornersList.stream()
.map(Triple::getRight)
.allMatch(it -> it.width() > 0 && it.height() > 0));
var cal = calibration3dPipeline.tryCalibration();
calibration3dPipeline.finishCalibration();
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());
// Confirm we didn't get leaky on our mat usage
assertTrue(CVMat.getMatCount() == startMatCount);
}
@Test
public void calibrateSquares320x240_pi() {
// Pi3 and V1.3 camera
String base = TestUtils.getSquaresBoardImagesPath().toAbsolutePath().toString();
File dir = Path.of(base, "piCam", "320_240_1").toFile();
Size sz = new Size(320, 240);
calibrateSquaresCommon(sz, dir);
}
@Test
public void calibrateSquares640x480_pi() {
// Pi3 and V1.3 camera
String base = TestUtils.getSquaresBoardImagesPath().toAbsolutePath().toString();
File dir = Path.of(base, "piCam", "640_480_1").toFile();
Size sz = new Size(640, 480);
calibrateSquaresCommon(sz, dir);
}
@Test
public void calibrateSquares960x720_pi() {
// Pi3 and V1.3 camera
String base = TestUtils.getSquaresBoardImagesPath().toAbsolutePath().toString();
File dir = Path.of(base, "piCam", "960_720_1").toFile();
Size sz = new Size(960, 720);
calibrateSquaresCommon(sz, dir);
}
@Test
public void calibrateSquares1920x1080_pi() {
// Pi3 and V1.3 camera
String base = TestUtils.getSquaresBoardImagesPath().toAbsolutePath().toString();
File dir = Path.of(base, "piCam", "1920_1080_1").toFile();
Size sz = new Size(1920, 1080);
calibrateSquaresCommon(sz, dir);
}
@Test
public void calibrateSquares320x240_gloworm() {
// Gloworm Beta
String base = TestUtils.getSquaresBoardImagesPath().toAbsolutePath().toString();
File dir = Path.of(base, "gloworm", "320_240_1").toFile();
Size sz = new Size(320, 240);
Size boardDim = new Size(9, 7);
calibrateSquaresCommon(sz, dir, boardDim);
}
@Test
public void calibrateSquares_960_720_gloworm() {
// Gloworm Beta
String base = TestUtils.getSquaresBoardImagesPath().toAbsolutePath().toString();
File dir = Path.of(base, "gloworm", "960_720_1").toFile();
Size sz = new Size(960, 720);
Size boardDim = new Size(9, 7);
calibrateSquaresCommon(sz, dir, boardDim);
}
@Test
public void calibrateSquares_1280_720_gloworm() {
// Gloworm Beta
// This image set will return a fairly offset Y-pixel for the optical center point
String base = TestUtils.getSquaresBoardImagesPath().toAbsolutePath().toString();
File dir = Path.of(base, "gloworm", "1280_720_1").toFile();
Size sz = new Size(1280, 720);
Size boardDim = new Size(9, 7);
calibrateSquaresCommon(sz, dir, boardDim, 640, 192);
}
@Test
public void calibrateSquares_1920_1080_gloworm() {
// Gloworm Beta
// This image set has most samples on the right, and is expected to return a slightly
// wonky calibration.
String base = TestUtils.getSquaresBoardImagesPath().toAbsolutePath().toString();
File dir = Path.of(base, "gloworm", "1920_1080_1").toFile();
Size sz = new Size(1920, 1080);
Size boardDim = new Size(9, 7);
calibrateSquaresCommon(sz, dir, boardDim, 1311, 540);
}
public void calibrateSquaresCommon(Size imgRes, File rootFolder) {
calibrateSquaresCommon(imgRes, rootFolder, new Size(8, 8));
}
public void calibrateSquaresCommon(Size imgRes, File rootFolder, Size boardDim) {
calibrateSquaresCommon(
imgRes, rootFolder, boardDim, Units.inchesToMeters(1), imgRes.width / 2, imgRes.height / 2);
}
public void calibrateSquaresCommon(
Size imgRes, File rootFolder, Size boardDim, double expectedXCenter, double expectedYCenter) {
calibrateSquaresCommon(
imgRes, rootFolder, boardDim, Units.inchesToMeters(1), expectedXCenter, expectedYCenter);
}
public void calibrateSquaresCommon(
Size imgRes,
File rootFolder,
Size boardDim,
double boardGridSize_m,
double expectedXCenter,
double expectedYCenter) {
int startMatCount = CVMat.getMatCount();
File[] directoryListing = rootFolder.listFiles();
assertTrue(directoryListing.length >= 25);
Calibrate3dPipeline calibration3dPipeline = new Calibrate3dPipeline(20);
calibration3dPipeline.getSettings().boardType = UICalibrationData.BoardType.CHESSBOARD;
calibration3dPipeline.getSettings().resolution = imgRes;
calibration3dPipeline.getSettings().boardHeight = (int) Math.round(boardDim.height);
calibration3dPipeline.getSettings().boardWidth = (int) Math.round(boardDim.width);
calibration3dPipeline.getSettings().gridSize = boardGridSize_m;
for (var file : directoryListing) {
if (file.isFile()) {
calibration3dPipeline.takeSnapshot();
var output =
calibration3dPipeline.run(
new Frame(
new CVMat(Imgcodecs.imread(file.getAbsolutePath())),
new FrameStaticProperties(
(int) imgRes.width, (int) imgRes.height, 67, new Rotation2d(), null)),
QuirkyCamera.DefaultCamera);
// TestUtils.showImage(output.outputFrame.image.getMat(), file.getName(), 1);
output.outputFrame.release();
}
}
assertTrue(
calibration3dPipeline.foundCornersList.stream()
.map(Triple::getRight)
.allMatch(it -> it.width() > 0 && it.height() > 0));
var cal = calibration3dPipeline.tryCalibration();
calibration3dPipeline.finishCalibration();
// visuallyDebugDistortion(directoryListing, imgRes, cal );
// Confirm we have indeed gotten valid calibration objects
assertNotNull(cal);
assertNotNull(cal.perViewErrors);
// Confirm the calibrated center pixel is fairly close to of the "expected" location at the
// center of the sensor.
// For all our data samples so far, this should be true.
double centerXErrPct =
Math.abs(cal.cameraIntrinsics.data[2] - expectedXCenter) / (expectedXCenter) * 100.0;
double centerYErrPct =
Math.abs(cal.cameraIntrinsics.data[5] - expectedYCenter) / (expectedYCenter) * 100.0;
assertTrue(centerXErrPct < 10.0);
assertTrue(centerYErrPct < 10.0);
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());
// Confirm we didn't get leaky on our mat usage
assertTrue(CVMat.getMatCount() == startMatCount);
}
/**
* Uses a given camera coefficents matrix set to "undistort" every image file found in a given
* directory and display them. Provides an easy way to visually debug the results of the
* calibration routine. Seems to play havoc with CI and takes a chunk of time, so shouldn't
* usually be left active in tests.
*
* @param directoryListing
* @param imgRes
* @param cal
*/
@SuppressWarnings("unused")
private void visuallyDebugDistortion(
File[] directoryListing, Size imgRes, CameraCalibrationCoefficients cal) {
for (var file : directoryListing) {
if (file.isFile()) {
Mat raw = Imgcodecs.imread(file.getAbsolutePath());
Mat undistorted = new Mat(new Size(imgRes.width * 2, imgRes.height * 2), raw.type());
Imgproc.undistort(
raw, undistorted, cal.cameraIntrinsics.getAsMat(), cal.cameraExtrinsics.getAsMat());
TestUtils.showImage(undistorted, "undistorted " + file.getName(), 1);
raw.release();
undistorted.release();
}
}
}
}

View File

@@ -1,170 +0,0 @@
/*
* Copyright (C) Photon Vision.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.photonvision.vision.pipeline;
import static org.junit.jupiter.api.Assertions.*;
import edu.wpi.first.wpilibj.geometry.Rotation2d;
import java.util.stream.Collectors;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.photonvision.common.util.TestUtils;
import org.photonvision.vision.calibration.CameraCalibrationCoefficients;
import org.photonvision.vision.camera.QuirkyCamera;
import org.photonvision.vision.frame.Frame;
import org.photonvision.vision.frame.provider.FileFrameProvider;
import org.photonvision.vision.opencv.CVMat;
import org.photonvision.vision.opencv.ContourGroupingMode;
import org.photonvision.vision.opencv.ContourIntersectionDirection;
import org.photonvision.vision.opencv.ContourShape;
import org.photonvision.vision.pipeline.result.CVPipelineResult;
import org.photonvision.vision.target.TargetModel;
import org.photonvision.vision.target.TrackedTarget;
public class CirclePNPTest {
private static final String LIFECAM_240P_CAL_FILE = "lifecam240p.json";
private static final String LIFECAM_480P_CAL_FILE = "lifecam480p.json";
@BeforeEach
public void Init() {
TestUtils.loadLibraries();
}
@Test
public void loadCameraIntrinsics() {
var lifecam240pCal = getCoeffs(LIFECAM_240P_CAL_FILE);
var lifecam480pCal = getCoeffs(LIFECAM_480P_CAL_FILE);
assertNotNull(lifecam240pCal);
checkCameraCoefficients(lifecam240pCal);
assertNotNull(lifecam480pCal);
checkCameraCoefficients(lifecam480pCal);
}
private CameraCalibrationCoefficients getCoeffs(String filename) {
var cameraCalibration = TestUtils.getCoeffs(filename, true);
checkCameraCoefficients(cameraCalibration);
return cameraCalibration;
}
private void checkCameraCoefficients(CameraCalibrationCoefficients cameraCalibration) {
assertNotNull(cameraCalibration);
assertEquals(3, cameraCalibration.cameraIntrinsics.rows);
assertEquals(3, cameraCalibration.cameraIntrinsics.cols);
assertEquals(3, cameraCalibration.cameraIntrinsics.getAsMat().rows());
assertEquals(3, cameraCalibration.cameraIntrinsics.getAsMat().cols());
assertEquals(3, cameraCalibration.cameraIntrinsics.getAsMatOfDouble().rows());
assertEquals(3, cameraCalibration.cameraIntrinsics.getAsMatOfDouble().cols());
assertEquals(3, cameraCalibration.getCameraIntrinsicsMat().rows());
assertEquals(3, cameraCalibration.getCameraIntrinsicsMat().cols());
assertEquals(1, cameraCalibration.cameraExtrinsics.rows);
assertEquals(5, cameraCalibration.cameraExtrinsics.cols);
assertEquals(1, cameraCalibration.cameraExtrinsics.getAsMat().rows());
assertEquals(5, cameraCalibration.cameraExtrinsics.getAsMat().cols());
assertEquals(1, cameraCalibration.cameraExtrinsics.getAsMatOfDouble().rows());
assertEquals(5, cameraCalibration.cameraExtrinsics.getAsMatOfDouble().cols());
assertEquals(1, cameraCalibration.getCameraExtrinsicsMat().rows());
assertEquals(5, cameraCalibration.getCameraExtrinsicsMat().cols());
}
@Test
public void testCircle() {
var pipeline = new ColoredShapePipeline();
pipeline.getSettings().hsvHue.set(0, 100);
pipeline.getSettings().hsvSaturation.set(100, 255);
pipeline.getSettings().hsvValue.set(100, 255);
pipeline.getSettings().outputShouldDraw = true;
pipeline.getSettings().maxCannyThresh = 50;
pipeline.getSettings().accuracy = 15;
pipeline.getSettings().allowableThreshold = 5;
pipeline.getSettings().solvePNPEnabled = true;
pipeline.getSettings().cornerDetectionAccuracyPercentage = 4;
pipeline.getSettings().cornerDetectionUseConvexHulls = true;
pipeline.getSettings().cameraCalibration = getCoeffs(LIFECAM_480P_CAL_FILE);
pipeline.getSettings().targetModel = TargetModel.kCircularPowerCell7in;
pipeline.getSettings().outputShouldDraw = true;
pipeline.getSettings().outputShowMultipleTargets = false;
pipeline.getSettings().contourGroupingMode = ContourGroupingMode.Single;
pipeline.getSettings().contourIntersection = ContourIntersectionDirection.Up;
pipeline.getSettings().desiredShape = ContourShape.Circle;
pipeline.getSettings().allowableThreshold = 10;
pipeline.getSettings().minRadius = 30;
pipeline.getSettings().accuracyPercentage = 30.0;
var frameProvider =
new FileFrameProvider(
TestUtils.getPowercellImagePath(TestUtils.PowercellTestImages.kPowercell_test_6, false),
TestUtils.WPI2020Image.FOV,
new Rotation2d(),
TestUtils.get2020LifeCamCoeffs(true));
CVPipelineResult pipelineResult = pipeline.run(frameProvider.get(), QuirkyCamera.DefaultCamera);
printTestResults(pipelineResult);
TestUtils.showImage(pipelineResult.outputFrame.image.getMat(), "Pipeline output", 999999);
}
private static void continuouslyRunPipeline(Frame frame, ReflectivePipelineSettings settings) {
var pipeline = new ReflectivePipeline();
pipeline.settings = settings;
while (true) {
CVPipelineResult pipelineResult = pipeline.run(frame, QuirkyCamera.DefaultCamera);
printTestResults(pipelineResult);
int preRelease = CVMat.getMatCount();
pipelineResult.release();
int postRelease = CVMat.getMatCount();
System.out.printf("Pre: %d, Post: %d\n", preRelease, postRelease);
}
}
// used to run VisualVM for profiling, which won't run on unit tests.
public static void main(String[] args) {
TestUtils.loadLibraries();
var frameProvider =
new FileFrameProvider(
TestUtils.getWPIImagePath(TestUtils.WPI2019Image.kCargoStraightDark72in_HighRes, false),
TestUtils.WPI2019Image.FOV);
var settings = new ReflectivePipelineSettings();
settings.hsvHue.set(60, 100);
settings.hsvSaturation.set(100, 255);
settings.hsvValue.set(190, 255);
settings.outputShouldDraw = true;
settings.outputShowMultipleTargets = true;
settings.contourGroupingMode = ContourGroupingMode.Dual;
settings.contourIntersection = ContourIntersectionDirection.Up;
continuouslyRunPipeline(frameProvider.get(), settings);
}
private static void printTestResults(CVPipelineResult pipelineResult) {
double fps = 1000 / pipelineResult.getLatencyMillis();
System.out.println(
"Pipeline ran in " + pipelineResult.getLatencyMillis() + "ms (" + fps + " " + "fps)");
System.out.println("Found " + pipelineResult.targets.size() + " valid targets");
System.out.println(
"Found targets at "
+ pipelineResult.targets.stream()
.map(TrackedTarget::getCameraToTarget)
.collect(Collectors.toList()));
}
}

View File

@@ -1,123 +0,0 @@
/*
* Copyright (C) Photon Vision.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.photonvision.vision.pipeline;
import org.junit.jupiter.api.Test;
import org.photonvision.common.util.TestUtils;
import org.photonvision.vision.camera.QuirkyCamera;
import org.photonvision.vision.frame.Frame;
import org.photonvision.vision.frame.provider.FileFrameProvider;
import org.photonvision.vision.opencv.ContourGroupingMode;
import org.photonvision.vision.opencv.ContourIntersectionDirection;
import org.photonvision.vision.opencv.ContourShape;
import org.photonvision.vision.pipeline.result.CVPipelineResult;
public class ColoredShapePipelineTest {
public static void testTriangleDetection(
ColoredShapePipeline pipeline, ColoredShapePipelineSettings settings, Frame frame) {
pipeline.settings = settings;
CVPipelineResult colouredShapePipelineResult = pipeline.run(frame, QuirkyCamera.DefaultCamera);
TestUtils.showImage(
colouredShapePipelineResult.outputFrame.image.getMat(), "Pipeline output: Triangle.");
printTestResults(colouredShapePipelineResult);
}
public static void testQuadrilateralDetection(
ColoredShapePipeline pipeline, ColoredShapePipelineSettings settings, Frame frame) {
settings.desiredShape = ContourShape.Quadrilateral;
pipeline.settings = settings;
CVPipelineResult colouredShapePipelineResult = pipeline.run(frame, QuirkyCamera.DefaultCamera);
TestUtils.showImage(
colouredShapePipelineResult.outputFrame.image.getMat(), "Pipeline output: Quadrilateral.");
printTestResults(colouredShapePipelineResult);
}
public static void testCustomShapeDetection(
ColoredShapePipeline pipeline, ColoredShapePipelineSettings settings, Frame frame) {
settings.desiredShape = ContourShape.Custom;
pipeline.settings = settings;
CVPipelineResult colouredShapePipelineResult = pipeline.run(frame, QuirkyCamera.DefaultCamera);
TestUtils.showImage(
colouredShapePipelineResult.outputFrame.image.getMat(), "Pipeline output: Custom.");
printTestResults(colouredShapePipelineResult);
}
@Test
public static void testCircleShapeDetection(
ColoredShapePipeline pipeline, ColoredShapePipelineSettings settings, Frame frame) {
settings.desiredShape = ContourShape.Circle;
pipeline.settings = settings;
CVPipelineResult colouredShapePipelineResult = pipeline.run(frame, QuirkyCamera.DefaultCamera);
TestUtils.showImage(
colouredShapePipelineResult.outputFrame.image.getMat(), "Pipeline output: Circle.");
printTestResults(colouredShapePipelineResult);
}
@Test
public static void testPowercellDetection(
ColoredShapePipelineSettings settings, ColoredShapePipeline pipeline) {
settings.hsvHue.set(10, 40);
settings.hsvSaturation.set(100, 255);
settings.hsvValue.set(100, 255);
settings.maxCannyThresh = 50;
settings.accuracy = 15;
settings.allowableThreshold = 5;
var frameProvider =
new FileFrameProvider(
TestUtils.getPowercellImagePath(TestUtils.PowercellTestImages.kPowercell_test_6, false),
TestUtils.WPI2019Image.FOV);
testCircleShapeDetection(pipeline, settings, frameProvider.get());
}
public static void main(String[] args) {
TestUtils.loadLibraries();
System.out.println(
TestUtils.getWPIImagePath(TestUtils.WPI2020Image.kBlueGoal_108in_Center, false));
var frameProvider =
new FileFrameProvider(
TestUtils.getPolygonImagePath(TestUtils.PolygonTestImages.kPolygons, false),
TestUtils.WPI2019Image.FOV);
var settings = new ColoredShapePipelineSettings();
settings.hsvHue.set(0, 100);
settings.hsvSaturation.set(100, 255);
settings.hsvValue.set(100, 255);
settings.outputShouldDraw = true;
settings.outputShowMultipleTargets = true;
settings.contourGroupingMode = ContourGroupingMode.Single;
settings.contourIntersection = ContourIntersectionDirection.Up;
settings.desiredShape = ContourShape.Triangle;
settings.allowableThreshold = 10;
settings.accuracyPercentage = 30.0;
ColoredShapePipeline pipeline = new ColoredShapePipeline();
testTriangleDetection(pipeline, settings, frameProvider.get());
testQuadrilateralDetection(pipeline, settings, frameProvider.get());
testCustomShapeDetection(pipeline, settings, frameProvider.get());
testCircleShapeDetection(pipeline, settings, frameProvider.get());
testPowercellDetection(settings, pipeline);
}
private static void printTestResults(CVPipelineResult pipelineResult) {
double fps = 1000 / pipelineResult.getLatencyMillis();
System.out.print(
"Pipeline ran in " + pipelineResult.getLatencyMillis() + "ms (" + fps + " fps), ");
System.out.println("Found " + pipelineResult.targets.size() + " valid targets");
}
}

View File

@@ -1,42 +0,0 @@
/*
* Copyright (C) Photon Vision.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.photonvision.vision.pipeline;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class PipelineProfilerTest {
@Test
public void reflectiveProfile() {
long[] invalidNanos = new long[20];
long[] validNanos = new long[PipelineProfiler.ReflectivePipeCount];
for (int i = 0; i < validNanos.length; i++) {
validNanos[i] = (long) (i * 1e+6); // fill data
}
var invalidResult = PipelineProfiler.getReflectiveProfileString(invalidNanos);
var validResult = PipelineProfiler.getReflectiveProfileString(validNanos);
System.out.println(validResult);
Assertions.assertEquals("Invalid data", invalidResult);
Assertions.assertTrue(validResult.contains("Total: 45.0ms"));
}
}

View File

@@ -1,124 +0,0 @@
/*
* Copyright (C) Photon Vision.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.photonvision.vision.pipeline;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.photonvision.common.util.TestUtils;
import org.photonvision.vision.camera.QuirkyCamera;
import org.photonvision.vision.frame.Frame;
import org.photonvision.vision.frame.provider.FileFrameProvider;
import org.photonvision.vision.opencv.CVMat;
import org.photonvision.vision.opencv.ContourGroupingMode;
import org.photonvision.vision.opencv.ContourIntersectionDirection;
import org.photonvision.vision.pipeline.result.CVPipelineResult;
public class ReflectivePipelineTest {
@Test
public void test2019() {
TestUtils.loadLibraries();
var pipeline = new ReflectivePipeline();
pipeline.getSettings().hsvHue.set(60, 100);
pipeline.getSettings().hsvSaturation.set(100, 255);
pipeline.getSettings().hsvValue.set(190, 255);
pipeline.getSettings().outputShouldDraw = true;
pipeline.getSettings().outputShowMultipleTargets = true;
pipeline.getSettings().contourGroupingMode = ContourGroupingMode.Dual;
pipeline.getSettings().contourIntersection = ContourIntersectionDirection.Up;
var frameProvider =
new FileFrameProvider(
TestUtils.getWPIImagePath(TestUtils.WPI2019Image.kCargoStraightDark72in_HighRes, false),
TestUtils.WPI2019Image.FOV);
TestUtils.showImage(frameProvider.get().image.getMat(), "Pipeline input", 1);
CVPipelineResult pipelineResult;
pipelineResult = pipeline.run(frameProvider.get(), QuirkyCamera.DefaultCamera);
printTestResults(pipelineResult);
Assertions.assertTrue(pipelineResult.hasTargets());
Assertions.assertEquals(2, pipelineResult.targets.size(), "Target count wrong!");
TestUtils.showImage(pipelineResult.outputFrame.image.getMat(), "Pipeline output");
}
@Test
public void test2020() {
TestUtils.loadLibraries();
var pipeline = new ReflectivePipeline();
pipeline.getSettings().hsvHue.set(60, 100);
pipeline.getSettings().hsvSaturation.set(200, 255);
pipeline.getSettings().hsvValue.set(200, 255);
pipeline.getSettings().outputShouldDraw = true;
var frameProvider =
new FileFrameProvider(
TestUtils.getWPIImagePath(TestUtils.WPI2020Image.kBlueGoal_108in_Center, false),
TestUtils.WPI2020Image.FOV);
CVPipelineResult pipelineResult = pipeline.run(frameProvider.get(), QuirkyCamera.DefaultCamera);
printTestResults(pipelineResult);
TestUtils.showImage(pipelineResult.outputFrame.image.getMat(), "Pipeline output");
}
private static void continuouslyRunPipeline(Frame frame, ReflectivePipelineSettings settings) {
var pipeline = new ReflectivePipeline();
while (true) {
CVPipelineResult pipelineResult = pipeline.run(frame, QuirkyCamera.DefaultCamera);
printTestResults(pipelineResult);
int preRelease = CVMat.getMatCount();
pipelineResult.release();
int postRelease = CVMat.getMatCount();
System.out.printf("Pre: %d, Post: %d\n", preRelease, postRelease);
}
}
// used to run VisualVM for profiling. It won't run on unit tests.
public static void main(String[] args) {
TestUtils.loadLibraries();
var frameProvider =
new FileFrameProvider(
TestUtils.getWPIImagePath(TestUtils.WPI2019Image.kCargoStraightDark72in_HighRes, false),
TestUtils.WPI2019Image.FOV);
var settings = new ReflectivePipelineSettings();
settings.hsvHue.set(60, 100);
settings.hsvSaturation.set(100, 255);
settings.hsvValue.set(190, 255);
settings.outputShouldDraw = true;
settings.outputShowMultipleTargets = true;
settings.contourGroupingMode = ContourGroupingMode.Dual;
settings.contourIntersection = ContourIntersectionDirection.Up;
continuouslyRunPipeline(frameProvider.get(), settings);
}
private static void printTestResults(CVPipelineResult pipelineResult) {
double fps = 1000 / pipelineResult.getLatencyMillis();
System.out.print(
"Pipeline ran in " + pipelineResult.getLatencyMillis() + "ms (" + fps + " fps), ");
System.out.println("Found " + pipelineResult.targets.size() + " valid targets");
}
}

View File

@@ -1,204 +0,0 @@
/*
* Copyright (C) Photon Vision.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.photonvision.vision.pipeline;
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.camera.QuirkyCamera;
import org.photonvision.vision.frame.Frame;
import org.photonvision.vision.frame.provider.FileFrameProvider;
import org.photonvision.vision.opencv.CVMat;
import org.photonvision.vision.opencv.ContourGroupingMode;
import org.photonvision.vision.opencv.ContourIntersectionDirection;
import org.photonvision.vision.pipeline.result.CVPipelineResult;
import org.photonvision.vision.target.TargetModel;
import org.photonvision.vision.target.TrackedTarget;
public class SolvePNPTest {
private static final String LIFECAM_240P_CAL_FILE = "lifecam240p.json";
private static final String LIFECAM_480P_CAL_FILE = "lifecam480p.json";
@BeforeEach
public void Init() {
TestUtils.loadLibraries();
}
@Test
public void loadCameraIntrinsics() {
var lifecam240pCal = getCoeffs(LIFECAM_240P_CAL_FILE);
var lifecam480pCal = getCoeffs(LIFECAM_480P_CAL_FILE);
assertNotNull(lifecam240pCal);
checkCameraCoefficients(lifecam240pCal);
assertNotNull(lifecam480pCal);
checkCameraCoefficients(lifecam480pCal);
}
private CameraCalibrationCoefficients getCoeffs(String filename) {
var cameraCalibration = TestUtils.getCoeffs(filename, true);
checkCameraCoefficients(cameraCalibration);
return cameraCalibration;
}
private void checkCameraCoefficients(CameraCalibrationCoefficients cameraCalibration) {
assertNotNull(cameraCalibration);
assertEquals(3, cameraCalibration.cameraIntrinsics.rows);
assertEquals(3, cameraCalibration.cameraIntrinsics.cols);
assertEquals(3, cameraCalibration.cameraIntrinsics.getAsMat().rows());
assertEquals(3, cameraCalibration.cameraIntrinsics.getAsMat().cols());
assertEquals(3, cameraCalibration.cameraIntrinsics.getAsMatOfDouble().rows());
assertEquals(3, cameraCalibration.cameraIntrinsics.getAsMatOfDouble().cols());
assertEquals(3, cameraCalibration.getCameraIntrinsicsMat().rows());
assertEquals(3, cameraCalibration.getCameraIntrinsicsMat().cols());
assertEquals(1, cameraCalibration.cameraExtrinsics.rows);
assertEquals(5, cameraCalibration.cameraExtrinsics.cols);
assertEquals(1, cameraCalibration.cameraExtrinsics.getAsMat().rows());
assertEquals(5, cameraCalibration.cameraExtrinsics.getAsMat().cols());
assertEquals(1, cameraCalibration.cameraExtrinsics.getAsMatOfDouble().rows());
assertEquals(5, cameraCalibration.cameraExtrinsics.getAsMatOfDouble().cols());
assertEquals(1, cameraCalibration.getCameraExtrinsicsMat().rows());
assertEquals(5, cameraCalibration.getCameraExtrinsicsMat().cols());
}
@Test
public void test2019() {
var pipeline = new ReflectivePipeline();
pipeline.getSettings().hsvHue.set(60, 100);
pipeline.getSettings().hsvSaturation.set(100, 255);
pipeline.getSettings().hsvValue.set(190, 255);
pipeline.getSettings().outputShouldDraw = true;
pipeline.getSettings().outputShowMultipleTargets = true;
pipeline.getSettings().solvePNPEnabled = true;
pipeline.getSettings().contourGroupingMode = ContourGroupingMode.Dual;
pipeline.getSettings().contourIntersection = ContourIntersectionDirection.Up;
pipeline.getSettings().cornerDetectionUseConvexHulls = true;
pipeline.getSettings().targetModel = TargetModel.k2019DualTarget;
var frameProvider =
new FileFrameProvider(
TestUtils.getWPIImagePath(TestUtils.WPI2019Image.kCargoStraightDark48in, false),
TestUtils.WPI2019Image.FOV,
new Rotation2d(),
TestUtils.get2019LifeCamCoeffs(true));
CVPipelineResult pipelineResult;
pipelineResult = pipeline.run(frameProvider.get(), QuirkyCamera.DefaultCamera);
printTestResults(pipelineResult);
// these numbers are not *accurate*, but they are known and expected
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);
Imgcodecs.imwrite("D:\\out.jpg", pipelineResult.outputFrame.image.getMat());
TestUtils.showImage(pipelineResult.outputFrame.image.getMat(), "Pipeline output", 999999);
}
@Test
public void test2020() {
var pipeline = new ReflectivePipeline();
pipeline.getSettings().hsvHue.set(60, 100);
pipeline.getSettings().hsvSaturation.set(100, 255);
pipeline.getSettings().hsvValue.set(60, 255);
pipeline.getSettings().outputShouldDraw = true;
pipeline.getSettings().solvePNPEnabled = true;
pipeline.getSettings().cornerDetectionAccuracyPercentage = 4;
pipeline.getSettings().cornerDetectionUseConvexHulls = true;
pipeline.getSettings().targetModel = TargetModel.k2020HighGoalOuter;
var frameProvider =
new FileFrameProvider(
TestUtils.getWPIImagePath(TestUtils.WPI2020Image.kBlueGoal_224in_Left, false),
TestUtils.WPI2020Image.FOV,
new Rotation2d(),
TestUtils.get2020LifeCamCoeffs(true));
CVPipelineResult pipelineResult = pipeline.run(frameProvider.get(), QuirkyCamera.DefaultCamera);
printTestResults(pipelineResult);
// these numbers are not *accurate*, but they are known and expected
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);
}
private static void continuouslyRunPipeline(Frame frame, ReflectivePipelineSettings settings) {
var pipeline = new ReflectivePipeline();
pipeline.settings = settings;
while (true) {
CVPipelineResult pipelineResult = pipeline.run(frame, QuirkyCamera.DefaultCamera);
printTestResults(pipelineResult);
int preRelease = CVMat.getMatCount();
pipelineResult.release();
int postRelease = CVMat.getMatCount();
System.out.printf("Pre: %d, Post: %d\n", preRelease, postRelease);
}
}
// used to run VisualVM for profiling, which won't run on unit tests.
public static void main(String[] args) {
TestUtils.loadLibraries();
var frameProvider =
new FileFrameProvider(
TestUtils.getWPIImagePath(TestUtils.WPI2019Image.kCargoStraightDark72in_HighRes, false),
TestUtils.WPI2019Image.FOV);
var settings = new ReflectivePipelineSettings();
settings.hsvHue.set(60, 100);
settings.hsvSaturation.set(100, 255);
settings.hsvValue.set(190, 255);
settings.outputShouldDraw = true;
settings.outputShowMultipleTargets = true;
settings.contourGroupingMode = ContourGroupingMode.Dual;
settings.contourIntersection = ContourIntersectionDirection.Up;
continuouslyRunPipeline(frameProvider.get(), settings);
}
private static void printTestResults(CVPipelineResult pipelineResult) {
double fps = 1000 / pipelineResult.getLatencyMillis();
System.out.println(
"Pipeline ran in " + pipelineResult.getLatencyMillis() + "ms (" + fps + " " + "fps)");
System.out.println("Found " + pipelineResult.targets.size() + " valid targets");
System.out.println(
"Found targets at "
+ pipelineResult.targets.stream()
.map(TrackedTarget::getCameraToTarget)
.collect(Collectors.toList()));
}
}