mirror of
https://github.com/PhotonVision/photonvision
synced 2026-07-02 02:51:40 +00:00
UI patches (#905)
- Show 0 clients when NT server props are undefined - Add Prettier --------- Co-authored-by: Matthew Morley <matthew.morley.ca@gmail.com>
This commit is contained in:
@@ -12,41 +12,60 @@ import CvSelect from "@/components/common/cv-select.vue";
|
||||
import CvNumberInput from "@/components/common/cv-number-input.vue";
|
||||
import { WebsocketPipelineType } from "@/types/WebsocketDataTypes";
|
||||
|
||||
|
||||
const settingsValid = ref(true);
|
||||
|
||||
const getCalibrationCoeffs = (resolution: Resolution) => {
|
||||
return useCameraSettingsStore().currentCameraSettings.completeCalibrations.find(cal => cal.resolution.width === resolution.width && cal.resolution.height === resolution.height);
|
||||
return useCameraSettingsStore().currentCameraSettings.completeCalibrations.find(
|
||||
(cal) => cal.resolution.width === resolution.width && cal.resolution.height === resolution.height
|
||||
);
|
||||
};
|
||||
const getUniqueVideoResolutions = (): VideoFormat[] => {
|
||||
const uniqueResolutions: VideoFormat[] = [];
|
||||
useCameraSettingsStore().currentCameraSettings.validVideoFormats.forEach((format, index) => {
|
||||
if(!uniqueResolutions.some(v => v.resolution.width === format.resolution.width && v.resolution.height === format.resolution.height)) {
|
||||
if (
|
||||
!uniqueResolutions.some(
|
||||
(v) => v.resolution.width === format.resolution.width && v.resolution.height === format.resolution.height
|
||||
)
|
||||
) {
|
||||
format.index = index;
|
||||
|
||||
const calib = getCalibrationCoeffs(format.resolution);
|
||||
if(calib !== undefined) {
|
||||
if (calib !== undefined) {
|
||||
format.standardDeviation = calib.standardDeviation;
|
||||
format.mean = calib.perViewErrors.reduce((a, b) => a + b) / calib.perViewErrors.length;
|
||||
format.horizontalFOV = 2 * Math.atan2(format.resolution.width/2, calib.intrinsics[0]) * (180/Math.PI);
|
||||
format.verticalFOV = 2 * Math.atan2(format.resolution.height/2, calib.intrinsics[4]) * (180/Math.PI);
|
||||
format.diagonalFOV = 2 * Math.atan2(Math.sqrt(format.resolution.width**2 + (format.resolution.height/(calib.intrinsics[4]/calib.intrinsics[0]))**2)/2, calib.intrinsics[0]) * (180/Math.PI);
|
||||
format.horizontalFOV = 2 * Math.atan2(format.resolution.width / 2, calib.intrinsics[0]) * (180 / Math.PI);
|
||||
format.verticalFOV = 2 * Math.atan2(format.resolution.height / 2, calib.intrinsics[4]) * (180 / Math.PI);
|
||||
format.diagonalFOV =
|
||||
2 *
|
||||
Math.atan2(
|
||||
Math.sqrt(
|
||||
format.resolution.width ** 2 +
|
||||
(format.resolution.height / (calib.intrinsics[4] / calib.intrinsics[0])) ** 2
|
||||
) / 2,
|
||||
calib.intrinsics[0]
|
||||
) *
|
||||
(180 / Math.PI);
|
||||
}
|
||||
uniqueResolutions.push(format);
|
||||
}
|
||||
});
|
||||
uniqueResolutions.sort((a, b) => (b.resolution.width + b.resolution.height) - (a.resolution.width + a.resolution.height));
|
||||
uniqueResolutions.sort(
|
||||
(a, b) => b.resolution.width + b.resolution.height - (a.resolution.width + a.resolution.height)
|
||||
);
|
||||
return uniqueResolutions;
|
||||
};
|
||||
const getUniqueVideoResolutionStrings = () => getUniqueVideoResolutions().map<{name: string, value: number}>(f => ({
|
||||
name: `${f.resolution.width} X ${f.resolution.height}`,
|
||||
// Index won't ever be undefined
|
||||
value: f.index || 0
|
||||
}));
|
||||
const calibrationDivisors = computed(() => [1, 2, 4].filter(v => {
|
||||
const currentRes = useCameraSettingsStore().currentVideoFormat.resolution;
|
||||
return ((currentRes.width / v) >= 300 && (currentRes.height / v) >= 220) || (v === 1);
|
||||
}));
|
||||
const getUniqueVideoResolutionStrings = () =>
|
||||
getUniqueVideoResolutions().map<{ name: string; value: number }>((f) => ({
|
||||
name: `${f.resolution.width} X ${f.resolution.height}`,
|
||||
// Index won't ever be undefined
|
||||
value: f.index || 0
|
||||
}));
|
||||
const calibrationDivisors = computed(() =>
|
||||
[1, 2, 4].filter((v) => {
|
||||
const currentRes = useCameraSettingsStore().currentVideoFormat.resolution;
|
||||
return (currentRes.width / v >= 300 && currentRes.height / v >= 220) || v === 1;
|
||||
})
|
||||
);
|
||||
|
||||
const squareSizeIn = ref(1);
|
||||
const patternWidth = ref(8);
|
||||
@@ -85,7 +104,8 @@ const downloadCalibBoard = () => {
|
||||
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;
|
||||
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;
|
||||
|
||||
@@ -118,12 +138,10 @@ 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.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`);
|
||||
};
|
||||
@@ -132,28 +150,29 @@ const importCalibrationFromCalibDB = ref();
|
||||
const openCalibUploadPrompt = () => {
|
||||
importCalibrationFromCalibDB.value.click();
|
||||
};
|
||||
const readImportedCalibration = ({ files } : { files: FileList}) => {
|
||||
files[0].text().then(text => {
|
||||
useCameraSettingsStore().importCalibDB({ payload: text, filename: files[0].name })
|
||||
.then((response) => {
|
||||
useStateStore().showSnackbarMessage({
|
||||
message: response.data.text || response.data,
|
||||
color: response.status === 200 ? "success" : "error"
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.request) {
|
||||
useStateStore().showSnackbarMessage({
|
||||
message: "Error while uploading calibration file! The backend didn't respond to the upload attempt.",
|
||||
color: "error"
|
||||
});
|
||||
} else {
|
||||
useStateStore().showSnackbarMessage({
|
||||
message: "Error while uploading calibration file!",
|
||||
color: "error"
|
||||
});
|
||||
}
|
||||
const readImportedCalibration = ({ files }: { files: FileList }) => {
|
||||
files[0].text().then((text) => {
|
||||
useCameraSettingsStore()
|
||||
.importCalibDB({ payload: text, filename: files[0].name })
|
||||
.then((response) => {
|
||||
useStateStore().showSnackbarMessage({
|
||||
message: response.data.text || response.data,
|
||||
color: response.status === 200 ? "success" : "error"
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err.request) {
|
||||
useStateStore().showSnackbarMessage({
|
||||
message: "Error while uploading calibration file! The backend didn't respond to the upload attempt.",
|
||||
color: "error"
|
||||
});
|
||||
} else {
|
||||
useStateStore().showSnackbarMessage({
|
||||
message: "Error while uploading calibration file!",
|
||||
color: "error"
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -174,13 +193,14 @@ const showCalibEndDialog = ref(false);
|
||||
const calibCanceled = ref(false);
|
||||
const calibSuccess = ref<boolean | undefined>(undefined);
|
||||
const endCalibration = () => {
|
||||
if(!useStateStore().calibrationData.hasEnoughImages) {
|
||||
if (!useStateStore().calibrationData.hasEnoughImages) {
|
||||
calibCanceled.value = true;
|
||||
}
|
||||
|
||||
showCalibEndDialog.value = true;
|
||||
// Check if calibration finished cleanly or was canceled
|
||||
useCameraSettingsStore().endPnPCalibration()
|
||||
useCameraSettingsStore()
|
||||
.endPnPCalibration()
|
||||
.then(() => {
|
||||
calibSuccess.value = true;
|
||||
})
|
||||
@@ -195,22 +215,12 @@ const endCalibration = () => {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<v-card
|
||||
class="pr-6 pb-3"
|
||||
color="primary"
|
||||
dark
|
||||
>
|
||||
<v-card class="pr-6 pb-3" color="primary" dark>
|
||||
<v-card-title>Camera Calibration</v-card-title>
|
||||
<div class="ml-5">
|
||||
<v-row>
|
||||
<v-col
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<v-form
|
||||
ref="form"
|
||||
v-model="settingsValid"
|
||||
>
|
||||
<v-col cols="12" md="6">
|
||||
<v-form ref="form" v-model="settingsValid">
|
||||
<cv-select
|
||||
v-model="useStateStore().calibrationData.videoFormatIndex"
|
||||
label="Resolution"
|
||||
@@ -225,7 +235,9 @@ const endCalibration = () => {
|
||||
tooltip="Resolution to which camera frames are downscaled for detection. Calibration still uses full-res"
|
||||
:items="calibrationDivisors"
|
||||
:select-cols="7"
|
||||
@input="v => useCameraSettingsStore().changeCurrentPipelineSetting({streamingFrameDivisor: v}, false)"
|
||||
@input="
|
||||
(v) => useCameraSettingsStore().changeCurrentPipelineSetting({ streamingFrameDivisor: v }, false)
|
||||
"
|
||||
/>
|
||||
<cv-select
|
||||
v-model="boardType"
|
||||
@@ -240,7 +252,7 @@ const endCalibration = () => {
|
||||
label="Pattern Spacing (in)"
|
||||
tooltip="Spacing between pattern features in inches"
|
||||
:disabled="isCalibrating"
|
||||
:rules="[v => (v > 0) || 'Size must be positive']"
|
||||
:rules="[(v) => v > 0 || 'Size must be positive']"
|
||||
:label-cols="5"
|
||||
/>
|
||||
<cv-number-input
|
||||
@@ -248,7 +260,7 @@ const endCalibration = () => {
|
||||
label="Board Width (in)"
|
||||
tooltip="Width of the board in dots or chessboard squares"
|
||||
:disabled="isCalibrating"
|
||||
:rules="[v => (v >= 4) || 'Width must be at least 4']"
|
||||
:rules="[(v) => v >= 4 || 'Width must be at least 4']"
|
||||
:label-cols="5"
|
||||
/>
|
||||
<cv-number-input
|
||||
@@ -256,54 +268,31 @@ const endCalibration = () => {
|
||||
label="Board Height (in)"
|
||||
tooltip="Height of the board in dots or chessboard squares"
|
||||
:disabled="isCalibrating"
|
||||
:rules="[v => (v >= 4) || 'Height must be at least 4']"
|
||||
:rules="[(v) => v >= 4 || 'Height must be at least 4']"
|
||||
:label-cols="5"
|
||||
/>
|
||||
</v-form>
|
||||
</v-col>
|
||||
<v-col
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<v-row
|
||||
align="start"
|
||||
class="pb-4 pt-2"
|
||||
>
|
||||
<v-simple-table
|
||||
fixed-header
|
||||
height="100%"
|
||||
dense
|
||||
>
|
||||
<v-col cols="12" md="6">
|
||||
<v-row align="start" class="pb-4 pt-2">
|
||||
<v-simple-table fixed-header height="100%" dense>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Resolution
|
||||
</th>
|
||||
<th>
|
||||
Mean Error
|
||||
</th>
|
||||
<th>
|
||||
Standard Deviation
|
||||
</th>
|
||||
<th>
|
||||
Horizontal FOV
|
||||
</th>
|
||||
<th>
|
||||
Vertical FOV
|
||||
</th>
|
||||
<th>
|
||||
Diagonal FOV
|
||||
</th>
|
||||
<th>Resolution</th>
|
||||
<th>Mean Error</th>
|
||||
<th>Standard Deviation</th>
|
||||
<th>Horizontal FOV</th>
|
||||
<th>Vertical FOV</th>
|
||||
<th>Diagonal FOV</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(value, index) in getUniqueVideoResolutions()"
|
||||
:key="index"
|
||||
>
|
||||
<tr v-for="(value, index) in getUniqueVideoResolutions()" :key="index">
|
||||
<td>{{ value.resolution.width }} X {{ value.resolution.height }}</td>
|
||||
<td>{{ value.mean !== undefined ? value.mean.toFixed(2) + "px" : "-" }}</td>
|
||||
<td>{{ value.standardDeviation !== undefined ? value.standardDeviation.toFixed(2) + "px" : "-" }}</td>
|
||||
<td>
|
||||
{{ value.standardDeviation !== undefined ? value.standardDeviation.toFixed(2) + "px" : "-" }}
|
||||
</td>
|
||||
<td>{{ value.horizontalFOV !== undefined ? value.horizontalFOV.toFixed(2) + "°" : "-" }}</td>
|
||||
<td>{{ value.verticalFOV !== undefined ? value.verticalFOV.toFixed(2) + "°" : "-" }}</td>
|
||||
<td>{{ value.diagonalFOV !== undefined ? value.diagonalFOV.toFixed(2) + "°" : "-" }}</td>
|
||||
@@ -317,16 +306,14 @@ const endCalibration = () => {
|
||||
label
|
||||
:color="useStateStore().calibrationData.hasEnoughImages ? 'secondary' : 'gray'"
|
||||
>
|
||||
Snapshots: {{ useStateStore().calibrationData.imageCount }} of at least {{ useStateStore().calibrationData.minimumImageCount }}
|
||||
Snapshots: {{ useStateStore().calibrationData.imageCount }} of at least
|
||||
{{ useStateStore().calibrationData.minimumImageCount }}
|
||||
</v-chip>
|
||||
</v-row>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="isCalibrating">
|
||||
<v-col
|
||||
cols="12"
|
||||
class="pt-0"
|
||||
>
|
||||
<v-col cols="12" class="pt-0">
|
||||
<cv-slider
|
||||
v-model="useCameraSettingsStore().currentPipelineSettings.cameraExposure"
|
||||
:disabled="useCameraSettingsStore().currentCameraSettings.pipelineSettings.cameraAutoExposure"
|
||||
@@ -336,7 +323,7 @@ const endCalibration = () => {
|
||||
:max="100"
|
||||
:slider-cols="8"
|
||||
:step="0.1"
|
||||
@input="args => useCameraSettingsStore().changeCurrentPipelineSetting({cameraExposure: args}, false)"
|
||||
@input="(args) => useCameraSettingsStore().changeCurrentPipelineSetting({ cameraExposure: args }, false)"
|
||||
/>
|
||||
<cv-slider
|
||||
v-model="useCameraSettingsStore().currentPipelineSettings.cameraBrightness"
|
||||
@@ -344,7 +331,9 @@ const endCalibration = () => {
|
||||
:min="0"
|
||||
:max="100"
|
||||
:slider-cols="8"
|
||||
@input="args => useCameraSettingsStore().changeCurrentPipelineSetting({cameraBrightness: args}, false)"
|
||||
@input="
|
||||
(args) => useCameraSettingsStore().changeCurrentPipelineSetting({ cameraBrightness: args }, false)
|
||||
"
|
||||
/>
|
||||
<cv-switch
|
||||
v-model="useCameraSettingsStore().currentPipelineSettings.cameraAutoExposure"
|
||||
@@ -352,7 +341,9 @@ const endCalibration = () => {
|
||||
label="Auto Exposure"
|
||||
:label-cols="4"
|
||||
tooltip="Enables or Disables camera automatic adjustment for current lighting conditions"
|
||||
@input="args => useCameraSettingsStore().changeCurrentPipelineSetting({cameraAutoExposure: args}, false)"
|
||||
@input="
|
||||
(args) => useCameraSettingsStore().changeCurrentPipelineSetting({ cameraAutoExposure: args }, false)
|
||||
"
|
||||
/>
|
||||
<cv-slider
|
||||
v-if="useCameraSettingsStore().currentPipelineSettings.cameraGain >= 0"
|
||||
@@ -361,7 +352,7 @@ const endCalibration = () => {
|
||||
tooltip="Controls camera gain, similar to brightness"
|
||||
:min="0"
|
||||
:max="100"
|
||||
@input="args => useCameraSettingsStore().changeCurrentPipelineSetting({cameraGain: args}, false)"
|
||||
@input="(args) => useCameraSettingsStore().changeCurrentPipelineSetting({ cameraGain: args }, false)"
|
||||
/>
|
||||
<cv-slider
|
||||
v-if="useCameraSettingsStore().currentPipelineSettings.cameraRedGain !== -1"
|
||||
@@ -370,7 +361,7 @@ const endCalibration = () => {
|
||||
:min="0"
|
||||
:max="100"
|
||||
tooltip="Controls red automatic white balance gain, which affects how the camera captures colors in different conditions"
|
||||
@input="args => useCameraSettingsStore().changeCurrentPipelineSetting({cameraRedGain: args}, false)"
|
||||
@input="(args) => useCameraSettingsStore().changeCurrentPipelineSetting({ cameraRedGain: args }, false)"
|
||||
/>
|
||||
<cv-slider
|
||||
v-if="useCameraSettingsStore().currentPipelineSettings.cameraBlueGain !== -1"
|
||||
@@ -379,7 +370,7 @@ const endCalibration = () => {
|
||||
:min="0"
|
||||
:max="100"
|
||||
tooltip="Controls blue automatic white balance gain, which affects how the camera captures colors in different conditions"
|
||||
@input="args => useCameraSettingsStore().changeCurrentPipelineSetting({cameraBlueGain: args}, false)"
|
||||
@input="(args) => useCameraSettingsStore().changeCurrentPipelineSetting({ cameraBlueGain: args }, false)"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
@@ -388,7 +379,7 @@ const endCalibration = () => {
|
||||
<v-btn
|
||||
small
|
||||
color="secondary"
|
||||
style="width: 100%;"
|
||||
style="width: 100%"
|
||||
:disabled="!settingsValid"
|
||||
@click="isCalibrating ? useCameraSettingsStore().takeCalibrationSnapshot(true) : startCalibration()"
|
||||
>
|
||||
@@ -400,7 +391,7 @@ const endCalibration = () => {
|
||||
small
|
||||
:color="useStateStore().calibrationData.hasEnoughImages ? 'accent' : 'red'"
|
||||
:class="useStateStore().calibrationData.hasEnoughImages ? 'black--text' : 'white---text'"
|
||||
style="width: 100%;"
|
||||
style="width: 100%"
|
||||
:disabled="!isCalibrating || !settingsValid"
|
||||
@click="endCalibration"
|
||||
>
|
||||
@@ -414,104 +405,70 @@ const endCalibration = () => {
|
||||
color="accent"
|
||||
small
|
||||
outlined
|
||||
style="width: 100%;"
|
||||
style="width: 100%"
|
||||
:disabled="!settingsValid"
|
||||
@click="downloadCalibBoard"
|
||||
>
|
||||
<v-icon left>
|
||||
mdi-download
|
||||
</v-icon>
|
||||
<v-icon left> mdi-download </v-icon>
|
||||
Generate Board
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-col :cols="6">
|
||||
<v-btn
|
||||
color="secondary"
|
||||
:disabled="isCalibrating"
|
||||
small
|
||||
style="width: 100%;"
|
||||
@click="openCalibUploadPrompt"
|
||||
>
|
||||
<v-icon left>
|
||||
mdi-upload
|
||||
</v-icon>
|
||||
<v-btn color="secondary" :disabled="isCalibrating" small style="width: 100%" @click="openCalibUploadPrompt">
|
||||
<v-icon left> mdi-upload </v-icon>
|
||||
Import From CalibDB
|
||||
</v-btn>
|
||||
<input
|
||||
ref="importCalibrationFromCalibDB"
|
||||
type="file"
|
||||
accept=".json"
|
||||
style="display: none;"
|
||||
style="display: none"
|
||||
@change="readImportedCalibration"
|
||||
>
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</v-card>
|
||||
<v-dialog
|
||||
v-model="showCalibEndDialog"
|
||||
width="500px"
|
||||
:persistent="true"
|
||||
>
|
||||
<v-card
|
||||
color="primary"
|
||||
dark
|
||||
>
|
||||
<v-card-title class="pb-8">
|
||||
Camera Calibration
|
||||
</v-card-title>
|
||||
<v-dialog v-model="showCalibEndDialog" width="500px" :persistent="true">
|
||||
<v-card color="primary" dark>
|
||||
<v-card-title class="pb-8"> Camera Calibration </v-card-title>
|
||||
<div class="ml-3">
|
||||
<v-col style="text-align: center">
|
||||
<template v-if="calibCanceled">
|
||||
<v-icon
|
||||
color="blue"
|
||||
size="70"
|
||||
<v-icon color="blue" size="70"> mdi-cancel </v-icon>
|
||||
<v-card-text
|
||||
>Camera Calibration has been Canceled, the backend is attempting to cleanly cancel the calibration
|
||||
process.</v-card-text
|
||||
>
|
||||
mdi-cancel
|
||||
</v-icon>
|
||||
<v-card-text>Camera Calibration has been Canceled, the backend is attempting to cleanly cancel the calibration process.</v-card-text>
|
||||
</template>
|
||||
<template v-else-if="isCalibrating">
|
||||
<v-progress-circular
|
||||
indeterminate
|
||||
:size="70"
|
||||
:width="8"
|
||||
color="accent"
|
||||
/>
|
||||
<v-progress-circular indeterminate :size="70" :width="8" color="accent" />
|
||||
<v-card-text>Camera is being calibrated. This process may take several minutes...</v-card-text>
|
||||
</template>
|
||||
<template v-else-if="calibSuccess">
|
||||
<v-icon
|
||||
color="green"
|
||||
size="70"
|
||||
>
|
||||
mdi-check-bold
|
||||
</v-icon>
|
||||
<v-icon color="green" size="70"> mdi-check-bold </v-icon>
|
||||
<v-card-text>
|
||||
Camera has been successfully calibrated for {{ getUniqueVideoResolutionStrings().find(v => v.value === useStateStore().calibrationData.videoFormatIndex).name }}!
|
||||
Camera has been successfully calibrated for
|
||||
{{
|
||||
getUniqueVideoResolutionStrings().find(
|
||||
(v) => v.value === useStateStore().calibrationData.videoFormatIndex
|
||||
).name
|
||||
}}!
|
||||
</v-card-text>
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-icon
|
||||
color="red"
|
||||
size="70"
|
||||
<v-icon color="red" size="70"> mdi-close </v-icon>
|
||||
<v-card-text
|
||||
>Camera calibration failed! Make sure that the photos are taken such that the rainbow grid circles align
|
||||
with the corners of the chessboard, and try again. More information is available in the program
|
||||
logs.</v-card-text
|
||||
>
|
||||
mdi-close
|
||||
</v-icon>
|
||||
<v-card-text>Camera calibration failed! Make sure that the photos are taken such that the rainbow grid circles align with the corners of the chessboard, and try again. More information is available in the program logs.</v-card-text>
|
||||
</template>
|
||||
</v-col>
|
||||
</div>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
v-if="!isCalibrating"
|
||||
color="white"
|
||||
text
|
||||
@click="showCalibEndDialog = false"
|
||||
>
|
||||
OK
|
||||
</v-btn>
|
||||
<v-btn v-if="!isCalibrating" color="white" text @click="showCalibEndDialog = false"> OK </v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
@@ -522,7 +479,8 @@ const endCalibration = () => {
|
||||
.v-data-table {
|
||||
text-align: center;
|
||||
|
||||
th, td {
|
||||
th,
|
||||
td {
|
||||
background-color: #006492 !important;
|
||||
font-size: 1rem !important;
|
||||
}
|
||||
|
||||
@@ -8,43 +8,40 @@ import { ref } from "vue";
|
||||
const currentFov = ref(useCameraSettingsStore().currentCameraSettings.fov.value);
|
||||
|
||||
const saveCameraSettings = () => {
|
||||
useCameraSettingsStore().updateCameraSettings({ fov: currentFov.value }, true)
|
||||
.then((response) => {
|
||||
useStateStore().showSnackbarMessage({
|
||||
color: "success",
|
||||
message: response.data.text || response.data
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
if(error.response) {
|
||||
useStateStore().showSnackbarMessage({
|
||||
color: "error",
|
||||
message: error.response.data.text || error.response.data
|
||||
});
|
||||
} else if(error.request) {
|
||||
useStateStore().showSnackbarMessage({
|
||||
color: "error",
|
||||
message: "Error while trying to process the request! The backend didn't respond."
|
||||
});
|
||||
} else {
|
||||
useStateStore().showSnackbarMessage({
|
||||
color: "error",
|
||||
message: "An error occurred while trying to process the request."
|
||||
});
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
useCameraSettingsStore().currentCameraSettings.fov.value = currentFov.value;
|
||||
useCameraSettingsStore()
|
||||
.updateCameraSettings({ fov: currentFov.value }, true)
|
||||
.then((response) => {
|
||||
useStateStore().showSnackbarMessage({
|
||||
color: "success",
|
||||
message: response.data.text || response.data
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.response) {
|
||||
useStateStore().showSnackbarMessage({
|
||||
color: "error",
|
||||
message: error.response.data.text || error.response.data
|
||||
});
|
||||
} else if (error.request) {
|
||||
useStateStore().showSnackbarMessage({
|
||||
color: "error",
|
||||
message: "Error while trying to process the request! The backend didn't respond."
|
||||
});
|
||||
} else {
|
||||
useStateStore().showSnackbarMessage({
|
||||
color: "error",
|
||||
message: "An error occurred while trying to process the request."
|
||||
});
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
useCameraSettingsStore().currentCameraSettings.fov.value = currentFov.value;
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-card
|
||||
class="mb-3 pr-6 pb-3"
|
||||
color="primary"
|
||||
dark
|
||||
>
|
||||
<v-card class="mb-3 pr-6 pb-3" color="primary" dark>
|
||||
<v-card-title>Camera Settings</v-card-title>
|
||||
<div class="ml-5">
|
||||
<cv-select
|
||||
@@ -52,29 +49,33 @@ const saveCameraSettings = () => {
|
||||
label="Camera"
|
||||
:items="useCameraSettingsStore().cameraNames"
|
||||
:select-cols="8"
|
||||
@input="args => {
|
||||
currentFov = useCameraSettingsStore().cameras[args].fov.value;
|
||||
useCameraSettingsStore().setCurrentCameraIndex(args);
|
||||
}"
|
||||
@input="
|
||||
(args) => {
|
||||
currentFov = useCameraSettingsStore().cameras[args].fov.value;
|
||||
useCameraSettingsStore().setCurrentCameraIndex(args);
|
||||
}
|
||||
"
|
||||
/>
|
||||
<cv-number-input
|
||||
v-model="currentFov"
|
||||
:tooltip="!useCameraSettingsStore().currentCameraSettings.fov.managedByVendor ? 'Field of view (in degrees) of the camera measured across the diagonal of the frame, in a video mode which covers the whole sensor area.' : 'This setting is managed by a vendor'"
|
||||
:tooltip="
|
||||
!useCameraSettingsStore().currentCameraSettings.fov.managedByVendor
|
||||
? 'Field of view (in degrees) of the camera measured across the diagonal of the frame, in a video mode which covers the whole sensor area.'
|
||||
: 'This setting is managed by a vendor'
|
||||
"
|
||||
label="Maximum Diagonal FOV"
|
||||
:disabled="useCameraSettingsStore().currentCameraSettings.fov.managedByVendor"
|
||||
:label-cols="4"
|
||||
/>
|
||||
<br>
|
||||
<br />
|
||||
<v-btn
|
||||
style="margin-top:10px"
|
||||
style="margin-top: 10px"
|
||||
small
|
||||
color="secondary"
|
||||
:disabled="currentFov === useCameraSettingsStore().currentCameraSettings.fov.value"
|
||||
@click="saveCameraSettings"
|
||||
>
|
||||
<v-icon left>
|
||||
mdi-content-save
|
||||
</v-icon>
|
||||
<v-icon left> mdi-content-save </v-icon>
|
||||
Save Changes
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
@@ -8,22 +8,25 @@ import { useSettingsStore } from "@/stores/settings/GeneralSettingsStore";
|
||||
|
||||
const props = defineProps<{
|
||||
// TODO fully update v-model usage in custom components on Vue3 update
|
||||
value: number[]
|
||||
value: number[];
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "input", value: number[]): void
|
||||
(e: "input", value: number[]): void;
|
||||
}>();
|
||||
|
||||
|
||||
const localValue = computed({
|
||||
get: () => props.value,
|
||||
set: v => emit("input", v)
|
||||
set: (v) => emit("input", v)
|
||||
});
|
||||
|
||||
const driverMode = computed<boolean>({
|
||||
get: () => useCameraSettingsStore().isDriverMode,
|
||||
set: v => useCameraSettingsStore().changeCurrentPipelineIndex(v ? -1 : useCameraSettingsStore().currentCameraSettings.lastPipelineIndex || 0, true)
|
||||
set: (v) =>
|
||||
useCameraSettingsStore().changeCurrentPipelineIndex(
|
||||
v ? -1 : useCameraSettingsStore().currentCameraSettings.lastPipelineIndex || 0,
|
||||
true
|
||||
)
|
||||
});
|
||||
|
||||
const fpsTooLow = computed<boolean>(() => {
|
||||
@@ -33,38 +36,30 @@ const fpsTooLow = computed<boolean>(() => {
|
||||
const gpuAccel = useSettingsStore().general.gpuAcceleration !== undefined;
|
||||
const isReflective = useCameraSettingsStore().currentPipelineSettings.pipelineType === PipelineType.Reflective;
|
||||
|
||||
return (currFPS - targetFPS) < -5 && currFPS !== 0 && !driverMode && gpuAccel && isReflective;
|
||||
return currFPS - targetFPS < -5 && currFPS !== 0 && !driverMode && gpuAccel && isReflective;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-card
|
||||
class="mb-3 pr-6 pb-3 pa-4"
|
||||
color="primary"
|
||||
dark
|
||||
>
|
||||
<v-card class="mb-3 pr-6 pb-3 pa-4" color="primary" dark>
|
||||
<v-card-title
|
||||
class="pb-0 mb-2 pl-4 pt-1"
|
||||
style="min-height: 50px; justify-content: space-between; align-content: center"
|
||||
>
|
||||
<div style="display: flex; flex-wrap: wrap">
|
||||
<div>
|
||||
<span
|
||||
class="mr-4"
|
||||
style="white-space: nowrap"
|
||||
>
|
||||
Cameras
|
||||
</span>
|
||||
<span class="mr-4" style="white-space: nowrap"> Cameras </span>
|
||||
</div>
|
||||
<div>
|
||||
<v-chip
|
||||
label
|
||||
:color="fpsTooLow ? 'error' : 'transparent'"
|
||||
:text-color="fpsTooLow ? '#C7EA46' : '#ff4d00'"
|
||||
style="font-size: 1rem; padding: 0; margin: 0;"
|
||||
style="font-size: 1rem; padding: 0; margin: 0"
|
||||
>
|
||||
<span class="pr-1">
|
||||
{{ Math.round(useStateStore().pipelineResults?.fps || 0) }} FPS – {{ Math.min(Math.round(useStateStore().pipelineResults?.latency || 0), 9999) }} ms latency
|
||||
{{ Math.round(useStateStore().pipelineResults?.fps || 0) }} FPS –
|
||||
{{ Math.min(Math.round(useStateStore().pipelineResults?.latency || 0), 9999) }} ms latency
|
||||
</span>
|
||||
</v-chip>
|
||||
</div>
|
||||
@@ -75,43 +70,24 @@ const fpsTooLow = computed<boolean>(() => {
|
||||
v-model="driverMode"
|
||||
:disabled="useCameraSettingsStore().isCalibrationMode"
|
||||
label="Driver Mode"
|
||||
style="margin-left: auto;"
|
||||
style="margin-left: auto"
|
||||
color="accent"
|
||||
class="pt-2"
|
||||
/>
|
||||
</div>
|
||||
</v-card-title>
|
||||
<div
|
||||
class="stream-container pb-4"
|
||||
>
|
||||
<div class="stream-container pb-4">
|
||||
<div class="stream">
|
||||
<photon-camera-stream
|
||||
v-show="value.includes(0)"
|
||||
stream-type="Raw"
|
||||
style="max-width: 100%"
|
||||
/>
|
||||
<photon-camera-stream v-show="value.includes(0)" stream-type="Raw" style="max-width: 100%" />
|
||||
</div>
|
||||
<div class="stream">
|
||||
<photon-camera-stream
|
||||
v-show="value.includes(1)"
|
||||
stream-type="Processed"
|
||||
style="max-width: 100%"
|
||||
/>
|
||||
<photon-camera-stream v-show="value.includes(1)" stream-type="Processed" style="max-width: 100%" />
|
||||
</div>
|
||||
</div>
|
||||
<v-divider />
|
||||
<div class="pt-4">
|
||||
<p style="color: white;">
|
||||
Stream Display
|
||||
</p>
|
||||
<v-btn-toggle
|
||||
v-model="localValue"
|
||||
:multiple="true"
|
||||
mandatory
|
||||
dark
|
||||
class="fill"
|
||||
style="width: 100%"
|
||||
>
|
||||
<p style="color: white">Stream Display</p>
|
||||
<v-btn-toggle v-model="localValue" :multiple="true" mandatory dark class="fill" style="width: 100%">
|
||||
<v-btn
|
||||
color="secondary"
|
||||
class="fill"
|
||||
|
||||
Reference in New Issue
Block a user