diff --git a/docs/source/docs/calibration/calibration.md b/docs/source/docs/calibration/calibration.md index 0de5c940f..c674e8c89 100644 --- a/docs/source/docs/calibration/calibration.md +++ b/docs/source/docs/calibration/calibration.md @@ -7,7 +7,7 @@ In order to detect AprilTags and use 3D mode, your camera must be calibrated at To calibrate a camera, images of a ChArUco board (or chessboard) are taken. By comparing where the grid corners should be in object space (for example, a corner once every inch in an 8x6 grid) with where they appear in the camera image, we can find a least-squares estimate for intrinsic camera properties like focal lengths, center point, and distortion coefficients. For more on camera calibration, please review the [OpenCV documentation](https://docs.opencv.org/4.x/dc/dbb/tutorial_py_calibration.html). :::{warning} -While any resolution can be calibrated, higher resolutions may be too performance-intensive for some coprocessors to handle. Therefore, we recommend experimenting to see what works best for your coprocessor. +PhotonVision supports many resolution configuration options, with only a minimum of 640x480 required. However, higher resolutions may be too performance-intensive for some coprocessors to handle. Therefore, we recommend experimenting to see what works best for your coprocessor. ::: :::{note} diff --git a/photon-client/src/components/cameras/CameraCalibrationCard.vue b/photon-client/src/components/cameras/CameraCalibrationCard.vue index 97e3c138b..d325fb008 100644 --- a/photon-client/src/components/cameras/CameraCalibrationCard.vue +++ b/photon-client/src/components/cameras/CameraCalibrationCard.vue @@ -38,6 +38,11 @@ const getUniqueVideoFormatsByResolution = (): VideoFormat[] => { if (!skip) { const calib = useCameraSettingsStore().getCalibrationCoeffs(format.resolution); + + // minPixelCount is the multiplied area of a 640x480 (the minimum for proper calibration) resolution + const minPixelCount = 640 * 480; + const resArea = format.resolution.width * format.resolution.height; + if (calib !== undefined) { // Mean overall reprojection error // Calculated as average of each observation's mean error @@ -60,7 +65,10 @@ const getUniqueVideoFormatsByResolution = (): VideoFormat[] => { ) * (180 / Math.PI); } - uniqueResolutions.push(format); + + if (resArea >= minPixelCount) { + uniqueResolutions.push(format); + } } }); uniqueResolutions.sort( @@ -86,12 +94,19 @@ const uniqueVideoResolutionString = ref(""); // Use a watchEffect so the value is populated/reacts when the stores become available or update. // This avoids trying to index into an array that may be empty during page reload. watchEffect(() => { - const currentIndex = useCameraSettingsStore().currentVideoFormat.index ?? 0; - useStateStore().calibrationData.videoFormatIndex = currentIndex; const names = useCameraSettingsStore().currentCameraSettings.validVideoFormats.map((f) => getResolutionString(f.resolution) ); - uniqueVideoResolutionString.value = names[currentIndex] ?? names[0] ?? ""; + const currentFormatIndex = useCameraSettingsStore().currentVideoFormat.index ?? 0; + // Checks if the current resolution is present in the list of valid formats, if not defaults to the last index (which is usually the highest resolution) + const currentIndex = + getUniqueVideoResolutionStrings() + .map((x) => x.name) + .find((n) => n === names[currentFormatIndex]) !== undefined + ? currentFormatIndex + : names.length - 1; + useStateStore().calibrationData.videoFormatIndex = currentIndex; + uniqueVideoResolutionString.value = names[currentIndex] ?? ""; }); const squareSizeIn = ref(1); const markerSizeIn = ref(0.75);