From bfdd7483472a6e26db84e989fe75ddbb98a06233 Mon Sep 17 00:00:00 2001 From: Bryon Lewis Date: Tue, 17 May 2022 14:12:37 -0400 Subject: [PATCH 1/2] changes linked camera behavior --- .../annotators/useMediaController.ts | 24 +++++++++++++++---- client/src/components/controls/Controls.vue | 6 +++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/client/src/components/annotators/useMediaController.ts b/client/src/components/annotators/useMediaController.ts index 0738ac2a4..bdecca3b7 100644 --- a/client/src/components/annotators/useMediaController.ts +++ b/client/src/components/annotators/useMediaController.ts @@ -145,15 +145,25 @@ export function useMediaController() { allowCameraTrigger = false; Object.entries(geoViewers).forEach(([camera, geoViewer]) => { if (geoViewer.value && camera !== camEvent.camera) { - geoViewer.value.center(activeMap.center()); - geoViewer.value.zoom(activeMap.zoom()); - geoViewer.value.rotation(activeMap.rotation()); + geoViewer.value.pan(camEvent.event.screenDelta); } }); allowCameraTrigger = true; } }); + bus.$on('zoom', (camEvent: {camera: string; event: GeoEvent}) => { + const activeMap = geoViewers[camEvent.camera]?.value; + if (activeMap !== undefined && synchronizeCameras.value) { + allowCameraTrigger = false; + Object.entries(geoViewers).forEach(([camera, geoViewer]) => { + if (geoViewer.value && camera !== camEvent.camera) { + geoViewer.value.zoom(activeMap.zoom()); + } + }); + allowCameraTrigger = true; + } + }); /** * This secondary initialization wrapper solves a sort of * chicken-and-egg problem, allowing the function consumer to use @@ -304,11 +314,17 @@ export function useMediaController() { //Add in bus control synchronization for cameras geoViewers[camera].value.geoOn(geo.event.pan, (e: GeoEvent) => { - // Only trigger if not handling other camera interactions.rrrrr + // Only trigger if not handling other camera interactions. if (allowCameraTrigger) { bus.$emit('pan', { camera: camera.toString(), event: e }); } }); + geoViewers[camera].value.geoOn(geo.event.zoom, (e: GeoEvent) => { + // Only trigger if not handling other camera interactions. + if (allowCameraTrigger) { + bus.$emit('zoom', { camera: camera.toString(), event: e }); + } + }); } function prevFrame() { diff --git a/client/src/components/controls/Controls.vue b/client/src/components/controls/Controls.vue index ecabec8fa..463b7bd60 100644 --- a/client/src/components/controls/Controls.vue +++ b/client/src/components/controls/Controls.vue @@ -62,6 +62,12 @@ export default defineComponent({ { bind: 'space', handler: togglePlay, disabled: visible() }, { bind: 'f', handler: mediaController.nextFrame, disabled: visible() }, { bind: 'd', handler: mediaController.prevFrame, disabled: visible() }, + { + bind: 'l', + handler: () => mediaController.toggleSynchronizeCameras(!mediaController.cameraSync.value), + disabled: visible(), + }, + ]" > Date: Wed, 18 May 2022 08:04:25 -0400 Subject: [PATCH 2/2] porting-in desktop fixes/features --- .../components/ConfidenceSubsection.vue | 42 ++++++++++---- .../components/ImageEnhancements.vue | 55 +++++++++++++++++++ .../components/TrackDetailsPanel.vue | 2 + client/dive-common/components/Viewer.vue | 14 ++++- client/dive-common/store/context.ts | 5 ++ .../frontend/components/ImportDialog.vue | 2 + .../components/annotators/ImageAnnotator.vue | 7 +++ .../components/annotators/VideoAnnotator.vue | 8 +++ client/src/components/controls/Controls.vue | 15 +++++ client/src/provides.ts | 14 +++++ client/src/use/index.ts | 2 + client/src/use/useImageEnhancements.ts | 45 +++++++++++++++ 12 files changed, 198 insertions(+), 13 deletions(-) create mode 100644 client/dive-common/components/ImageEnhancements.vue create mode 100644 client/src/use/useImageEnhancements.ts diff --git a/client/dive-common/components/ConfidenceSubsection.vue b/client/dive-common/components/ConfidenceSubsection.vue index 910a37584..5943dff02 100644 --- a/client/dive-common/components/ConfidenceSubsection.vue +++ b/client/dive-common/components/ConfidenceSubsection.vue @@ -19,6 +19,11 @@ export default defineComponent({ type: Array as PropType<[string, number][]>, required: true, }, + disabled: { + type: Boolean, + default: false, + }, + }, setup() { const typeStylingRef = useTypeStyling(); @@ -82,12 +87,35 @@ export default defineComponent({ }" /> - + {{ pair[0] }} - + + {{ pair[1].toFixed(4) }} + + + + Accept {{ pair[0] }} as correct type + + @@ -97,19 +125,9 @@ export default defineComponent({ diff --git a/client/dive-common/components/ImageEnhancements.vue b/client/dive-common/components/ImageEnhancements.vue new file mode 100644 index 000000000..ecc3e050e --- /dev/null +++ b/client/dive-common/components/ImageEnhancements.vue @@ -0,0 +1,55 @@ + + + + + diff --git a/client/dive-common/components/TrackDetailsPanel.vue b/client/dive-common/components/TrackDetailsPanel.vue index fb0304171..d7916ab9b 100644 --- a/client/dive-common/components/TrackDetailsPanel.vue +++ b/client/dive-common/components/TrackDetailsPanel.vue @@ -336,6 +336,8 @@ export default defineComponent({ :confidence-pairs=" flatten(selectedTrackList.map((t) => t.confidencePairs)).sort((a, b) => b[1] - a[1]) " + :disabled="selectedTrackList.length > 1" + @set-type="selectedTrackList[0].setType($event)" /> diff --git a/client/dive-common/store/context.ts b/client/dive-common/store/context.ts index 574b46cee..c7ebfbbfc 100644 --- a/client/dive-common/store/context.ts +++ b/client/dive-common/store/context.ts @@ -2,6 +2,7 @@ import Install, { reactive } from '@vue/composition-api'; import Vue, { VueConstructor } from 'vue'; /* Components */ import TypeThreshold from 'dive-common/components/TypeThreshold.vue'; +import ImageEnhancements from 'dive-common/components/ImageEnhancements.vue'; import MultiCamTools from 'dive-common/components/MultiCamTools.vue'; Vue.use(Install); @@ -28,6 +29,10 @@ const componentMap: Record = { description: 'Multicam Tools', component: MultiCamTools, }, + ImageEnhancements: { + description: 'Image Enhancements', + component: ImageEnhancements, + }, }; function register(item: ComponentMapItem) { diff --git a/client/platform/desktop/frontend/components/ImportDialog.vue b/client/platform/desktop/frontend/components/ImportDialog.vue index 9d77f9395..53a6d66e0 100644 --- a/client/platform/desktop/frontend/components/ImportDialog.vue +++ b/client/platform/desktop/frontend/components/ImportDialog.vue @@ -187,6 +187,7 @@ export default defineComponent({ label="Annotation File (Optional)" hint="Optional. Load existing annotations. Supports DIVE JSON and VIAME CSV" persistent-hint + @input="argCopy.trackFileAbsPath = $event" @click="openUpload('annotation')" @click:prepend-inner="openUpload('annotation')" @click:clear="argCopy.trackFileAbsPath=''" @@ -227,6 +228,7 @@ export default defineComponent({ label="Configuration File (Optional)" hint="Optional. Supports DIVE JSON configuration file." persistent-hint + @input="argCopy.metaFileAbsPath = $event" @click="openUpload('meta')" @click:prepend-inner="openUpload('meta')" @click:clear="argCopy.metaFileAbsPath=''" diff --git a/client/src/components/annotators/ImageAnnotator.vue b/client/src/components/annotators/ImageAnnotator.vue index 6e8dc6017..834c024e4 100644 --- a/client/src/components/annotators/ImageAnnotator.vue +++ b/client/src/components/annotators/ImageAnnotator.vue @@ -51,6 +51,10 @@ export default defineComponent({ type: String as PropType, default: 'singleCam', }, + intercept: { + type: Number as PropType, + default: undefined, + }, }, setup(props) { @@ -417,14 +421,17 @@ export default defineComponent({ diff --git a/client/src/components/annotators/VideoAnnotator.vue b/client/src/components/annotators/VideoAnnotator.vue index e8862751a..a33a7b26e 100644 --- a/client/src/components/annotators/VideoAnnotator.vue +++ b/client/src/components/annotators/VideoAnnotator.vue @@ -102,6 +102,11 @@ export default defineComponent({ type: String as PropType, default: 'singleCam', }, + intercept: { + type: Number as PropType, + default: undefined, + }, + }, setup(props) { @@ -301,14 +306,17 @@ export default defineComponent({ diff --git a/client/src/components/controls/Controls.vue b/client/src/components/controls/Controls.vue index 463b7bd60..978856015 100644 --- a/client/src/components/controls/Controls.vue +++ b/client/src/components/controls/Controls.vue @@ -3,6 +3,7 @@ import { defineComponent, reactive, watch, } from '@vue/composition-api'; import { usePrompt } from 'dive-common/vue-utilities/prompt-service'; +import context from 'dive-common/store/context'; import { injectAggregateController } from '../annotators/useMediaController'; export default defineComponent({ @@ -42,12 +43,17 @@ export default defineComponent({ } } + function toggleEnhancements() { + context.toggle('ImageEnhancements'); + } + return { data, mediaController, dragHandler, input, togglePlay, + toggleEnhancements, visible, }; }, @@ -157,6 +163,15 @@ export default defineComponent({ > mdi-image-filter-center-focus + + mdi-contrast-box + + >; const ReadOnlyModeSymbol = Symbol('readOnlyMode'); type ReadOnylModeType = Readonly>; +const ImageEnhancementsSymbol = Symbol('imageEnhancements'); +type ImageEnhancementsType = Readonly>; + /** * Handler interface describes all global events mutations * for above state @@ -167,6 +171,7 @@ export interface Handler { unstageFromMerge(ids: TrackId[]): void; /* Reload Annotation File */ reloadAnnotations(): Promise; + setSVGFilters({ blackPoint, whitePoint }: {blackPoint?: number; whitePoint?: number}): void; /* Set Selected Camera */ setSelectedCamera(camera: string, editMode: boolean): void; /* unlink Camera Track */ @@ -213,6 +218,7 @@ function dummyHandler(handle: (name: string, args: unknown[]) => void): Handler commitMerge(...args) { handle('commitMerge', args); }, unstageFromMerge(...args) { handle('unstageFromMerge', args); }, reloadAnnotations(...args) { handle('reloadTracks', args); return Promise.resolve(); }, + setSVGFilters(...args) { handle('setSVGFilter', args); }, setSelectedCamera(...args) { handle('setSelectedCamera', args); }, unlinkCameraTrack(...args) { handle('unlinkCameraTrack', args); }, linkCameraTrack(...args) { handle('linkCameraTrack', args); }, @@ -254,6 +260,7 @@ export interface State { time: TimeType; visibleModes: VisibleModesType; readOnlyMode: ReadOnylModeType; + imageEnhancements: ImageEnhancementsType; } /** @@ -312,6 +319,7 @@ function dummyState(): State { }, visibleModes: ref(['rectangle', 'text'] as VisibleAnnotationTypes[]), readOnlyMode: ref(false), + imageEnhancements: ref({}), }; } @@ -349,6 +357,7 @@ function provideAnnotator(state: State, handler: Handler) { provide(TimeSymbol, state.time); provide(VisibleModesSymbol, state.visibleModes); provide(ReadOnlyModeSymbol, state.readOnlyMode); + provide(ImageEnhancementsSymbol, state.imageEnhancements); provide(HandlerSymbol, handler); } @@ -464,6 +473,10 @@ function useVisibleModes() { function useReadOnlyMode() { return use(ReadOnlyModeSymbol); } +function useImageEnhancements() { + return use(ImageEnhancementsSymbol); +} + export { dummyHandler, @@ -496,4 +509,5 @@ export { useTime, useVisibleModes, useReadOnlyMode, + useImageEnhancements, }; diff --git a/client/src/use/index.ts b/client/src/use/index.ts index 6572058a1..4f75d3fcf 100644 --- a/client/src/use/index.ts +++ b/client/src/use/index.ts @@ -6,10 +6,12 @@ import useTimeObserver from './useTimeObserver'; import useTrackFilters from './useTrackFilters'; import useTrackSelectionControls from './useTrackSelectionControls'; import useTrackStore from './useTrackStore'; +import useImageEnhancements from './useImageEnhancements'; export { useAttributes, useEventChart, + useImageEnhancements, useLineChart, useStyling, useTimeObserver, diff --git a/client/src/use/useImageEnhancements.ts b/client/src/use/useImageEnhancements.ts new file mode 100644 index 000000000..85560eb20 --- /dev/null +++ b/client/src/use/useImageEnhancements.ts @@ -0,0 +1,45 @@ +import { + computed, + ref, Ref, set as VueSet, +} from '@vue/composition-api'; + +// Expecting this may be a placeholder for more complicated client side enhancements +// or more image filters +export interface ImageEnhancements { + blackPoint?: number; + whitePoint?: number; + } + + +export default function useImageEnhancements() { + const imageEnhancements: Ref = ref({}); + + const setSVGFilters = ({ blackPoint, whitePoint }: + {blackPoint?: number; whitePoint?: number }) => { + VueSet(imageEnhancements.value, 'blackPoint', blackPoint); + VueSet(imageEnhancements.value, 'whitePoint', whitePoint); + }; + + const brightness = computed(() => { + if (imageEnhancements.value.blackPoint !== undefined + && imageEnhancements.value.whitePoint !== undefined) { + return (1 / (imageEnhancements.value.whitePoint - imageEnhancements.value.blackPoint)); + } + return undefined; + }); + const intercept = computed(() => { + if (imageEnhancements.value.blackPoint !== undefined + && imageEnhancements.value.whitePoint !== undefined) { + return (-imageEnhancements.value.blackPoint + / (imageEnhancements.value.whitePoint - imageEnhancements.value.blackPoint)); + } + return undefined; + }); + + return { + imageEnhancements, + brightness, + intercept, + setSVGFilters, + }; +}