mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-25 01:41:40 +00:00
Split photon-server and photon-core (#211)
Uses multiple Gradle projects to support the split.
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user