Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions client/dive-common/components/ImageEnhancements.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<script lang="ts">
import { defineComponent, ref } from '@vue/composition-api';

import { useHandler, useImageEnhancements } from 'vue-media-annotator/provides';

export default defineComponent({
name: 'ImageEnhancements',
description: 'Image controls',
setup() {
const { setSVGFilters } = useHandler();
const imageEnhancements = useImageEnhancements();
const range = ref([
(imageEnhancements.value.blackPoint ?? 0) * 255.0,
(imageEnhancements.value.whitePoint ?? 1) * 255.0,
]);

const modifyValue = () => {
setSVGFilters({ blackPoint: range.value[0] / 255.0, whitePoint: range.value[1] / 255.0 });
};
return {
modifyValue,
range,
};
},
});
</script>

<template>
<div class="mx-4">
<span class="text-body-2">
Controls for adjusting images.
</span>
<v-divider class="my-3" />
<v-range-slider
v-model="range"
:min="0"
:max="255"
:step="1.0"
thumb-label="always"
label="Contrast:"
class="my-4"
@input="modifyValue"
/>
<v-btn
block
depressed
color="warning"
class="my-2"
@click="range = [0, 255]; modifyValue()"
>
Reset
</v-btn>
</div>
</template>

<style scoped>
</style>
16 changes: 15 additions & 1 deletion client/dive-common/components/Viewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { Vue } from 'vue/types/vue';
import Track, { TrackId } from 'vue-media-annotator/track';
import {
useAttributes,
useImageEnhancements,
useLineChart,
useStyling,
useTrackFilters,
Expand Down Expand Up @@ -122,6 +123,13 @@ export default defineComponent({
pendingSaveCount,
} = useSave(datasetId, readonlyState);

const {
imageEnhancements,
brightness,
intercept,
setSVGFilters,
} = useImageEnhancements();

const recipes = [
new PolygonBase(),
new HeadTail(),
Expand Down Expand Up @@ -403,6 +411,7 @@ export default defineComponent({
setConfidenceFilters,
deleteAttribute,
reloadAnnotations,
setSVGFilters,
};

provideAnnotator(
Expand Down Expand Up @@ -431,6 +440,7 @@ export default defineComponent({
time,
visibleModes,
readOnlyMode: readonlyState,
imageEnhancements,
},
globalHandler,
);
Expand Down Expand Up @@ -465,6 +475,8 @@ export default defineComponent({
originalFps: time.originalFps,
context,
readonlyState,
brightness,
intercept,
/* methods */
handler: globalHandler,
save,
Expand Down Expand Up @@ -627,7 +639,9 @@ export default defineComponent({
{ bind: 'r', handler: () => mediaController.resetZoom() },
{ bind: 'esc', handler: () => handler.trackAbort() },
]"
v-bind="{ imageData, videoUrl, updateTime, frameRate, originalFps }"
v-bind="{ imageData, videoUrl, updateTime, frameRate,
originalFps, brightness, intercept,
}"
class="playback-component"
>
<template slot="control">
Expand Down
5 changes: 5 additions & 0 deletions client/dive-common/store/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Vue.use(Install);

Expand All @@ -23,6 +24,10 @@ const componentMap: Record<string, ComponentMapItem> = {
description: 'Threshold Controls',
component: TypeThreshold,
},
ImageEnhancements: {
description: 'Image Enhancements',
component: ImageEnhancements,
},
};

function register(item: ComponentMapItem) {
Expand Down
7 changes: 7 additions & 0 deletions client/src/components/annotators/ImageAnnotator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ export default defineComponent({
type: Number as PropType<number | undefined>,
default: undefined,
},
intercept: {
type: Number as PropType<number | undefined>,
default: undefined,
},
},

setup(props) {
Expand Down Expand Up @@ -415,14 +419,17 @@ export default defineComponent({
<feFuncR
type="linear"
:slope="brightness"
:intercept="intercept"
/>
<feFuncG
type="linear"
:slope="brightness"
:intercept="intercept"
/>
<feFuncB
type="linear"
:slope="brightness"
:intercept="intercept"
/>
</feComponentTransfer>
</filter>
Expand Down
7 changes: 7 additions & 0 deletions client/src/components/annotators/VideoAnnotator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ export default defineComponent({
type: Number as PropType<number | undefined>,
default: undefined,
},
intercept: {
type: Number as PropType<number | undefined>,
default: undefined,
},
},

setup(props) {
Expand Down Expand Up @@ -295,14 +299,17 @@ export default defineComponent({
<feFuncR
type="linear"
:slope="brightness"
:intercept="intercept"
/>
<feFuncG
type="linear"
:slope="brightness"
:intercept="intercept"
/>
<feFuncB
type="linear"
:slope="brightness"
:intercept="intercept"
/>
</feComponentTransfer>
</filter>
Expand Down
13 changes: 13 additions & 0 deletions client/src/components/controls/Controls.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script lang="ts">
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 { injectMediaController } from '../annotators/useMediaController';

export default defineComponent({
Expand Down Expand Up @@ -41,13 +42,18 @@ export default defineComponent({
}
}

function toggleEnhancements() {
context.toggle('ImageEnhancements');
}

return {
data,
mediaController,
dragHandler,
input,
togglePlay,
visible,
toggleEnhancements,
};
},
});
Expand Down Expand Up @@ -146,6 +152,13 @@ export default defineComponent({
@click="mediaController.resetZoom"
>
<v-icon>mdi-image-filter-center-focus</v-icon>
</v-btn><v-btn
icon
small
title="Image Enhancements"
@click="toggleEnhancements"
>
<v-icon>mdi-contrast-box</v-icon>
</v-btn>
</v-col>
</v-row>
Expand Down
13 changes: 13 additions & 0 deletions client/src/provides.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { RectBounds } from './utils';
import { Attribute } from './use/useAttributes';
import { DefaultConfidence, TrackWithContext } from './use/useTrackFilters';
import { Time } from './use/useTimeObserver';
import { ImageEnhancements } from './use/useImageEnhancements';

/**
* Type definitions are read only because injectors may mutate internal state,
Expand Down Expand Up @@ -90,6 +91,9 @@ type VisibleModesType = Readonly<Ref<readonly VisibleAnnotationTypes[]>>;
const ReadOnlyModeSymbol = Symbol('readOnlyMode');
type ReadOnylModeType = Readonly<Ref<boolean>>;

const ImageEnhancementsSymbol = Symbol('imageEnhancements');
type ImageEnhancementsType = Readonly<Ref<ImageEnhancements>>;

/**
* Handler interface describes all global events mutations
* for above state
Expand Down Expand Up @@ -164,6 +168,7 @@ export interface Handler {
unstageFromMerge(ids: TrackId[]): void;
/* Reload Annotation File */
reloadAnnotations(): Promise<void>;
setSVGFilters({ blackPoint, whitePoint }: {blackPoint?: number; whitePoint?: number}): void;
}
const HandlerSymbol = Symbol('handler');

Expand Down Expand Up @@ -202,6 +207,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); },
};
}

Expand Down Expand Up @@ -237,6 +243,7 @@ export interface State {
time: TimeType;
visibleModes: VisibleModesType;
readOnlyMode: ReadOnylModeType;
imageEnhancements: ImageEnhancementsType;
}

/**
Expand Down Expand Up @@ -294,6 +301,7 @@ function dummyState(): State {
},
visibleModes: ref(['rectangle', 'text'] as VisibleAnnotationTypes[]),
readOnlyMode: ref(false),
imageEnhancements: ref({}),
};
}

Expand Down Expand Up @@ -330,6 +338,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);
}

Expand Down Expand Up @@ -441,6 +450,9 @@ function useVisibleModes() {
function useReadOnlyMode() {
return use<ReadOnylModeType>(ReadOnlyModeSymbol);
}
function useImageEnhancements() {
return use<ImageEnhancementsType>(ImageEnhancementsSymbol);
}

export {
dummyHandler,
Expand Down Expand Up @@ -472,4 +484,5 @@ export {
useTime,
useVisibleModes,
useReadOnlyMode,
useImageEnhancements,
};
2 changes: 2 additions & 0 deletions client/src/use/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
46 changes: 46 additions & 0 deletions client/src/use/useImageEnhancements.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

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<ImageEnhancements> = 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,
};
}
2 changes: 1 addition & 1 deletion docs/UI-Timeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ The timeline provides a control bar and a few different temporal visualizations.
* ==:material-skip-previous:== ==:material-play:== ==:material-skip-next:== are standard media playback controls.
* ==:material-lock-open:== will enable camera lock, which causes the annotation view to auto-zoom and pan to whatever annotation is currently selected. This is useful when reviewing the output of a pipeline.
* ==:material-image-filter-center-focus:== or the ++r++ key will reset zoom/pan in the annotation view.

* ==:material-contrast-box:== will open the image contrast adjustment panel.
## Detection Count

![Timeline View](images/Timeline/TimelineView.png)
Expand Down