Skip to content

Commit 180b758

Browse files
committed
add trackJSON to desktop exporting
1 parent ec6de7f commit 180b758

File tree

7 files changed

+111
-18
lines changed

7 files changed

+111
-18
lines changed

client/platform/desktop/backend/native/common.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,6 +1117,12 @@ async function exportDataset(settings: Settings, args: ExportDatasetArgs) {
11171117
const projectDirInfo = await getValidatedProjectDir(settings, args.id);
11181118
const meta = await loadJsonMetadata(projectDirInfo.metaFileAbsPath);
11191119
const data = await loadAnnotationFile(projectDirInfo.trackFileAbsPath);
1120+
if (args.type === 'json') {
1121+
return dive.serializeFile(args.path, data, meta, args.typeFilter, {
1122+
excludeBelowThreshold: args.exclude,
1123+
header: true,
1124+
});
1125+
}
11201126
return viameSerializers.serializeFile(args.path, data, meta, args.typeFilter, {
11211127
excludeBelowThreshold: args.exclude,
11221128
header: true,

client/platform/desktop/backend/serializers/dive.ts

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { AnnotationSchema, MultiTrackRecord } from 'dive-common/apispec';
22
import { has } from 'lodash';
3-
import { AnnotationsCurrentVersion } from 'platform/desktop/constants';
4-
import { TrackData, TrackId } from 'vue-media-annotator/track';
3+
import { AnnotationsCurrentVersion, JsonMeta } from 'platform/desktop/constants';
4+
import Track, { TrackData, TrackId } from 'vue-media-annotator/track';
5+
import fs from 'fs-extra';
56

67
function makeEmptyAnnotationFile(): AnnotationSchema {
78
return {
@@ -40,7 +41,56 @@ function migrate(jsonData: any): AnnotationSchema {
4041
}
4142
}
4243

44+
function filterTracks(
45+
data: AnnotationSchema,
46+
meta: JsonMeta,
47+
typeFilter = new Set<string>(),
48+
options = {
49+
excludeBelowThreshold: false,
50+
header: true,
51+
},
52+
): AnnotationSchema {
53+
const filteredTracks = Object.values(data.tracks).filter((track) => {
54+
const filters = meta.confidenceFilters || {};
55+
/* Include only the pairs that exceed the threshold in CSV output */
56+
const confidencePairs = options.excludeBelowThreshold
57+
? Track.exceedsThreshold(track.confidencePairs, filters)
58+
: track.confidencePairs;
59+
const filteredPairs = typeFilter.size > 0
60+
? confidencePairs.filter((x) => typeFilter.has(x[0]))
61+
: confidencePairs;
62+
return filteredPairs.length > 0;
63+
});
64+
// Convert the track list back into an object
65+
const updatedFilteredTracks: Record<number, Track> = {};
66+
filteredTracks.forEach((track) => {
67+
updatedFilteredTracks[track.id] = track;
68+
});
69+
const updatedData = { ...data };
70+
updatedData.tracks = updatedFilteredTracks;
71+
// Write out the tracks to a file
72+
return updatedData;
73+
}
74+
75+
async function serializeFile(
76+
path: string,
77+
data: AnnotationSchema,
78+
meta: JsonMeta,
79+
typeFilter = new Set<string>(),
80+
options = {
81+
excludeBelowThreshold: false,
82+
header: true,
83+
},
84+
) {
85+
const updatedData = filterTracks(data, meta, typeFilter, options);
86+
// write updatedData JSON to a path
87+
const jsonData = JSON.stringify(updatedData, null, 2);
88+
await fs.writeFile(path, jsonData, 'utf8');
89+
}
90+
4391
export {
4492
makeEmptyAnnotationFile,
4593
migrate,
94+
filterTracks,
95+
serializeFile,
4696
};

client/platform/desktop/backend/serializers/viame.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -553,9 +553,8 @@ async function serialize(
553553
Object.entries(feature.attributes || {}).forEach(([key, val]) => {
554554
row.push(`${AtrToken} ${key} ${val}`);
555555
});
556-
557556
/* Track Attributes */
558-
Object.entries(track.attributes).forEach(([key, val]) => {
557+
Object.entries(track.attributes || {}).forEach(([key, val]) => {
559558
row.push(`${TrackAtrToken} ${key} ${val}`);
560559
});
561560

client/platform/desktop/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ export interface ExportDatasetArgs {
220220
exclude: boolean;
221221
path: string;
222222
typeFilter: Set<string>;
223+
type?: 'csv' | 'json';
223224
}
224225

225226
export interface ExportConfigurationArgs {

client/platform/desktop/frontend/api.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,15 +144,16 @@ function finalizeImport(args: DesktopMediaImportResponse): Promise<JsonMeta> {
144144
}
145145

146146
async function exportDataset(
147-
id: string, exclude: boolean, typeFilter: readonly string[],
147+
id: string, exclude: boolean, typeFilter: readonly string[], type?: 'csv' | 'json',
148148
): Promise<string> {
149+
console.log(`Export Type: ${type}`);
149150
const location = await dialog.showSaveDialog({
150151
title: 'Export Dataset',
151-
defaultPath: npath.join(app.getPath('home'), `result_${id}.csv`),
152+
defaultPath: npath.join(app.getPath('home'), type === 'json' ? `result_${id}.json` : `result_${id}.csv`),
152153
});
153154
if (!location.canceled && location.filePath) {
154155
const args: ExportDatasetArgs = {
155-
id, exclude, path: location.filePath, typeFilter: new Set(typeFilter),
156+
id, exclude, path: location.filePath, typeFilter: new Set(typeFilter), type,
156157
};
157158
return ipcRenderer.invoke('export-dataset', args);
158159
}

client/platform/desktop/frontend/components/Export.vue

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export default defineComponent({
5454
? Object.keys(data.meta.confidenceFilters || {})
5555
: []));
5656
57-
async function doExport({ type, forceSave = false }: { type: 'dataset' | 'configuration'; forceSave?: boolean}) {
57+
async function doExport({ type, forceSave = false }: { type: 'dataset' | 'configuration' | 'trackJSON'; forceSave?: boolean}) {
5858
if (pendingSaveCount.value > 0 && forceSave) {
5959
await save();
6060
savePrompt.value = false;
@@ -67,6 +67,10 @@ export default defineComponent({
6767
const typeFilter = data.excludeUncheckedTypes ? checkedTypes.value : [];
6868
data.err = null;
6969
data.outPath = await exportDataset(props.id, data.excludeBelowThreshold, typeFilter);
70+
} else if (type === 'trackJSON') {
71+
const typeFilter = data.excludeUncheckedTypes ? checkedTypes.value : [];
72+
data.err = null;
73+
data.outPath = await exportDataset(props.id, data.excludeBelowThreshold, typeFilter, 'json');
7074
} else if (type === 'configuration') {
7175
data.outPath = await exportConfiguration(props.id);
7276
}
@@ -169,7 +173,7 @@ export default defineComponent({
169173
>
170174
Export succeeded.
171175
</v-alert>
172-
<div>Export to VIAME CSV format</div>
176+
<div>Export to Annotations</div>
173177
<template v-if="thresholds.length">
174178
<v-checkbox
175179
v-model="data.excludeBelowThreshold"
@@ -204,14 +208,26 @@ export default defineComponent({
204208
</template>
205209
</v-card-text>
206210
<v-card-actions>
207-
<v-spacer />
208-
<v-btn
209-
depressed
210-
block
211-
@click="doExport({ type: 'dataset' })"
212-
>
213-
<span>VIAME CSV</span>
214-
</v-btn>
211+
<v-row>
212+
<v-col>
213+
<v-btn
214+
depressed
215+
block
216+
class="my-1"
217+
@click="doExport({ type: 'dataset' })"
218+
>
219+
<span>VIAME CSV</span>
220+
</v-btn>
221+
<v-btn
222+
depressed
223+
block
224+
class="my-1"
225+
@click="doExport({ type: 'trackJSON' })"
226+
>
227+
<span>TRACK JSON</span>
228+
</v-btn>
229+
</v-col>
230+
</v-row>
215231
</v-card-actions>
216232
<v-card-text class="pb-0">
217233
Export the dataset configuration, including

server/dive_server/crud_dataset.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,27 @@ def makeMetajson():
289289
def makeDiveJson():
290290
"""Include DIVE JSON output annotation file"""
291291
annotations = crud_annotation.get_annotations(dsFolder)
292-
print(annotations)
292+
tracks = annotations['tracks']
293+
294+
if excludeBelowThreshold:
295+
thresholds = fromMeta(dsFolder, "confidenceFilters", {})
296+
if thresholds is None:
297+
thresholds = {}
298+
299+
updated_tracks = []
300+
if typeFilter is None:
301+
typeFilter = set()
302+
for t in tracks:
303+
track = models.Track(**t)
304+
if (not excludeBelowThreshold) or track.exceeds_thresholds(thresholds):
305+
# filter by types if applicable
306+
if typeFilter:
307+
confidence_pairs = [item for item in track.confidencePairs if item[0] in typeFilter]
308+
# skip line if no confidence pairs
309+
if not confidence_pairs:
310+
continue
311+
updated_tracks.append[track]
312+
annotations['tracks'] = updated_tracks
293313
yield json.dumps(annotations)
294314

295315
for data in z.addFile(makeMetajson, Path(f'{zip_path}meta.json')):

0 commit comments

Comments
 (0)