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
2 changes: 1 addition & 1 deletion client/dive-common/components/TrackDetailsPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ export default defineComponent({
<span class="trackNumber">{{ editingGroup.id }}</span>
<v-spacer />
<TypePicker
:value="editingGroup.getType()[0]"
:value="editingGroup.getType()"
:all-types="allGroupTypesRef"
:read-only-mode="readOnlyMode"
data-list-source="allGroupTypesOptions"
Expand Down
27 changes: 25 additions & 2 deletions client/dive-common/components/Viewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,18 @@ export default defineComponent({
const removeGroups = (id: AnnotationId) => {
cameraStore.removeGroups(id);
};
const setTrackType = (id: AnnotationId, newType: string,
confidenceVal?: number, currentType?: string) => {
cameraStore.setTrackType(id, newType, confidenceVal, currentType);
};
const removeTypes = (id: AnnotationId, types: string[]) => cameraStore.removeTypes(id, types);
const getTracksMerged = (id: AnnotationId) => cameraStore.getTracksMerged(id);
const groupFilters = new GroupFilterControls({
sorted: cameraStore.sortedGroups,
markChangesPending,
remove: removeGroups,
setType: setTrackType,
removeTypes,
});

// This context for removal
Expand All @@ -180,6 +188,8 @@ export default defineComponent({
markChangesPending,
lookupGroups: cameraStore.lookupGroups,
groupFilterControls: groupFilters,
setType: setTrackType,
removeTypes,
});

clientSettingsSetup(trackFilters.allTypes);
Expand Down Expand Up @@ -251,6 +261,7 @@ export default defineComponent({
enabledTracks: trackFilters.enabledAnnotations,
selectedTrackIds: allSelectedIds,
typeStyling: trackStyleManager.typeStyling,
getTracksMerged,
});

const { eventChartData: groupChartData } = useEventChart({
Expand All @@ -262,6 +273,7 @@ export default defineComponent({
}
return [];
}),
getTracksMerged,
});

async function trackSplit(trackId: AnnotationId | null, frame: number) {
Expand Down Expand Up @@ -538,6 +550,11 @@ export default defineComponent({
const trackStore = cameraStore.camMap.value.get(camera)?.trackStore;
const groupStore = cameraStore.camMap.value.get(camera)?.groupStore;
if (trackStore && groupStore) {
// We can start sorting if our total tracks are less than 20000
// If greater we do one sort at the end instead to speed loading.
if (tracks.length < 20000) {
trackStore.setEnableSorting();
}
for (let j = 0; j < tracks.length; j += 1) {
if (j % 4000 === 0) {
/* Every N tracks, yeild some cycles for other scheduled tasks */
Expand All @@ -558,13 +575,19 @@ export default defineComponent({
}
}
}
progress.loaded = true;
cameraStore.camMap.value.forEach((_cam, key) => {
cameraStore.camMap.value.forEach((cam, key) => {
const { trackStore } = cam;
// Enable Sorting after loading is complete if it isn't enabled already
if (trackStore) {
trackStore.setEnableSorting();
}

if (!multiCamList.value.includes(key)) {
cameraStore.removeCamera(key);
removeSaveCamera(key);
}
});
progress.loaded = true;
// If multiCam add Tools and remove group Tools
if (cameraStore.camMap.value.size > 1) {
context.unregister({
Expand Down
8 changes: 4 additions & 4 deletions client/dive-common/use/useModeManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@ import { AggregateMediaController } from 'vue-media-annotator/components/annotat
import Recipe from 'vue-media-annotator/recipe';
import type { AnnotationId } from 'vue-media-annotator/BaseAnnotation';
import type TrackFilterControls from 'vue-media-annotator/TrackFilterControls';
import BaseAnnotation from 'vue-media-annotator/BaseAnnotation';

import { usePrompt } from 'dive-common/vue-utilities/prompt-service';
import { clientSettings } from 'dive-common/store/settings';
import GroupFilterControls from 'vue-media-annotator/GroupFilterControls';
import CameraStore from 'vue-media-annotator/CameraStore';
import { SortedAnnotation } from 'vue-media-annotator/BaseAnnotationStore';


type SupportedFeature = GeoJSON.Feature<GeoJSON.Point | GeoJSON.Polygon | GeoJSON.LineString>;

/* default to index + 1
* call with -1 to select previous, or pass any other delta
*/
function selectNext<T extends BaseAnnotation>(
function selectNext<T extends SortedAnnotation>(
filtered: Readonly<T>[], selected: Readonly<AnnotationId | null>, delta = 1,
): AnnotationId | null {
if (filtered.length > 0) {
Expand Down Expand Up @@ -334,7 +334,7 @@ export default function useModeManager({
frame.value, trackType,
selectedTrackId.value || undefined,
overrideTrackId,
).trackId;
).id;
selectTrack(newTrackId, true);
creating = true;
return newTrackId;
Expand Down Expand Up @@ -684,7 +684,7 @@ export default function useModeManager({
));
handleRemoveTrack(otherTrackIds, true);
handleToggleMerge();
handleSelectTrack(track.trackId, false);
handleSelectTrack(track.id, false);
}
}

Expand Down
6 changes: 5 additions & 1 deletion client/platform/desktop/frontend/components/ImportDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ export default defineComponent({
type: Object as PropType<DesktopMediaImportResponse>,
required: true,
},
disabled: {
type: Boolean,
default: false,
},
},
setup(props) {
const argCopy = ref(cloneDeep(props.importData));
Expand Down Expand Up @@ -316,7 +320,7 @@ export default defineComponent({
</v-btn>
<v-btn
color="primary"
:disabled="!ready"
:disabled="!ready || disabled"
@click="$emit('finalize-import', argCopy)"
>
Finish Import
Expand Down
5 changes: 5 additions & 0 deletions client/platform/desktop/frontend/components/Recent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export default defineComponent({
const searchText: Ref<string | null> = ref('');
const stereo = ref(false);
const multiCamOpenType: Ref<'image-sequence'|'video'> = ref('image-sequence');
const importing = ref(false);
const { prompt } = usePrompt();
const {
error, loading: checkingMedia, request, reset: resetError,
Expand All @@ -61,6 +62,7 @@ export default defineComponent({

/** Accept args from the dialog, as it may have modified some parts */
async function finalizeImport(args: DesktopMediaImportResponse) {
importing.value = true;
await request(async () => {
const jsonMeta = await api.finalizeImport(args);
pendingImportPayload.value = null; // close dialog
Expand All @@ -75,6 +77,7 @@ export default defineComponent({
setRecents(recentsMeta);
}
});
importing.value = false;
}

function openMultiCamDialog(args: {stereo: boolean; openType: 'image-sequence' | 'video'}) {
Expand Down Expand Up @@ -195,6 +198,7 @@ export default defineComponent({
pendingImportPayload,
searchText,
error,
importing,
importMultiCamDialog,
headers,
upgradedVersion,
Expand All @@ -220,6 +224,7 @@ export default defineComponent({
<ImportDialog
v-if="pendingImportPayload !== null"
:import-data="pendingImportPayload"
:disabled="importing"
@finalize-import="finalizeImport($event)"
@abort="pendingImportPayload = null"
/>
Expand Down
35 changes: 32 additions & 3 deletions client/src/BaseAnnotationStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ref, Ref, computed } from '@vue/composition-api';
import IntervalTree from '@flatten-js/interval-tree';
import type Track from './track';
import type Group from './Group';
import type { AnnotationId, NotifierFuncParams } from './BaseAnnotation';
import type { AnnotationId, ConfidencePair, NotifierFuncParams } from './BaseAnnotation';

export type MarkChangesPending = ({
action,
Expand All @@ -21,6 +21,15 @@ export interface InsertArgs {
afterId?: AnnotationId;
}

//A subset of Track so copies of full track aren't passed around
export interface SortedAnnotation {
id: AnnotationId;
begin: number;
end: number;
confidencePairs: ConfidencePair[];
getType: (index?: number) => string;
}

function isTrack(value: Track | Group): value is Track {
return (value as Track).features !== undefined;
}
Expand Down Expand Up @@ -55,12 +64,14 @@ export default abstract class BaseAnnotationStore<T extends Track | Group> {
*/
annotationIds: Ref<AnnotationId[]>;

sorted: Ref<OneOf<T, [Group, Track]>[]>;
sorted: Ref<SortedAnnotation[]>;

cameraName: string;

private canary: Ref<number>;

enableSorting: Ref<boolean>; //Sorting is false to start with

constructor({ markChangesPending, cameraName }:
{ markChangesPending: MarkChangesPending; cameraName: string }) {
this.markChangesPending = markChangesPending;
Expand All @@ -69,10 +80,24 @@ export default abstract class BaseAnnotationStore<T extends Track | Group> {
this.annotationIds = ref([]);
this.intervalTree = new IntervalTree();
this.canary = ref(0);
this.enableSorting = ref(false);
this.sorted = computed(() => {
this.depend();
// Prevent sorting when loading data
if (!this.enableSorting.value) {
return [];
}
return this.annotationIds.value
.map((trackId) => this.get(trackId))
.map((trackId) => {
const track = this.get(trackId);
return {
begin: track.begin,
end: track.end,
id: track.id,
confidencePairs: track.confidencePairs,
getType: (index?: number) => ((track.confidencePairs[index || 0][0]) || 'unknown'),
};
})
.sort((a, b) => a.begin - b.begin);
});
}
Expand All @@ -87,6 +112,10 @@ export default abstract class BaseAnnotationStore<T extends Track | Group> {
return this.canary.value;
}

setEnableSorting() {
this.enableSorting.value = true;
}

get(annotationId: AnnotationId) {
const value = this.annotationMap.get(annotationId);
if (value === undefined) {
Expand Down
25 changes: 19 additions & 6 deletions client/src/BaseFilterControls.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {
ref, computed, Ref, watch,
} from '@vue/composition-api';
import type { AnnotationId } from './BaseAnnotation';
import type { AnnotationId, ConfidencePair } from './BaseAnnotation';
import { SortedAnnotation } from './BaseAnnotationStore';
import type Group from './Group';
import type Track from './track';
import { updateSubset } from './utils';
Expand All @@ -13,17 +14,20 @@ export const DefaultConfidence = 0.1;
* or function.
*/
export interface AnnotationWithContext<T extends Track | Group> {
annotation: Readonly<OneOf<T, [Track, Group]>>;
annotation: Readonly<SortedAnnotation>;
context: {
// confidencePair index within annotation that makes this annotation a positive filter result
confidencePairIndex: number;
};
}

export interface FilterControlsParams<T extends Track | Group> {
sorted: Ref<T[]>;
sorted: Ref<SortedAnnotation[]>;
markChangesPending: () => void;
remove: (id: AnnotationId) => void;
setType: (id: AnnotationId, newType: string,
confidenceVal?: number, currentType?: string) => void;
removeTypes: (id: AnnotationId, types: string[]) => ConfidencePair[];
}

export type TrackWithContext = AnnotationWithContext<Track>;
Expand Down Expand Up @@ -59,10 +63,15 @@ export default abstract class BaseFilterControls<T extends Track | Group> {
private markChangesPending: () => void;

/* Hold a reference to the annotationStore */
sorted: Readonly<Ref<T[]>>;
sorted: Readonly<Ref<SortedAnnotation[]>>;

remove: (id: AnnotationId) => void;

setType: (id: AnnotationId, newType: string,
confidenceVal?: number, currentType?: string) => void;

removeTypes: (id: AnnotationId, types: string[]) => ConfidencePair[];

constructor(params: FilterControlsParams<T>) {
this.checkedIDs = ref(params.sorted.value.map((t) => t.id));

Expand All @@ -74,6 +83,10 @@ export default abstract class BaseFilterControls<T extends Track | Group> {

this.remove = params.remove;

this.setType = params.setType;

this.removeTypes = params.removeTypes;

this.markChangesPending = params.markChangesPending;

this.allTypes = computed(() => {
Expand Down Expand Up @@ -165,7 +178,7 @@ export default abstract class BaseFilterControls<T extends Track | Group> {
for (let i = 0; i < annotation.confidencePairs.length; i += 1) {
const [name, confidenceVal] = annotation.confidencePairs[i];
if (name === currentType) {
annotation.setType(newType, confidenceVal, currentType);
this.setType(annotation.id, newType, confidenceVal, currentType);
break;
}
}
Expand All @@ -184,7 +197,7 @@ export default abstract class BaseFilterControls<T extends Track | Group> {
const filteredType = filtered.annotation.getType(filtered.context.confidencePairIndex);
if (filteredType && types.includes(filteredType[0])) {
//Remove the type from the annotation if multiple types exist
const newConfidencePairs = filtered.annotation.removeTypes(types);
const newConfidencePairs = this.removeTypes(filtered.annotation.id, types);
if (newConfidencePairs.length === 0) {
this.remove(filtered.annotation.id);
}
Expand Down
Loading