mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-20 00:51:41 +00:00
Fix calibration modal bug, save calibration images (#165)
Saves calibration images to photonvision_config/calibImgs
This commit is contained in:
@@ -361,6 +361,10 @@ public class ConfigManager {
|
||||
return Path.of(configDirectoryFile.toString(), "logs");
|
||||
}
|
||||
|
||||
public Path getCalibDir() {
|
||||
return Path.of(configDirectoryFile.toString(), "calibImgs");
|
||||
}
|
||||
|
||||
public static final String LOG_PREFIX = "photonvision-";
|
||||
public static final String LOG_EXT = ".log";
|
||||
public static final String LOG_DATE_TIME_FORMAT = "yyyy-M-d_hh-mm-ss";
|
||||
|
||||
@@ -81,10 +81,17 @@ public class Calibrate3dPipe
|
||||
try {
|
||||
// FindBoardCorners pipe outputs all the image points, object points, and frames to calculate
|
||||
// imageSize from, other parameters are output Mats
|
||||
|
||||
var objPoints = in.stream().map(Triple::getMiddle).collect(Collectors.toList());
|
||||
var imgPts = in.stream().map(Triple::getRight).collect(Collectors.toList());
|
||||
if (objPoints.size() != imgPts.size()) {
|
||||
logger.error("objpts.size != imgpts.size");
|
||||
return null;
|
||||
}
|
||||
calibrationAccuracy =
|
||||
Calib3d.calibrateCameraExtended(
|
||||
in.stream().map(Triple::getMiddle).collect(Collectors.toList()),
|
||||
in.stream().map(Triple::getRight).collect(Collectors.toList()),
|
||||
objPoints,
|
||||
imgPts,
|
||||
new Size(in.get(0).getLeft().width, in.get(0).getLeft().height),
|
||||
cameraMatrix,
|
||||
distortionCoefficients,
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package org.photonvision.vision.pipe.impl;
|
||||
|
||||
import java.util.Objects;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
import org.opencv.calib3d.Calib3d;
|
||||
import org.opencv.core.*;
|
||||
@@ -28,7 +29,8 @@ import org.photonvision.vision.pipe.CVPipe;
|
||||
import org.photonvision.vision.pipeline.UICalibrationData;
|
||||
|
||||
public class FindBoardCornersPipe
|
||||
extends CVPipe<Mat, Triple<Size, Mat, Mat>, FindBoardCornersPipe.FindCornersPipeParams> {
|
||||
extends CVPipe<
|
||||
Pair<Mat, Mat>, Triple<Size, Mat, Mat>, FindBoardCornersPipe.FindCornersPipeParams> {
|
||||
private static final Logger logger =
|
||||
new Logger(FindBoardCornersPipe.class, LogGroup.VisionModule);
|
||||
|
||||
@@ -91,11 +93,11 @@ public class FindBoardCornersPipe
|
||||
/**
|
||||
* Finds the corners in a given image and returns them
|
||||
*
|
||||
* @param in Input for pipe processing.
|
||||
* @param in Input for pipe processing. Pair of input and output mat
|
||||
* @return All valid Mats for camera calibration
|
||||
*/
|
||||
@Override
|
||||
protected Triple<Size, Mat, Mat> process(Mat in) {
|
||||
protected Triple<Size, Mat, Mat> process(Pair<Mat, Mat> in) {
|
||||
|
||||
// Create the object points
|
||||
createObjectPoints();
|
||||
@@ -103,43 +105,50 @@ public class FindBoardCornersPipe
|
||||
return findBoardCorners(in);
|
||||
}
|
||||
|
||||
private Triple<Size, Mat, Mat> findBoardCorners(Mat frame) {
|
||||
/**
|
||||
* Find chessboard corners given a input mat and output mat to draw on
|
||||
*
|
||||
* @return Frame resolution, object points, board corners
|
||||
*/
|
||||
private Triple<Size, Mat, Mat> findBoardCorners(Pair<Mat, Mat> in) {
|
||||
createObjectPoints();
|
||||
|
||||
// Convert the frame to grayscale to increase contrast
|
||||
Imgproc.cvtColor(frame, frame, Imgproc.COLOR_BGR2GRAY);
|
||||
var inFrame = in.getLeft();
|
||||
var outFrame = in.getRight();
|
||||
|
||||
// Convert the inFrame to grayscale to increase contrast
|
||||
Imgproc.cvtColor(inFrame, inFrame, Imgproc.COLOR_BGR2GRAY);
|
||||
boolean boardFound = false;
|
||||
|
||||
if (params.type == UICalibrationData.BoardType.CHESSBOARD) {
|
||||
// This is for chessboards
|
||||
boardFound = Calib3d.findChessboardCorners(frame, patternSize, boardCorners);
|
||||
boardFound = Calib3d.findChessboardCorners(inFrame, patternSize, boardCorners);
|
||||
} else if (params.type == UICalibrationData.BoardType.DOTBOARD) {
|
||||
// For dot boards
|
||||
boardFound =
|
||||
Calib3d.findCirclesGrid(
|
||||
frame, patternSize, boardCorners, Calib3d.CALIB_CB_ASYMMETRIC_GRID);
|
||||
inFrame, patternSize, boardCorners, Calib3d.CALIB_CB_ASYMMETRIC_GRID);
|
||||
}
|
||||
|
||||
if (!boardFound) {
|
||||
// If we can't find a chessboard/dot board, convert the frame back to BGR and return false.
|
||||
Imgproc.cvtColor(frame, frame, Imgproc.COLOR_GRAY2BGR);
|
||||
// If we can't find a chessboard/dot board, convert the inFrame back to BGR and return false.
|
||||
|
||||
return null;
|
||||
}
|
||||
var outBoardCorners = new MatOfPoint2f();
|
||||
boardCorners.copyTo(outBoardCorners);
|
||||
|
||||
// Get the size of the frame
|
||||
this.imageSize = new Size(frame.width(), frame.height());
|
||||
// Get the size of the inFrame
|
||||
this.imageSize = new Size(inFrame.width(), inFrame.height());
|
||||
|
||||
// Do sub corner pix for drawing chessboard
|
||||
Imgproc.cornerSubPix(frame, outBoardCorners, windowSize, zeroZone, criteria);
|
||||
Imgproc.cornerSubPix(inFrame, outBoardCorners, windowSize, zeroZone, criteria);
|
||||
|
||||
// convert back to BGR
|
||||
Imgproc.cvtColor(frame, frame, Imgproc.COLOR_GRAY2BGR);
|
||||
// Imgproc.cvtColor(inFrame, inFrame, Imgproc.COLOR_GRAY2BGR);
|
||||
// draw the chessboard, doesn't have to be different for a dot board since it just re projects
|
||||
// the corners we found
|
||||
Calib3d.drawChessboardCorners(frame, patternSize, outBoardCorners, true);
|
||||
Calib3d.drawChessboardCorners(outFrame, patternSize, outBoardCorners, true);
|
||||
|
||||
// // Add the 3D points and the points of the corners found
|
||||
// if (addToSnapList) {
|
||||
@@ -147,7 +156,7 @@ public class FindBoardCornersPipe
|
||||
// this.listOfImagePoints.add(boardCorners);
|
||||
// }
|
||||
|
||||
return Triple.of(frame.size(), objectPoints, outBoardCorners);
|
||||
return Triple.of(inFrame.size(), objectPoints, outBoardCorners);
|
||||
}
|
||||
|
||||
public static class FindCornersPipeParams {
|
||||
|
||||
@@ -19,15 +19,20 @@ package org.photonvision.vision.pipeline;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import edu.wpi.first.wpilibj.util.Units;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.Size;
|
||||
import org.opencv.imgcodecs.Imgcodecs;
|
||||
import org.photonvision.common.configuration.ConfigManager;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
import org.photonvision.common.util.SerializationUtils;
|
||||
import org.photonvision.common.util.file.FileUtils;
|
||||
import org.photonvision.common.util.math.MathUtils;
|
||||
import org.photonvision.server.SocketHandler;
|
||||
import org.photonvision.vision.calibration.CameraCalibrationCoefficients;
|
||||
@@ -62,6 +67,9 @@ public class Calibrate3dPipeline
|
||||
|
||||
private boolean calibrating = false;
|
||||
|
||||
// Path to save images
|
||||
private final Path imageDir = ConfigManager.getInstance().getCalibDir();
|
||||
|
||||
public Calibrate3dPipeline() {
|
||||
this(25);
|
||||
}
|
||||
@@ -99,7 +107,9 @@ public class Calibrate3dPipeline
|
||||
long sumPipeNanosElapsed = 0L;
|
||||
|
||||
// Check if the frame has chessboard corners
|
||||
var findBoardResult = findBoardCornersPipe.run(frame.image.getMat()).output;
|
||||
var outFrame = new Mat();
|
||||
frame.image.getMat().copyTo(outFrame);
|
||||
var findBoardResult = findBoardCornersPipe.run(Pair.of(frame.image.getMat(), outFrame)).output;
|
||||
|
||||
if (takeSnapshot) {
|
||||
// Set snapshot to false even if we don't find a board
|
||||
@@ -107,6 +117,9 @@ public class Calibrate3dPipeline
|
||||
|
||||
if (findBoardResult != null) {
|
||||
foundCornersList.add(findBoardResult);
|
||||
Imgcodecs.imwrite(
|
||||
Path.of(imageDir.toString(), "img" + foundCornersList.size() + ".jpg").toString(),
|
||||
frame.image.getMat());
|
||||
|
||||
// update the UI
|
||||
broadcastState();
|
||||
@@ -120,7 +133,13 @@ public class Calibrate3dPipeline
|
||||
return new CVPipelineResult(
|
||||
MathUtils.nanosToMillis(sumPipeNanosElapsed),
|
||||
null,
|
||||
new Frame(new CVMat(frame.image.getMat()), frame.frameStaticProperties));
|
||||
new Frame(new CVMat(outFrame), frame.frameStaticProperties));
|
||||
}
|
||||
|
||||
public void deleteSavedImages() {
|
||||
imageDir.toFile().mkdirs();
|
||||
imageDir.toFile().mkdir();
|
||||
FileUtils.deleteDirectory(imageDir);
|
||||
}
|
||||
|
||||
public boolean hasEnough() {
|
||||
|
||||
@@ -206,6 +206,7 @@ public class VisionModule {
|
||||
|
||||
public void startCalibration(UICalibrationData data) {
|
||||
var settings = pipelineManager.calibration3dPipeline.getSettings();
|
||||
pipelineManager.calibration3dPipeline.deleteSavedImages();
|
||||
settings.cameraVideoModeIndex = data.videoModeIndex;
|
||||
visionSource.getSettables().setVideoModeIndex(data.videoModeIndex);
|
||||
logger.info(
|
||||
@@ -223,7 +224,7 @@ public class VisionModule {
|
||||
settings.cameraGain = -1;
|
||||
}
|
||||
|
||||
pipelineManager.setCalibrationMode(true);
|
||||
setPipeline(PipelineManager.CAL_3D_INDEX);
|
||||
}
|
||||
|
||||
public void takeCalibrationSnapshot() {
|
||||
|
||||
Reference in New Issue
Block a user