Skip to content

Commit

Permalink
Remove separate charuco pipe, Add simple Charuco calibration sheetdow…
Browse files Browse the repository at this point in the history
…nload, Remove DotBoard.
  • Loading branch information
BytingBulldogs3539 committed May 7, 2024
1 parent 52b62d4 commit b8bbe9b
Show file tree
Hide file tree
Showing 44 changed files with 143 additions and 255 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 17 additions & 20 deletions photon-client/src/components/cameras/CameraCalibrationCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { CalibrationBoardTypes, type VideoFormat } from "@/types/SettingTypes";
import JsPDF from "jspdf";
import { font as PromptRegular } from "@/assets/fonts/PromptRegular";
import MonoLogo from "@/assets/images/logoMono.png";
import CharucoImage from "@/assets/images/ChArUco_Marker8x8.png";
import PvSlider from "@/components/common/pv-slider.vue";
import { useStateStore } from "@/stores/StateStore";
import PvSwitch from "@/components/common/pv-switch.vue";
Expand Down Expand Up @@ -47,7 +48,7 @@ const getUniqueVideoFormatsByResolution = (): VideoFormat[] => {
(a, b) => b.resolution.width + b.resolution.height - (a.resolution.width + a.resolution.height)
);
useStateStore().calibrationData.videoFormatIndex = uniqueResolutions[uniqueResolutions.length - 1].index;
useStateStore().calibrationData.videoFormatIndex = uniqueResolutions[0].index;
return uniqueResolutions;
};
Expand Down Expand Up @@ -107,22 +108,23 @@ const downloadCalibBoard = () => {
}
}
}
doc.text(`${patternWidth.value} x ${patternHeight.value} | ${squareSizeIn.value}in`, paperWidth - 1, 1.0, {
maxWidth: (paperWidth - 2.0) / 2,
align: "right"
});
break;
case CalibrationBoardTypes.DotBoard:
// eslint-disable-next-line no-case-declarations
const dotgridStartX =
(paperWidth - (2 * (patternWidth.value - 1) + ((patternHeight.value - 1) % 2)) * squareSizeIn.value) / 2.0;
// eslint-disable-next-line no-case-declarations
const dotgridStartY = (paperHeight - (patternHeight.value - squareSizeIn.value)) / 2;
for (let squareY = 0; squareY < patternHeight.value; squareY++) {
for (let squareX = 0; squareX < patternWidth.value; squareX++) {
const xPos = dotgridStartX + (2 * squareX + (squareY % 2)) * squareSizeIn.value;
const yPos = dotgridStartY + squareY * squareSizeIn.value;
case CalibrationBoardTypes.Charuco:
// Add pregenerated charuco
const charucoImage = new Image();
charucoImage.src = CharucoImage;
doc.addImage(charucoImage, "PNG", 0.25, 1.50, 8, 8);
doc.text(`8 x 8 | 1in & 0.75in`, paperWidth - 1, 1.0, {
maxWidth: (paperWidth - 2.0) / 2,
align: "right"
});
doc.circle(xPos, yPos, squareSizeIn.value / 4, "F");
}
}
break;
}
Expand All @@ -144,11 +146,6 @@ const downloadCalibBoard = () => {
logoImage.src = MonoLogo;
doc.addImage(logoImage, "PNG", 1.0, 0.75, 1.4, 0.5);
doc.text(`${patternWidth.value} x ${patternHeight.value} | ${squareSizeIn.value}in`, paperWidth - 1, 1.0, {
maxWidth: (paperWidth - 2.0) / 2,
align: "right"
});
doc.save(`calibrationTarget-${CalibrationBoardTypes[boardType.value]}.pdf`);
};
Expand Down Expand Up @@ -292,7 +289,7 @@ const setSelectedVideoFormat = (format: VideoFormat) => {
label="Board Type"
tooltip="Calibration board pattern to use"
:select-cols="7"
:items="['Chessboard', 'Dotboard', 'Charuco']"
:items="['Chessboard', 'Charuco']"
:disabled="isCalibrating"
/>
<pv-number-input
Expand Down
3 changes: 1 addition & 2 deletions photon-client/src/types/SettingTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,7 @@ export const PlaceholderCameraSettings: CameraSettings = {

export enum CalibrationBoardTypes {
Chessboard = 0,
DotBoard = 1,
Charuco = 2
Charuco = 1
}

export enum RobotOffsetType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -340,10 +340,6 @@ public static Path getPowercellImagePath(PowercellTestImages image, boolean test
return getPowercellPath(testMode).resolve(image.path);
}

public static Path getDotBoardImagesPath() {
return getResourcesFolderPath(false).resolve("calibrationBoardImages");
}

public static Path getSquaresBoardImagesPath() {
return getResourcesFolderPath(false).resolve("calibrationSquaresImg");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@

package org.photonvision.vision.pipe.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import org.opencv.calib3d.Calib3d;
import org.opencv.core.*;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CharucoBoard;
import org.opencv.objdetect.CharucoDetector;
import org.opencv.objdetect.Objdetect;
import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
import org.photonvision.vision.frame.FrameDivisor;
Expand All @@ -42,6 +47,9 @@ public class FindBoardCornersPipe
Size imageSize;
Size patternSize;

CharucoBoard board;
CharucoDetector detector;

// Configure the optimizations used while using OpenCV's find corners algorithm
// Since we return results in real-time, we want to ensure it goes as fast as
// possible
Expand Down Expand Up @@ -99,17 +107,14 @@ public void createObjectPoints() {
objectPoints.push_back(new MatOfPoint3f(new Point3(boardXCoord, boardYCoord, 0.0)));
}
}
} else if (params.type == UICalibrationData.BoardType.DOTBOARD) {
// Here we need to alternate the amount of dots per column since a dot board is
// not
// rectangular and also by taking in account the grid size which should be in mm
for (int i = 0; i < patternSize.height; i++) {
for (int j = 0; j < patternSize.width; j++) {
objectPoints.push_back(
new MatOfPoint3f(
new Point3((2 * j + i % 2) * params.gridSize, i * params.gridSize, 0.0d)));
}
}
} else if (params.type == UICalibrationData.BoardType.CHARUCOBOARD) {
board =
new CharucoBoard(
new Size(params.boardWidth, params.boardHeight),
(float) params.gridSize,
(float) params.markerSize,
Objdetect.getPredefinedDictionary(params.tagFamily));
detector = new CharucoDetector(board);
} else {
logger.error("Can't create pattern for unknown board type " + params.type);
}
Expand Down Expand Up @@ -227,14 +232,89 @@ private Size getWindowSize(MatOfPoint2f inPoints) {
private FindBoardCornersPipeResult findBoardCorners(Pair<Mat, Mat> in) {
createObjectPoints();

float[] levels = null;
var outLevels = new MatOfFloat();

var objPts = new MatOfPoint3f();
var outBoardCorners = new MatOfPoint2f();

var inFrame = in.getLeft();
var outFrame = in.getRight();

// Convert the inFrame too grayscale to increase contrast
Imgproc.cvtColor(inFrame, inFrame, Imgproc.COLOR_BGR2GRAY);
boolean boardFound = false;

if (params.type == UICalibrationData.BoardType.CHESSBOARD) {
// Get the size of the inFrame
this.imageSize = new Size(inFrame.width(), inFrame.height());

if (params.type == UICalibrationData.BoardType.CHARUCOBOARD) {
Mat ObjPoints =
new Mat(); // 3 dimensional currentObjectPoints, the physical target ChArUco Board
Mat imgPoints =
new Mat(); // 2 dimensional currentImagePoints, the likely distorted board on the flat
// camera sensor frame posed relative to the target
Mat detectedCorners = new Mat(); // currentCharucoCorners
Mat detectedIds = new Mat(); // currentCharucoIds
detector.detectBoard(inFrame, detectedCorners, detectedIds);

// reformat the Mat to a List<Mat> for matchImagePoints
final List<Mat> detectedCornersList = new ArrayList<>();
for (int i = 0; i < detectedCorners.total(); i++) {
detectedCornersList.add(detectedCorners.row(i));
}
if (detectedCornersList.size() > 0) {
boardFound = true;
}

if (!boardFound) {
// If we can't find a board, give up
return null;
}
board.matchImagePoints(detectedCornersList, detectedIds, ObjPoints, imgPoints);

// draw the charuco board
Objdetect.drawDetectedCornersCharuco(
outFrame, detectedCorners, detectedIds, new Scalar(0, 0, 255)); // Red Text

if (!boardFound) {
// If we can't find a charuco board, give up
return null;
}

imgPoints.copyTo(outBoardCorners);
ObjPoints.copyTo(objPts);

if (params.useMrCal) {
Point[] boardCorners =
new Point[(this.params.boardHeight - 1) * (this.params.boardWidth - 1)];
Point3[] objectPoints =
new Point3[(this.params.boardHeight - 1) * (this.params.boardWidth - 1)];
levels = new float[(this.params.boardHeight - 1) * (this.params.boardWidth - 1)];

for (int i = 0; i < detectedIds.total(); i++) {
int id = (int) detectedIds.get(i, 0)[0];
boardCorners[id] = outBoardCorners.toList().get(i);
objectPoints[id] = objPts.toList().get(i);
levels[id] = 1.0f;
}
for (int i = 0; i < boardCorners.length; i++) {
if (boardCorners[i] == null) {
boardCorners[i] = new Point(-1, -1);
objectPoints[i] = new Point3(-1, -1, -1);
levels[i] = -1.0f;
}
}

outBoardCorners.fromArray(boardCorners);
outLevels.fromArray(levels);
}
imgPoints.release();
ObjPoints.release();
detectedCorners.release();
detectedIds.release();

} else { // If not aruco then do chessboard
// Reduce the image size to be much more manageable
// Note that opencv will copy the frame if no resize is requested; we can skip
// this since we
Expand All @@ -256,39 +336,28 @@ private FindBoardCornersPipeResult findBoardCorners(Pair<Mat, Mat> in) {
rescalePointsToOrigFrame(smallerBoardCorners, inFrame, boardCorners);
}

} else if (params.type == UICalibrationData.BoardType.DOTBOARD) {
boardFound =
Calib3d.findCirclesGrid(
inFrame, patternSize, boardCorners, Calib3d.CALIB_CB_ASYMMETRIC_GRID);
boardCorners.copyTo(outBoardCorners);

objectPoints.copyTo(objPts);

// Do sub corner pix for drawing chessboard when using OpenCV
Imgproc.cornerSubPix(
inFrame, outBoardCorners, getWindowSize(outBoardCorners), zeroZone, criteria);

// draw the chessboard, doesn't have to be different for a dot board since it
// just re projects
// the corners we found
Calib3d.drawChessboardCorners(outFrame, patternSize, outBoardCorners, true);

levels = new float[(int) objPts.total()];
Arrays.fill(levels, 1.0f);
outLevels.fromArray(levels);
}
if (!boardFound) {
// If we can't find a chessboard/dot board, give up
return null;
}

var outBoardCorners = new MatOfPoint2f();
boardCorners.copyTo(outBoardCorners);

var objPts = new MatOfPoint3f();
objectPoints.copyTo(objPts);

// Get the size of the inFrame
this.imageSize = new Size(inFrame.width(), inFrame.height());

// Do sub corner pix for drawing chessboard when using OpenCV
Imgproc.cornerSubPix(
inFrame, outBoardCorners, getWindowSize(outBoardCorners), zeroZone, criteria);

// draw the chessboard, doesn't have to be different for a dot board since it
// just re projects
// the corners we found
Calib3d.drawChessboardCorners(outFrame, patternSize, outBoardCorners, true);

float[] levels = new float[(int) objPts.total()];
Arrays.fill(levels, 1.0f);
var outLevels = new MatOfFloat();
outLevels.fromArray(levels);

return new FindBoardCornersPipeResult(inFrame.size(), objPts, outBoardCorners, outLevels);
}

Expand Down
Loading

0 comments on commit b8bbe9b

Please sign in to comment.