From 4df8f232a1e150d8fc6c0a30aa9cbbf6ff0e115c Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Thu, 18 Apr 2019 11:33:05 +0300 Subject: [PATCH 1/4] Some client saving fixes --- .../static/engine/js/annotationSaver.js | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/cvat/apps/engine/static/engine/js/annotationSaver.js b/cvat/apps/engine/static/engine/js/annotationSaver.js index 40759d8d5ab3..b3ce668dc3fb 100644 --- a/cvat/apps/engine/static/engine/js/annotationSaver.js +++ b/cvat/apps/engine/static/engine/js/annotationSaver.js @@ -24,11 +24,14 @@ class AnnotationSaverModel extends Listener { this._hash = this._getHash(); - for (const shape of initialData.shapes) { + // We need use data from export instead of initialData + // Otherwise we have differ keys order and JSON comparison code incorrect + const data = this._shapeCollection.export()[0]; + for (const shape of data.shapes) { this._initialObjects[shape.id] = shape; } - for (const track of initialData.tracks) { + for (const track of data.tracks) { this._initialObjects[track.id] = track; } } @@ -177,6 +180,7 @@ class AnnotationSaverModel extends Listener { } _updateCreatedObjects(objectsToSave, savedObjects, mapping) { + // Method setups IDs of created objects after saving on a server const allSavedObjects = savedObjects.shapes.concat(savedObjects.tracks); const allObjectsToSave = objectsToSave.shapes.concat(objectsToSave.tracks); if (allSavedObjects.length !== allObjectsToSave.length) { @@ -185,7 +189,9 @@ class AnnotationSaverModel extends Listener { for (let idx = 0; idx < allSavedObjects.length; idx += 1) { const objectModel = mapping.filter(el => el[0] === allObjectsToSave[idx])[0][1]; - objectModel.serverID = allSavedObjects[idx].id; + const { id } = allSavedObjects[idx]; + objectModel.serverID = id; + allObjectsToSave[idx].id = id; } this._shapeCollection.update(); @@ -218,6 +224,7 @@ class AnnotationSaverModel extends Listener { const savedObjects = await this._put(data); this._updateCreatedObjects(exported, savedObjects, mapping); this._shapeCollection.flush = false; + this._version = savedObjects.version; for (const object of savedObjects.shapes.concat(savedObjects.tracks)) { this._initialObjects[object.id] = object; } @@ -228,13 +235,15 @@ class AnnotationSaverModel extends Listener { this.notify('saveCreated'); const savedCreated = await this._create(created); this._updateCreatedObjects(created, savedCreated, mapping); - for (const object of savedCreated.shapes.concat(savedCreated.tracks)) { + this._version = savedCreated.version; + for (const object of created.shapes.concat(created.tracks)) { this._initialObjects[object.id] = object; } this.notify('saveUpdated'); const savedUpdated = await this._update(updated); - for (const object of savedUpdated.shapes.concat(savedUpdated.tracks)) { + this._version = savedUpdated.version; + for (const object of updated.shapes.concat(updated.tracks)) { if (object.id in this._initialObjects) { this._initialObjects[object.id] = object; } @@ -242,6 +251,7 @@ class AnnotationSaverModel extends Listener { this.notify('saveDeleted'); const savedDeleted = await this._delete(deleted); + this._version = savedDeleted.version; for (const object of savedDeleted.shapes.concat(savedDeleted.tracks)) { if (object.id in this._initialObjects) { delete this._initialObjects[object.id]; From 3bd1904840a9c0df7dfbfec43bdfbb465b77c466 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Thu, 18 Apr 2019 13:08:32 +0300 Subject: [PATCH 2/4] Dump fixes --- cvat/apps/engine/annotation.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/cvat/apps/engine/annotation.py b/cvat/apps/engine/annotation.py index c2a13b1e292a..07aea695ba2a 100644 --- a/cvat/apps/engine/annotation.py +++ b/cvat/apps/engine/annotation.py @@ -555,6 +555,11 @@ def _init_tracks_from_db(self): 'trackedshapeattributeval__id', ] }, 'id') + db_track["labeledtrackattributeval_set"] = list(set(db_track["labeledtrackattributeval_set"])) + for db_shape in db_track["trackedshape_set"]: + db_shape["trackedshapeattributeval_set"] = list( + set(db_shape["trackedshapeattributeval_set"]) + ) serializer = serializers.LabeledTrackSerializer(db_tracks, many=True) self.data["tracks"] = serializer.data @@ -1099,15 +1104,17 @@ def interpolate(shape0, shape1): curr_frame = track["shapes"][0]["frame"] prev_shape = {} for shape in track["shapes"]: - if shape["frame"] != curr_frame: + if prev_shape: assert shape["frame"] > curr_frame + for attr in prev_shape["attributes"]: + if attr["spec_id"] not in map(lambda el: el["spec_id"], shape["attributes"]): + shape["attributes"].append(copy.deepcopy(attr)) if not prev_shape["outside"]: shapes.extend(interpolate(prev_shape, shape)) - if not shape["outside"]: - shape["keyframe"] = True - shapes.append(shape) - curr_frame = shape["frame"] + 1 + shape["keyframe"] = True + shapes.append(shape) + curr_frame = shape["frame"] prev_shape = shape # TODO: Need to modify a client and a database (append "outside" shapes for polytracks) From da74c0858a7fade36f09f65445de6bc059f2ed0d Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Thu, 18 Apr 2019 13:34:21 +0300 Subject: [PATCH 3/4] Added empty attributes --- cvat/apps/engine/annotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cvat/apps/engine/annotation.py b/cvat/apps/engine/annotation.py index 07aea695ba2a..2120e6a33af4 100644 --- a/cvat/apps/engine/annotation.py +++ b/cvat/apps/engine/annotation.py @@ -929,8 +929,8 @@ def to_tracks(self): shape0 = copy.copy(shape) shape0["keyframe"] = True shape0["outside"] = False + shape0["attributes"] = [] shape0.pop("group", None) - shape0.pop("attributes") shape1 = copy.copy(shape0) shape1["outside"] = True shape1["frame"] += 1 From 4cbdce484aaec9d5f98d5c4e2bb2f4d4b89d2187 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Thu, 18 Apr 2019 14:11:02 +0300 Subject: [PATCH 4/4] Couple of comments --- cvat/apps/engine/annotation.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cvat/apps/engine/annotation.py b/cvat/apps/engine/annotation.py index 2120e6a33af4..862ef2913c03 100644 --- a/cvat/apps/engine/annotation.py +++ b/cvat/apps/engine/annotation.py @@ -165,7 +165,7 @@ class dotdict(OrderedDict): for key in keys_for_merge: item = dotdict({v.split('__', 1)[-1]:row[v] for v in keys_for_merge[key]}) - if item.id: + if item.id is not None: merged_rows[row_id][key].append(item) # Remove redundant keys from final objects @@ -555,6 +555,9 @@ def _init_tracks_from_db(self): 'trackedshapeattributeval__id', ] }, 'id') + + # A result table can consist many equal rows for track/shape attributes + # We need filter unique attributes manually db_track["labeledtrackattributeval_set"] = list(set(db_track["labeledtrackattributeval_set"])) for db_shape in db_track["trackedshape_set"]: db_shape["trackedshapeattributeval_set"] = list( @@ -929,6 +932,7 @@ def to_tracks(self): shape0 = copy.copy(shape) shape0["keyframe"] = True shape0["outside"] = False + # TODO: Separate attributes on mutable and unmutable shape0["attributes"] = [] shape0.pop("group", None) shape1 = copy.copy(shape0)