Improve UI stability, reliability, and readability (#1104)

closes #1090
closes #1030

Also fixes various styling issues and overflow issues for mobile support
This commit is contained in:
Sriman Achanta
2024-01-02 11:03:16 -05:00
committed by GitHub
parent 2a1792e71a
commit e4f475a253
24 changed files with 1436 additions and 5260 deletions

View File

@@ -11,28 +11,28 @@ import PvSwitch from "@/components/common/pv-switch.vue";
import PvSelect from "@/components/common/pv-select.vue";
import PvNumberInput from "@/components/common/pv-number-input.vue";
import { WebsocketPipelineType } from "@/types/WebsocketDataTypes";
import { getResolutionString, resolutionsAreEqual } from "@/lib/PhotonUtils";
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) =>
resolutionsAreEqual(cal.resolution, resolution)
);
};
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) => resolutionsAreEqual(v.resolution, format.resolution))) {
format.index = index;
const calib = getCalibrationCoeffs(format.resolution);
if (calib !== undefined) {
format.standardDeviation = calib.standardDeviation;
format.mean = calib.perViewErrors.reduce((a, b) => a + b) / calib.perViewErrors.length;
format.mean =
calib.perViewErrors === null
? Number.NaN
: 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 =
@@ -54,9 +54,9 @@ const getUniqueVideoResolutions = (): VideoFormat[] => {
);
return uniqueResolutions;
};
const getUniqueVideoResolutionStrings = () =>
const getUniqueVideoResolutionStrings = (): { name: string; value: number }[] =>
getUniqueVideoResolutions().map<{ name: string; value: number }>((f) => ({
name: `${f.resolution.width} X ${f.resolution.height}`,
name: `${getResolutionString(f.resolution)}`,
// Index won't ever be undefined
value: f.index || 0
}));
@@ -150,9 +150,9 @@ const importCalibrationFromCalibDB = ref();
const openCalibUploadPrompt = () => {
importCalibrationFromCalibDB.value.click();
};
const readImportedCalibration = (payload: Event) => {
if (payload.target == null || !payload.target?.files) return;
const files: FileList = payload.target.files as FileList;
const readImportedCalibration = () => {
const files = importCalibrationFromCalibDB.value.files;
if (files.length === 0) return;
files[0].text().then((text) => {
useCameraSettingsStore()
@@ -221,100 +221,96 @@ const endCalibration = () => {
<v-card class="mb-3 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">
<pv-select
v-model="useStateStore().calibrationData.videoFormatIndex"
label="Resolution"
:select-cols="7"
:disabled="isCalibrating"
tooltip="Resolution to calibrate at (you will have to calibrate every resolution you use 3D mode on)"
:items="getUniqueVideoResolutionStrings()"
/>
<pv-select
v-show="isCalibrating"
v-model="useCameraSettingsStore().currentPipelineSettings.streamingFrameDivisor"
label="Decimation"
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)
"
/>
<pv-select
v-model="boardType"
label="Board Type"
tooltip="Calibration board pattern to use"
:select-cols="7"
:items="['Chessboard', 'Dotboard']"
:disabled="isCalibrating"
/>
<pv-number-input
v-model="squareSizeIn"
label="Pattern Spacing (in)"
tooltip="Spacing between pattern features in inches"
:disabled="isCalibrating"
:rules="[(v) => v > 0 || 'Size must be positive']"
:label-cols="5"
/>
<pv-number-input
v-model="patternWidth"
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']"
:label-cols="5"
/>
<pv-number-input
v-model="patternHeight"
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']"
: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>
<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>
</tr>
</thead>
<tbody>
<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.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>
</tr>
</tbody>
</v-simple-table>
</v-row>
<v-row justify="center">
<v-chip
v-show="isCalibrating"
label
:color="useStateStore().calibrationData.hasEnoughImages ? 'secondary' : 'gray'"
>
Snapshots: {{ useStateStore().calibrationData.imageCount }} of at least
{{ useStateStore().calibrationData.minimumImageCount }}
</v-chip>
</v-row>
</v-col>
<v-row v-show="!isCalibrating" class="pb-12">
<v-card-subtitle class="pb-0 mb-0 pl-3">Complete Calibrations</v-card-subtitle>
<v-simple-table fixed-header height="100%" dense class="mt-2">
<thead>
<tr>
<th>Resolution</th>
<th>Mean Error</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">
<td>{{ getResolutionString(value.resolution) }}</td>
<td>
{{ value.mean !== undefined ? (isNaN(value.mean) ? "NaN" : value.mean.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>
</tr>
</tbody>
</v-simple-table>
</v-row>
<v-divider />
<v-row style="display: flex; flex-direction: column" class="mt-4">
<v-card-subtitle v-show="!isCalibrating" class="pl-3 pa-0 ma-0"> Configure New Calibration</v-card-subtitle>
<v-form ref="form" v-model="settingsValid" class="pl-4 mb-10 pr-5">
<pv-select
v-model="useStateStore().calibrationData.videoFormatIndex"
label="Resolution"
:select-cols="7"
:disabled="isCalibrating"
tooltip="Resolution to calibrate at (you will have to calibrate every resolution you use 3D mode on)"
:items="getUniqueVideoResolutionStrings()"
/>
<pv-select
v-show="isCalibrating"
v-model="useCameraSettingsStore().currentPipelineSettings.streamingFrameDivisor"
label="Decimation"
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)"
/>
<pv-select
v-model="boardType"
label="Board Type"
tooltip="Calibration board pattern to use"
:select-cols="7"
:items="['Chessboard', 'Dotboard']"
:disabled="isCalibrating"
/>
<pv-number-input
v-model="squareSizeIn"
label="Pattern Spacing (in)"
tooltip="Spacing between pattern features in inches"
:disabled="isCalibrating"
:rules="[(v) => v > 0 || 'Size must be positive']"
:label-cols="5"
/>
<pv-number-input
v-model="patternWidth"
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']"
:label-cols="5"
/>
<pv-number-input
v-model="patternHeight"
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']"
:label-cols="5"
/>
</v-form>
<v-row justify="center">
<v-chip
v-show="isCalibrating"
label
:color="useStateStore().calibrationData.hasEnoughImages ? 'secondary' : 'gray'"
class="mb-6"
>
Snapshots: {{ useStateStore().calibrationData.imageCount }} of at least
{{ useStateStore().calibrationData.minimumImageCount }}
</v-chip>
</v-row>
</v-row>
<v-row v-if="isCalibrating">
<v-col cols="12" class="pt-0">
@@ -387,7 +383,8 @@ const endCalibration = () => {
:disabled="!settingsValid"
@click="isCalibrating ? useCameraSettingsStore().takeCalibrationSnapshot() : startCalibration()"
>
{{ isCalibrating ? "Take Snapshot" : "Start Calibration" }}
<v-icon left class="calib-btn-icon"> {{ isCalibrating ? "mdi-camera" : "mdi-flag-outline" }} </v-icon>
<span class="calib-btn-label">{{ isCalibrating ? "Take Snapshot" : "Start Calibration" }}</span>
</v-btn>
</v-col>
<v-col :cols="6">
@@ -399,7 +396,12 @@ const endCalibration = () => {
:disabled="!isCalibrating || !settingsValid"
@click="endCalibration"
>
{{ useStateStore().calibrationData.hasEnoughImages ? "Finish Calibration" : "Cancel Calibration" }}
<v-icon left class="calib-btn-icon">
{{ useStateStore().calibrationData.hasEnoughImages ? "mdi-flag-checkered" : "mdi-flag-off-outline" }}
</v-icon>
<span class="calib-btn-label">{{
useStateStore().calibrationData.hasEnoughImages ? "Finish Calibration" : "Cancel Calibration"
}}</span>
</v-btn>
</v-col>
</v-row>
@@ -413,14 +415,14 @@ const endCalibration = () => {
:disabled="!settingsValid"
@click="downloadCalibBoard"
>
<v-icon left> mdi-download </v-icon>
Generate Board
<v-icon left class="calib-btn-icon"> mdi-download </v-icon>
<span class="calib-btn-label">Generate Board</span>
</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>
Import From CalibDB
<v-icon left class="calib-btn-icon"> mdi-upload </v-icon>
<span class="calib-btn-label">Import From CalibDB</span>
</v-btn>
<input
ref="importCalibrationFromCalibDB"
@@ -456,7 +458,7 @@ const endCalibration = () => {
{{
getUniqueVideoResolutionStrings().find(
(v) => v.value === useStateStore().calibrationData.videoFormatIndex
).name
)?.name
}}!
</v-card-text>
</template>
@@ -482,6 +484,7 @@ const endCalibration = () => {
<style scoped lang="scss">
.v-data-table {
text-align: center;
width: 100%;
th,
td {
@@ -509,4 +512,13 @@ const endCalibration = () => {
border-radius: 10px;
}
}
@media only screen and (max-width: 512px) {
.calib-btn-icon {
margin: 0 !important;
}
.calib-btn-label {
display: none;
}
}
</style>

View File

@@ -96,8 +96,8 @@ const expanded = ref([]);
<v-row class="pl-6">
<v-col>
<v-btn color="secondary" @click="fetchSnapshots">
<v-icon left> mdi-folder </v-icon>
Show Saved Snapshots
<v-icon left class="open-icon"> mdi-folder </v-icon>
<span class="open-label">Show Saved Snapshots</span>
</v-btn>
</v-col>
</v-row>
@@ -199,4 +199,12 @@ const expanded = ref([]);
max-width: 100%;
}
}
@media only screen and (max-width: 351px) {
.open-icon {
margin: 0 !important;
}
.open-label {
display: none;
}
}
</style>

View File

@@ -103,16 +103,16 @@ const fpsTooLow = computed<boolean>(() => {
class="fill"
:disabled="useCameraSettingsStore().isDriverMode || useCameraSettingsStore().isCalibrationMode"
>
<v-icon>mdi-import</v-icon>
<span>Raw</span>
<v-icon left class="mode-btn-icon">mdi-import</v-icon>
<span class="mode-btn-label">Raw</span>
</v-btn>
<v-btn
color="secondary"
class="fill"
:disabled="useCameraSettingsStore().isDriverMode || useCameraSettingsStore().isCalibrationMode"
>
<v-icon>mdi-export</v-icon>
<span>Processed</span>
<v-icon left class="mode-btn-icon">mdi-export</v-icon>
<span class="mode-btn-label">Processed</span>
</v-btn>
</v-btn-toggle>
</div>
@@ -156,4 +156,12 @@ th {
max-width: 50%;
}
}
@media only screen and (max-width: 351px) {
.mode-btn-icon {
margin: 0 !important;
}
.mode-btn-label {
display: none;
}
}
</style>