From 6b9e05b125bf034f34702e69f8be16f8a8dab8d4 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Tue, 4 Apr 2023 11:22:31 -0700 Subject: [PATCH 01/19] Tried to optimize annotation fetching --- cvat/apps/dataset_manager/task.py | 21 +++++++++++++++++++-- cvat/apps/engine/mixins.py | 3 +-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/cvat/apps/dataset_manager/task.py b/cvat/apps/dataset_manager/task.py index 058d282a3ffd..b721e86db520 100644 --- a/cvat/apps/dataset_manager/task.py +++ b/cvat/apps/dataset_manager/task.py @@ -453,8 +453,25 @@ def _init_shapes_from_db(self): for shape_id, shape_elements in elements.items(): shapes[shape_id].elements = shape_elements - serializer = serializers.LabeledShapeSerializer(list(shapes.values()), many=True) - self.ir_data.shapes = serializer.data + def convert_shape(value): + value['attributes'] = value['labeledshapeattributeval_set'] + del value['labeledshapeattributeval_set'] + for attr in value['attributes']: + del attr['id'] + + keys = list(value.keys()) + for field in keys: + if value[field] is None: + del value[field] + + if value['elements']: + for element in value['elements']: + convert_shape(element) + + return value + + values = [convert_shape(value) for value in shapes.values()] + self.ir_data.shapes = values def _init_tracks_from_db(self): db_tracks = self.db_job.labeledtrack_set.prefetch_related( diff --git a/cvat/apps/engine/mixins.py b/cvat/apps/engine/mixins.py index 5d4739f757e0..a36fb8b50bbb 100644 --- a/cvat/apps/engine/mixins.py +++ b/cvat/apps/engine/mixins.py @@ -279,8 +279,7 @@ def export_annotations(self, request, db_obj, export_func, callback, get_data=No data = get_data(self._object.pk) serializer = LabeledDataSerializer(data=data) - if serializer.is_valid(raise_exception=True): - return Response(serializer.data) + return Response(serializer.initial_data) def import_annotations(self, request, db_obj, import_func, rq_func, rq_id): is_tus_request = request.headers.get('Upload-Length', None) is not None or \ From ec584168db5cc4bf209c8cebc464852eadc82e9d Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Tue, 4 Apr 2023 12:18:53 -0700 Subject: [PATCH 02/19] Do not validate when backup --- cvat/apps/engine/backup.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cvat/apps/engine/backup.py b/cvat/apps/engine/backup.py index 98c6fcc5457b..b08ba01903d5 100644 --- a/cvat/apps/engine/backup.py +++ b/cvat/apps/engine/backup.py @@ -386,8 +386,7 @@ def serialize_annotations(): for db_job_id in db_job_ids: annotations = dm.task.get_job_data(db_job_id) annotations_serializer = LabeledDataSerializer(data=annotations) - annotations_serializer.is_valid(raise_exception=True) - job_annotations.append(self._prepare_annotations(annotations_serializer.data, self._label_mapping)) + job_annotations.append(self._prepare_annotations(annotations_serializer.initial_data, self._label_mapping)) return job_annotations From 082b0b4f907d0fc20c924ac65bce0c0a3acfc1b9 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Tue, 4 Apr 2023 13:38:33 -0700 Subject: [PATCH 03/19] Implemented for other types --- cvat/apps/dataset_manager/task.py | 71 ++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 16 deletions(-) diff --git a/cvat/apps/dataset_manager/task.py b/cvat/apps/dataset_manager/task.py index b721e86db520..4b08e5effd1d 100644 --- a/cvat/apps/dataset_manager/task.py +++ b/cvat/apps/dataset_manager/task.py @@ -366,6 +366,7 @@ def _extend_attributes(attributeval_set, default_attribute_values): attributeval_set.append(dotdict([ ('spec_id', db_attr.spec_id), ('value', db_attr.value), + ('id', None), ])) def _init_tags_from_db(self): @@ -399,8 +400,18 @@ def _init_tags_from_db(self): self._extend_attributes(db_tag.labeledimageattributeval_set, self.db_attributes[db_tag.label_id]["all"].values()) - serializer = serializers.LabeledImageSerializer(db_tags, many=True) - self.ir_data.tags = serializer.data + def convert_tag(value): + value['attributes'] = value['labeledshapeattributeval_set'] + del value['labeledshapeattributeval_set'] + for attr in value['attributes']: + del attr['id'] + + keys = list(value.keys()) + for field in keys: + if value[field] is None: + del value[field] + + self.ir_data.tags = [convert_tag(value) for value in db_tags] def _init_shapes_from_db(self): db_shapes = self.db_job.labeledshape_set.prefetch_related( @@ -453,25 +464,24 @@ def _init_shapes_from_db(self): for shape_id, shape_elements in elements.items(): shapes[shape_id].elements = shape_elements - def convert_shape(value): - value['attributes'] = value['labeledshapeattributeval_set'] - del value['labeledshapeattributeval_set'] - for attr in value['attributes']: + def convert_shape(shape): + shape['attributes'] = shape['labeledshapeattributeval_set'] + del shape['labeledshapeattributeval_set'] + for attr in shape['attributes']: del attr['id'] - keys = list(value.keys()) + keys = list(shape.keys()) for field in keys: - if value[field] is None: - del value[field] + if shape[field] is None: + del shape[field] - if value['elements']: - for element in value['elements']: + if 'elements' in shape: + for element in shape['elements']: convert_shape(element) - return value + return shape - values = [convert_shape(value) for value in shapes.values()] - self.ir_data.shapes = values + self.ir_data.shapes = [convert_shape(value) for value in shapes.values()] def _init_tracks_from_db(self): db_tracks = self.db_job.labeledtrack_set.prefetch_related( @@ -529,6 +539,7 @@ def _init_tracks_from_db(self): tracks = {} elements = {} for db_track in db_tracks: + db_track['elements'] = [] db_track["trackedshape_set"] = _merge_table_rows(db_track["trackedshape_set"], { 'trackedshapeattributeval_set': [ 'trackedshapeattributeval__value', @@ -563,8 +574,36 @@ def _init_tracks_from_db(self): for track_id, track_elements in elements.items(): tracks[track_id].elements = track_elements - serializer = serializers.LabeledTrackSerializer(list(tracks.values()), many=True) - self.ir_data.tracks = serializer.data + def remove_none(dictionary): + keys = list(dictionary.keys()) + for field in keys: + if dictionary[field] is None: + del dictionary[field] + return dictionary + + def convert_track(track): + track['attributes'] = track['labeledtrackattributeval_set'] + del track['labeledtrackattributeval_set'] + for attr in track['attributes']: + del attr['id'] + + remove_none(track) + track['shapes'] = track['trackedshape_set'] + del track['trackedshape_set'] + + for shape in track['shapes']: + remove_none(shape) + shape['attributes'] = shape['trackedshapeattributeval_set'] + for attr in shape['attributes']: + del attr['id'] + + if 'elements' in track: + for element in track['elements']: + convert_track(element) + + return track + + self.ir_data.tracks = [convert_track(value) for value in tracks.values()] def _init_version_from_db(self): self.ir_data.version = 0 # FIXME: should be removed in the future From 2f95e52619f525d3c3dd3f8fda8ba1960ec978cc Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Tue, 4 Apr 2023 14:19:07 -0700 Subject: [PATCH 04/19] Reduced code dublication, fixed pylint --- cvat/apps/dataset_manager/task.py | 47 +++++++++++++------------------ 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/cvat/apps/dataset_manager/task.py b/cvat/apps/dataset_manager/task.py index 4b08e5effd1d..ba294863c06d 100644 --- a/cvat/apps/dataset_manager/task.py +++ b/cvat/apps/dataset_manager/task.py @@ -12,7 +12,7 @@ from django.db.models.query import Prefetch from django.utils import timezone -from cvat.apps.engine import models, serializers +from cvat.apps.engine import models from cvat.apps.engine.plugins import plugin_decorator from cvat.apps.profiler import silk_profile @@ -42,6 +42,13 @@ def values(cls): def __str__(self): return self.value +def _remove_none(dictionary): + keys = list(dictionary.keys()) + for field in keys: + if dictionary[field] is None: + del dictionary[field] + return dictionary + def _merge_table_rows(rows, keys_for_merge, field_id): # It is necessary to keep a stable order of original rows # (e.g. for tracked boxes). Otherwise prev_box.frame can be bigger @@ -313,6 +320,7 @@ def _create(self, data): self._set_updated_date() self.db_job.save() + def create(self, data): self._create(data) @@ -400,16 +408,13 @@ def _init_tags_from_db(self): self._extend_attributes(db_tag.labeledimageattributeval_set, self.db_attributes[db_tag.label_id]["all"].values()) - def convert_tag(value): - value['attributes'] = value['labeledshapeattributeval_set'] - del value['labeledshapeattributeval_set'] - for attr in value['attributes']: - del attr['id'] + def convert_tag(tag): + tag['attributes'] = tag['labeledshapeattributeval_set'] + del tag['labeledshapeattributeval_set'] - keys = list(value.keys()) - for field in keys: - if value[field] is None: - del value[field] + _remove_none(tag) + for attr in tag['attributes']: + del attr['id'] self.ir_data.tags = [convert_tag(value) for value in db_tags] @@ -467,14 +472,10 @@ def _init_shapes_from_db(self): def convert_shape(shape): shape['attributes'] = shape['labeledshapeattributeval_set'] del shape['labeledshapeattributeval_set'] + _remove_none(shape) for attr in shape['attributes']: del attr['id'] - keys = list(shape.keys()) - for field in keys: - if shape[field] is None: - del shape[field] - if 'elements' in shape: for element in shape['elements']: convert_shape(element) @@ -574,25 +575,17 @@ def _init_tracks_from_db(self): for track_id, track_elements in elements.items(): tracks[track_id].elements = track_elements - def remove_none(dictionary): - keys = list(dictionary.keys()) - for field in keys: - if dictionary[field] is None: - del dictionary[field] - return dictionary - def convert_track(track): + track['shapes'] = track['trackedshape_set'] + del track['trackedshape_set'] track['attributes'] = track['labeledtrackattributeval_set'] del track['labeledtrackattributeval_set'] + _remove_none(track) for attr in track['attributes']: del attr['id'] - remove_none(track) - track['shapes'] = track['trackedshape_set'] - del track['trackedshape_set'] - for shape in track['shapes']: - remove_none(shape) + _remove_none(shape) shape['attributes'] = shape['trackedshapeattributeval_set'] for attr in shape['attributes']: del attr['id'] From 6de86617f4e20a213c894b33de41043f037d20e8 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 5 Apr 2023 09:05:48 +0300 Subject: [PATCH 05/19] Update cvat/apps/dataset_manager/task.py --- cvat/apps/dataset_manager/task.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cvat/apps/dataset_manager/task.py b/cvat/apps/dataset_manager/task.py index ba294863c06d..c86e50b22ba2 100644 --- a/cvat/apps/dataset_manager/task.py +++ b/cvat/apps/dataset_manager/task.py @@ -409,8 +409,8 @@ def _init_tags_from_db(self): self.db_attributes[db_tag.label_id]["all"].values()) def convert_tag(tag): - tag['attributes'] = tag['labeledshapeattributeval_set'] - del tag['labeledshapeattributeval_set'] + tag['attributes'] = tag['labeledimageattributeval_set'] + del tag['labeledimageattributeval_set'] _remove_none(tag) for attr in tag['attributes']: From c67038fb8d16a7284010dd85197bc4c1b9e4d1e3 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 5 Apr 2023 09:05:53 +0300 Subject: [PATCH 06/19] Update cvat/apps/dataset_manager/task.py --- cvat/apps/dataset_manager/task.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cvat/apps/dataset_manager/task.py b/cvat/apps/dataset_manager/task.py index c86e50b22ba2..42da3d0eef8d 100644 --- a/cvat/apps/dataset_manager/task.py +++ b/cvat/apps/dataset_manager/task.py @@ -587,6 +587,7 @@ def convert_track(track): for shape in track['shapes']: _remove_none(shape) shape['attributes'] = shape['trackedshapeattributeval_set'] + del shape['trackedshapeattributeval_set'] for attr in shape['attributes']: del attr['id'] From 106656ff43de3479ed249bf9bba46ba56019bc09 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 5 Apr 2023 00:36:45 -0700 Subject: [PATCH 07/19] Fixed for tags --- cvat/apps/dataset_manager/task.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cvat/apps/dataset_manager/task.py b/cvat/apps/dataset_manager/task.py index ba294863c06d..c15a28b9fcc7 100644 --- a/cvat/apps/dataset_manager/task.py +++ b/cvat/apps/dataset_manager/task.py @@ -409,13 +409,15 @@ def _init_tags_from_db(self): self.db_attributes[db_tag.label_id]["all"].values()) def convert_tag(tag): - tag['attributes'] = tag['labeledshapeattributeval_set'] - del tag['labeledshapeattributeval_set'] + tag['attributes'] = tag['labeledimageattributeval_set'] + del tag['labeledimageattributeval_set'] _remove_none(tag) for attr in tag['attributes']: del attr['id'] + return tag + self.ir_data.tags = [convert_tag(value) for value in db_tags] def _init_shapes_from_db(self): From ab97880ab0d192bcf3c636380e759e23451e1891 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 5 Apr 2023 01:13:32 -0700 Subject: [PATCH 08/19] Not remove all none values --- cvat/apps/dataset_manager/task.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/cvat/apps/dataset_manager/task.py b/cvat/apps/dataset_manager/task.py index 77ab3af9556a..101626bdf4f4 100644 --- a/cvat/apps/dataset_manager/task.py +++ b/cvat/apps/dataset_manager/task.py @@ -42,11 +42,10 @@ def values(cls): def __str__(self): return self.value -def _remove_none(dictionary): - keys = list(dictionary.keys()) - for field in keys: - if dictionary[field] is None: - del dictionary[field] +def _transform_object(dictionary): + if 'parent' in dictionary: + del dictionary['parent'] + return dictionary def _merge_table_rows(rows, keys_for_merge, field_id): @@ -412,7 +411,7 @@ def convert_tag(tag): tag['attributes'] = tag['labeledimageattributeval_set'] del tag['labeledimageattributeval_set'] - _remove_none(tag) + _transform_object(tag) for attr in tag['attributes']: del attr['id'] @@ -474,7 +473,7 @@ def _init_shapes_from_db(self): def convert_shape(shape): shape['attributes'] = shape['labeledshapeattributeval_set'] del shape['labeledshapeattributeval_set'] - _remove_none(shape) + _transform_object(shape) for attr in shape['attributes']: del attr['id'] @@ -582,12 +581,12 @@ def convert_track(track): del track['trackedshape_set'] track['attributes'] = track['labeledtrackattributeval_set'] del track['labeledtrackattributeval_set'] - _remove_none(track) + _transform_object(track) for attr in track['attributes']: del attr['id'] for shape in track['shapes']: - _remove_none(shape) + _transform_object(shape) shape['attributes'] = shape['trackedshapeattributeval_set'] del shape['trackedshapeattributeval_set'] for attr in shape['attributes']: From fc37e3c5093d8dffe7bd199f0b8f140f154fdc94 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 5 Apr 2023 01:41:53 -0700 Subject: [PATCH 09/19] dotdict to ordereddict --- cvat/apps/dataset_manager/task.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cvat/apps/dataset_manager/task.py b/cvat/apps/dataset_manager/task.py index 101626bdf4f4..a471f1a6e573 100644 --- a/cvat/apps/dataset_manager/task.py +++ b/cvat/apps/dataset_manager/task.py @@ -408,14 +408,14 @@ def _init_tags_from_db(self): self.db_attributes[db_tag.label_id]["all"].values()) def convert_tag(tag): - tag['attributes'] = tag['labeledimageattributeval_set'] + tag['attributes'] = [OrderedDict(attr) for attr in tag['labeledimageattributeval_set']] del tag['labeledimageattributeval_set'] _transform_object(tag) for attr in tag['attributes']: del attr['id'] - return tag + return OrderedDict(tag) self.ir_data.tags = [convert_tag(value) for value in db_tags] @@ -471,7 +471,7 @@ def _init_shapes_from_db(self): shapes[shape_id].elements = shape_elements def convert_shape(shape): - shape['attributes'] = shape['labeledshapeattributeval_set'] + shape['attributes'] = [OrderedDict(attr) for attr in shape['labeledshapeattributeval_set']] del shape['labeledshapeattributeval_set'] _transform_object(shape) for attr in shape['attributes']: @@ -481,7 +481,7 @@ def convert_shape(shape): for element in shape['elements']: convert_shape(element) - return shape + return OrderedDict(shape) self.ir_data.shapes = [convert_shape(value) for value in shapes.values()] @@ -577,9 +577,9 @@ def _init_tracks_from_db(self): tracks[track_id].elements = track_elements def convert_track(track): - track['shapes'] = track['trackedshape_set'] + track['shapes'] = [OrderedDict(attr) for attr in track['trackedshape_set']] del track['trackedshape_set'] - track['attributes'] = track['labeledtrackattributeval_set'] + track['attributes'] = [OrderedDict(attr) for attr in track['labeledtrackattributeval_set']] del track['labeledtrackattributeval_set'] _transform_object(track) for attr in track['attributes']: @@ -587,7 +587,7 @@ def convert_track(track): for shape in track['shapes']: _transform_object(shape) - shape['attributes'] = shape['trackedshapeattributeval_set'] + shape['attributes'] = [OrderedDict(attr) for attr in shape['trackedshapeattributeval_set']] del shape['trackedshapeattributeval_set'] for attr in shape['attributes']: del attr['id'] @@ -596,7 +596,7 @@ def convert_track(track): for element in track['elements']: convert_track(element) - return track + return OrderedDict(track) self.ir_data.tracks = [convert_track(value) for value in tracks.values()] From dfebda230a0459e9e0505f579b728eb6f3df24ef Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 5 Apr 2023 02:54:25 -0700 Subject: [PATCH 10/19] Logic moved to serializers --- cvat/apps/dataset_manager/task.py | 56 ++++------------------------- cvat/apps/engine/serializers.py | 58 ++++++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 50 deletions(-) diff --git a/cvat/apps/dataset_manager/task.py b/cvat/apps/dataset_manager/task.py index a471f1a6e573..bbfa07aeb3eb 100644 --- a/cvat/apps/dataset_manager/task.py +++ b/cvat/apps/dataset_manager/task.py @@ -12,7 +12,7 @@ from django.db.models.query import Prefetch from django.utils import timezone -from cvat.apps.engine import models +from cvat.apps.engine import models, serializers from cvat.apps.engine.plugins import plugin_decorator from cvat.apps.profiler import silk_profile @@ -407,17 +407,8 @@ def _init_tags_from_db(self): self._extend_attributes(db_tag.labeledimageattributeval_set, self.db_attributes[db_tag.label_id]["all"].values()) - def convert_tag(tag): - tag['attributes'] = [OrderedDict(attr) for attr in tag['labeledimageattributeval_set']] - del tag['labeledimageattributeval_set'] - - _transform_object(tag) - for attr in tag['attributes']: - del attr['id'] - - return OrderedDict(tag) - - self.ir_data.tags = [convert_tag(value) for value in db_tags] + serializer = serializers.LabeledImageSerializerFromDB(db_tags, many=True) + self.ir_data.tags = serializer.data def _init_shapes_from_db(self): db_shapes = self.db_job.labeledshape_set.prefetch_related( @@ -470,20 +461,8 @@ def _init_shapes_from_db(self): for shape_id, shape_elements in elements.items(): shapes[shape_id].elements = shape_elements - def convert_shape(shape): - shape['attributes'] = [OrderedDict(attr) for attr in shape['labeledshapeattributeval_set']] - del shape['labeledshapeattributeval_set'] - _transform_object(shape) - for attr in shape['attributes']: - del attr['id'] - - if 'elements' in shape: - for element in shape['elements']: - convert_shape(element) - - return OrderedDict(shape) - - self.ir_data.shapes = [convert_shape(value) for value in shapes.values()] + serializer = serializers.LabeledShapeSerializerFromDB(list(shapes.values()), many=True) + self.ir_data.shapes = serializer.data def _init_tracks_from_db(self): db_tracks = self.db_job.labeledtrack_set.prefetch_related( @@ -576,29 +555,8 @@ def _init_tracks_from_db(self): for track_id, track_elements in elements.items(): tracks[track_id].elements = track_elements - def convert_track(track): - track['shapes'] = [OrderedDict(attr) for attr in track['trackedshape_set']] - del track['trackedshape_set'] - track['attributes'] = [OrderedDict(attr) for attr in track['labeledtrackattributeval_set']] - del track['labeledtrackattributeval_set'] - _transform_object(track) - for attr in track['attributes']: - del attr['id'] - - for shape in track['shapes']: - _transform_object(shape) - shape['attributes'] = [OrderedDict(attr) for attr in shape['trackedshapeattributeval_set']] - del shape['trackedshapeattributeval_set'] - for attr in shape['attributes']: - del attr['id'] - - if 'elements' in track: - for element in track['elements']: - convert_track(element) - - return OrderedDict(track) - - self.ir_data.tracks = [convert_track(value) for value in tracks.values()] + serializer = serializers.LabeledTrackSerializerFromDB(list(tracks.values()), many=True) + self.ir_data.tracks = serializer.data def _init_version_from_db(self): self.ir_data.version = 0 # FIXME: should be removed in the future diff --git a/cvat/apps/engine/serializers.py b/cvat/apps/engine/serializers.py index f4d44f58fc9d..8557d44467e6 100644 --- a/cvat/apps/engine/serializers.py +++ b/cvat/apps/engine/serializers.py @@ -1189,7 +1189,6 @@ def run_child_validation(self, data): raise exceptions.ValidationError(errors) - class ShapeSerializer(serializers.Serializer): type = serializers.ChoiceField(choices=models.ShapeType.choices()) occluded = serializers.BooleanField(default=False) @@ -1207,6 +1206,63 @@ class SubLabeledShapeSerializer(ShapeSerializer, AnnotationSerializer): class LabeledShapeSerializer(SubLabeledShapeSerializer): elements = SubLabeledShapeSerializer(many=True, required=False) +class LabeledImageSerializerFromDB(serializers.BaseSerializer): + def to_representation(self, instance): + def convert_tag(tag): + tag['attributes'] = [OrderedDict(attr) for attr in tag['labeledimageattributeval_set']] + del tag['labeledimageattributeval_set'] + if 'parent' in tag: + del tag['parent'] + for attr in tag['attributes']: + del attr['id'] + + return OrderedDict(tag) + + return convert_tag(instance) + +class LabeledShapeSerializerFromDB(serializers.BaseSerializer): + def to_representation(self, instance): + def convert_shape(shape): + shape['attributes'] = [OrderedDict(attr) for attr in shape['labeledshapeattributeval_set']] + del shape['labeledshapeattributeval_set'] + if 'parent' in shape: + del shape['parent'] + for attr in shape['attributes']: + del attr['id'] + if 'elements' in shape: + for element in shape['elements']: + convert_shape(element) + + return OrderedDict(shape) + + return convert_shape(instance) + +class LabeledTrackSerializerFromDB(serializers.BaseSerializer): + def to_representation(self, instance): + def convert_track(track): + track['shapes'] = [OrderedDict(attr) for attr in track['trackedshape_set']] + del track['trackedshape_set'] + track['attributes'] = [OrderedDict(attr) for attr in track['labeledtrackattributeval_set']] + del track['labeledtrackattributeval_set'] + if 'parent' in track: + del track['parent'] + for attr in track['attributes']: + del attr['id'] + + for shape in track['shapes']: + shape['attributes'] = [OrderedDict(attr) for attr in shape['trackedshapeattributeval_set']] + del shape['trackedshapeattributeval_set'] + for attr in shape['attributes']: + del attr['id'] + + if 'elements' in track: + for element in track['elements']: + convert_track(element) + + return OrderedDict(track) + + return convert_track(instance) + class TrackedShapeSerializer(ShapeSerializer): id = serializers.IntegerField(default=None, allow_null=True) frame = serializers.IntegerField(min_value=0) From aa7ad7bb32a1d41967f54d83b20fb0bfb8989389 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 5 Apr 2023 02:59:23 -0700 Subject: [PATCH 11/19] Removed extra code --- cvat/apps/dataset_manager/task.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/cvat/apps/dataset_manager/task.py b/cvat/apps/dataset_manager/task.py index bbfa07aeb3eb..21fe85b44ce7 100644 --- a/cvat/apps/dataset_manager/task.py +++ b/cvat/apps/dataset_manager/task.py @@ -42,12 +42,6 @@ def values(cls): def __str__(self): return self.value -def _transform_object(dictionary): - if 'parent' in dictionary: - del dictionary['parent'] - - return dictionary - def _merge_table_rows(rows, keys_for_merge, field_id): # It is necessary to keep a stable order of original rows # (e.g. for tracked boxes). Otherwise prev_box.frame can be bigger @@ -319,7 +313,6 @@ def _create(self, data): self._set_updated_date() self.db_job.save() - def create(self, data): self._create(data) From ec65017c13541c5b84a5aabaa5dfb5c8809fb432 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 5 Apr 2023 14:00:47 +0300 Subject: [PATCH 12/19] Update cvat/apps/dataset_manager/task.py --- cvat/apps/dataset_manager/task.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cvat/apps/dataset_manager/task.py b/cvat/apps/dataset_manager/task.py index 21fe85b44ce7..2b1d7858f4cf 100644 --- a/cvat/apps/dataset_manager/task.py +++ b/cvat/apps/dataset_manager/task.py @@ -513,7 +513,6 @@ def _init_tracks_from_db(self): tracks = {} elements = {} for db_track in db_tracks: - db_track['elements'] = [] db_track["trackedshape_set"] = _merge_table_rows(db_track["trackedshape_set"], { 'trackedshapeattributeval_set': [ 'trackedshapeattributeval__value', From 3db23931e64625d23424e4e9baf13de86dd19087 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 5 Apr 2023 05:49:53 -0700 Subject: [PATCH 13/19] Remove elements from child --- cvat/apps/engine/serializers.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cvat/apps/engine/serializers.py b/cvat/apps/engine/serializers.py index 8557d44467e6..e5b0b77e619b 100644 --- a/cvat/apps/engine/serializers.py +++ b/cvat/apps/engine/serializers.py @@ -1225,14 +1225,16 @@ def to_representation(self, instance): def convert_shape(shape): shape['attributes'] = [OrderedDict(attr) for attr in shape['labeledshapeattributeval_set']] del shape['labeledshapeattributeval_set'] + if 'parent' in shape: + if 'elements' in shape: + del shape['elements'] del shape['parent'] for attr in shape['attributes']: del attr['id'] if 'elements' in shape: for element in shape['elements']: convert_shape(element) - return OrderedDict(shape) return convert_shape(instance) From 6e9cd5bcb74e6ca6bb0d11c71b30cbe2c4d677d1 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 5 Apr 2023 06:06:18 -0700 Subject: [PATCH 14/19] Fixed condition --- cvat/apps/engine/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cvat/apps/engine/serializers.py b/cvat/apps/engine/serializers.py index e5b0b77e619b..466e7b31b733 100644 --- a/cvat/apps/engine/serializers.py +++ b/cvat/apps/engine/serializers.py @@ -1227,7 +1227,7 @@ def convert_shape(shape): del shape['labeledshapeattributeval_set'] if 'parent' in shape: - if 'elements' in shape: + if 'elements' in shape and shape['parent'] is not None: del shape['elements'] del shape['parent'] for attr in shape['attributes']: From a0a11c214f7e4af904b5dc68ea9caface52316cb Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 5 Apr 2023 07:43:54 -0700 Subject: [PATCH 15/19] Applied some comments --- cvat/apps/dataset_manager/task.py | 1 - cvat/apps/engine/backup.py | 3 +- cvat/apps/engine/serializers.py | 85 +++++++++++++++++-------------- 3 files changed, 49 insertions(+), 40 deletions(-) diff --git a/cvat/apps/dataset_manager/task.py b/cvat/apps/dataset_manager/task.py index 2b1d7858f4cf..08674082cf1b 100644 --- a/cvat/apps/dataset_manager/task.py +++ b/cvat/apps/dataset_manager/task.py @@ -366,7 +366,6 @@ def _extend_attributes(attributeval_set, default_attribute_values): attributeval_set.append(dotdict([ ('spec_id', db_attr.spec_id), ('value', db_attr.value), - ('id', None), ])) def _init_tags_from_db(self): diff --git a/cvat/apps/engine/backup.py b/cvat/apps/engine/backup.py index b08ba01903d5..98c6fcc5457b 100644 --- a/cvat/apps/engine/backup.py +++ b/cvat/apps/engine/backup.py @@ -386,7 +386,8 @@ def serialize_annotations(): for db_job_id in db_job_ids: annotations = dm.task.get_job_data(db_job_id) annotations_serializer = LabeledDataSerializer(data=annotations) - job_annotations.append(self._prepare_annotations(annotations_serializer.initial_data, self._label_mapping)) + annotations_serializer.is_valid(raise_exception=True) + job_annotations.append(self._prepare_annotations(annotations_serializer.data, self._label_mapping)) return job_annotations diff --git a/cvat/apps/engine/serializers.py b/cvat/apps/engine/serializers.py index 466e7b31b733..6202f881c948 100644 --- a/cvat/apps/engine/serializers.py +++ b/cvat/apps/engine/serializers.py @@ -1209,59 +1209,68 @@ class LabeledShapeSerializer(SubLabeledShapeSerializer): class LabeledImageSerializerFromDB(serializers.BaseSerializer): def to_representation(self, instance): def convert_tag(tag): - tag['attributes'] = [OrderedDict(attr) for attr in tag['labeledimageattributeval_set']] - del tag['labeledimageattributeval_set'] - if 'parent' in tag: - del tag['parent'] - for attr in tag['attributes']: - del attr['id'] + attr_keys = ['spec_id', 'value'] + keys = ['id', 'label_id', 'frame', 'group', 'source'] - return OrderedDict(tag) + result = OrderedDict([(key, tag.get(key, None)) for key in keys]) + result['attributes'] = [ + OrderedDict([(key, attr[key]) for key in attr_keys]) for attr in tag['labeledimageattributeval_set'] + ] + + return result return convert_tag(instance) class LabeledShapeSerializerFromDB(serializers.BaseSerializer): def to_representation(self, instance): def convert_shape(shape): - shape['attributes'] = [OrderedDict(attr) for attr in shape['labeledshapeattributeval_set']] - del shape['labeledshapeattributeval_set'] - - if 'parent' in shape: - if 'elements' in shape and shape['parent'] is not None: - del shape['elements'] - del shape['parent'] - for attr in shape['attributes']: - del attr['id'] - if 'elements' in shape: - for element in shape['elements']: - convert_shape(element) - return OrderedDict(shape) + attr_keys = ['spec_id', 'value'] + keys = [ + 'id', 'label_id', 'type', 'frame', 'group', + 'source', 'occluded', 'outside', 'z_order', + 'rotation', 'points', + ] + result = OrderedDict([(key, shape.get(key, None)) for key in keys]) + result['attributes'] = [ + OrderedDict([(key, attr[key]) for key in attr_keys]) for attr in shape['labeledshapeattributeval_set'] + ] + if shape.get('elements', None) is not None and shape['parent'] is None: + result['elements'] = [convert_shape(element) for element in shape['elements']] + return result return convert_shape(instance) class LabeledTrackSerializerFromDB(serializers.BaseSerializer): def to_representation(self, instance): def convert_track(track): - track['shapes'] = [OrderedDict(attr) for attr in track['trackedshape_set']] - del track['trackedshape_set'] - track['attributes'] = [OrderedDict(attr) for attr in track['labeledtrackattributeval_set']] - del track['labeledtrackattributeval_set'] - if 'parent' in track: - del track['parent'] - for attr in track['attributes']: - del attr['id'] - - for shape in track['shapes']: - shape['attributes'] = [OrderedDict(attr) for attr in shape['trackedshapeattributeval_set']] - del shape['trackedshapeattributeval_set'] - for attr in shape['attributes']: - del attr['id'] + attr_keys = ['spec_id', 'value'] + keys = [ + 'id', 'label_id', 'frame', 'group', 'source', + ] + shape_keys = [ + 'id', 'type', 'frame', 'occluded', 'outside', 'z_order', + 'rotation', 'points', 'trackedshapeattributeval_set', + ] + result = OrderedDict([(key, track.get(key, None)) for key in keys]) + result['shapes'] = [ + OrderedDict([(key, shape[key]) for key in shape_keys]) for shape in track['trackedshape_set'] + ] + result['attributes'] = [ + OrderedDict([(key, attr[key]) for key in attr_keys]) for attr in track['labeledtrackattributeval_set'] + ] + + if track.get('parent', None) is not None: + result.pop('elements', None) + for shape in result['shapes']: + shape['attributes'] = [ + OrderedDict([(key, attr[key]) for key in attr_keys]) for attr in shape['trackedshapeattributeval_set'] + ] + shape.pop('trackedshapeattributeval_set', None) - if 'elements' in track: - for element in track['elements']: - convert_track(element) + if track.get('elements', None) is not None and track['parent'] is None: + result['elements'] = [convert_track(element) for element in track['elements']] - return OrderedDict(track) + return result return convert_track(instance) From 0366cf6ec57a974fa099406e771ce93608e37a65 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 5 Apr 2023 07:49:07 -0700 Subject: [PATCH 16/19] Minor changes --- cvat/apps/engine/serializers.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cvat/apps/engine/serializers.py b/cvat/apps/engine/serializers.py index 6202f881c948..1e221ad0b25a 100644 --- a/cvat/apps/engine/serializers.py +++ b/cvat/apps/engine/serializers.py @@ -1212,7 +1212,7 @@ def convert_tag(tag): attr_keys = ['spec_id', 'value'] keys = ['id', 'label_id', 'frame', 'group', 'source'] - result = OrderedDict([(key, tag.get(key, None)) for key in keys]) + result = OrderedDict([(key, tag[key]) for key in keys]) result['attributes'] = [ OrderedDict([(key, attr[key]) for key in attr_keys]) for attr in tag['labeledimageattributeval_set'] ] @@ -1230,7 +1230,7 @@ def convert_shape(shape): 'source', 'occluded', 'outside', 'z_order', 'rotation', 'points', ] - result = OrderedDict([(key, shape.get(key, None)) for key in keys]) + result = OrderedDict([(key, shape[key]) for key in keys]) result['attributes'] = [ OrderedDict([(key, attr[key]) for key in attr_keys]) for attr in shape['labeledshapeattributeval_set'] ] @@ -1251,7 +1251,7 @@ def convert_track(track): 'id', 'type', 'frame', 'occluded', 'outside', 'z_order', 'rotation', 'points', 'trackedshapeattributeval_set', ] - result = OrderedDict([(key, track.get(key, None)) for key in keys]) + result = OrderedDict([(key, track[key]) for key in keys]) result['shapes'] = [ OrderedDict([(key, shape[key]) for key in shape_keys]) for shape in track['trackedshape_set'] ] @@ -1259,8 +1259,6 @@ def convert_track(track): OrderedDict([(key, attr[key]) for key in attr_keys]) for attr in track['labeledtrackattributeval_set'] ] - if track.get('parent', None) is not None: - result.pop('elements', None) for shape in result['shapes']: shape['attributes'] = [ OrderedDict([(key, attr[key]) for key in attr_keys]) for attr in shape['trackedshapeattributeval_set'] From cb142c2c95d7840a3140c73e7707af409ef4f5f6 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 5 Apr 2023 07:51:47 -0700 Subject: [PATCH 17/19] Avoid constructing serializer --- cvat/apps/engine/mixins.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cvat/apps/engine/mixins.py b/cvat/apps/engine/mixins.py index a36fb8b50bbb..17691f421342 100644 --- a/cvat/apps/engine/mixins.py +++ b/cvat/apps/engine/mixins.py @@ -278,8 +278,7 @@ def export_annotations(self, request, db_obj, export_func, callback, get_data=No return Response("Format is not specified",status=status.HTTP_400_BAD_REQUEST) data = get_data(self._object.pk) - serializer = LabeledDataSerializer(data=data) - return Response(serializer.initial_data) + return Response(data) def import_annotations(self, request, db_obj, import_func, rq_func, rq_id): is_tus_request = request.headers.get('Upload-Length', None) is not None or \ From 0892b42a04d44777cf7186afdef8746c6592d03f Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 5 Apr 2023 07:53:45 -0700 Subject: [PATCH 18/19] Fixed pylint --- cvat/apps/engine/mixins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cvat/apps/engine/mixins.py b/cvat/apps/engine/mixins.py index 17691f421342..4773c307122b 100644 --- a/cvat/apps/engine/mixins.py +++ b/cvat/apps/engine/mixins.py @@ -16,7 +16,7 @@ from cvat.apps.engine.models import Location from cvat.apps.engine.location import StorageType, get_location_configuration -from cvat.apps.engine.serializers import DataSerializer, LabeledDataSerializer +from cvat.apps.engine.serializers import DataSerializer from cvat.apps.webhooks.signals import signal_update, signal_create, signal_delete class TusFile: From ed77df254c420d6474975a73ee66f756bf6e2c9a Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 5 Apr 2023 08:51:06 -0700 Subject: [PATCH 19/19] Added common functions --- cvat/apps/engine/serializers.py | 61 ++++++++++++++------------------- 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/cvat/apps/engine/serializers.py b/cvat/apps/engine/serializers.py index 1e221ad0b25a..733595e0621d 100644 --- a/cvat/apps/engine/serializers.py +++ b/cvat/apps/engine/serializers.py @@ -1206,34 +1206,36 @@ class SubLabeledShapeSerializer(ShapeSerializer, AnnotationSerializer): class LabeledShapeSerializer(SubLabeledShapeSerializer): elements = SubLabeledShapeSerializer(many=True, required=False) +def _convert_annotation(obj, keys): + return OrderedDict([(key, obj[key]) for key in keys]) + +def _convert_attributes(attr_set): + attr_keys = ['spec_id', 'value'] + return [ + OrderedDict([(key, attr[key]) for key in attr_keys]) for attr in attr_set + ] + class LabeledImageSerializerFromDB(serializers.BaseSerializer): + # Use this serializer to export data from the database + # Because default DRF serializer is too slow on huge collections def to_representation(self, instance): def convert_tag(tag): - attr_keys = ['spec_id', 'value'] - keys = ['id', 'label_id', 'frame', 'group', 'source'] - - result = OrderedDict([(key, tag[key]) for key in keys]) - result['attributes'] = [ - OrderedDict([(key, attr[key]) for key in attr_keys]) for attr in tag['labeledimageattributeval_set'] - ] - + result = _convert_annotation(tag, ['id', 'label_id', 'frame', 'group', 'source']) + result['attributes'] = _convert_attributes(tag['labeledimageattributeval_set']) return result return convert_tag(instance) class LabeledShapeSerializerFromDB(serializers.BaseSerializer): + # Use this serializer to export data from the database + # Because default DRF serializer is too slow on huge collections def to_representation(self, instance): def convert_shape(shape): - attr_keys = ['spec_id', 'value'] - keys = [ - 'id', 'label_id', 'type', 'frame', 'group', - 'source', 'occluded', 'outside', 'z_order', - 'rotation', 'points', - ] - result = OrderedDict([(key, shape[key]) for key in keys]) - result['attributes'] = [ - OrderedDict([(key, attr[key]) for key in attr_keys]) for attr in shape['labeledshapeattributeval_set'] - ] + result = _convert_annotation(shape, [ + 'id', 'label_id', 'type', 'frame', 'group', 'source', + 'occluded', 'outside', 'z_order', 'rotation', 'points', + ]) + result['attributes'] = _convert_attributes(shape['labeledshapeattributeval_set']) if shape.get('elements', None) is not None and shape['parent'] is None: result['elements'] = [convert_shape(element) for element in shape['elements']] return result @@ -1241,33 +1243,22 @@ def convert_shape(shape): return convert_shape(instance) class LabeledTrackSerializerFromDB(serializers.BaseSerializer): + # Use this serializer to export data from the database + # Because default DRF serializer is too slow on huge collections def to_representation(self, instance): def convert_track(track): - attr_keys = ['spec_id', 'value'] - keys = [ - 'id', 'label_id', 'frame', 'group', 'source', - ] shape_keys = [ 'id', 'type', 'frame', 'occluded', 'outside', 'z_order', 'rotation', 'points', 'trackedshapeattributeval_set', ] - result = OrderedDict([(key, track[key]) for key in keys]) - result['shapes'] = [ - OrderedDict([(key, shape[key]) for key in shape_keys]) for shape in track['trackedshape_set'] - ] - result['attributes'] = [ - OrderedDict([(key, attr[key]) for key in attr_keys]) for attr in track['labeledtrackattributeval_set'] - ] - + result = _convert_annotation(track, ['id', 'label_id', 'frame', 'group', 'source']) + result['shapes'] = [_convert_annotation(shape, shape_keys) for shape in track['trackedshape_set']] + result['attributes'] = _convert_attributes(track['labeledtrackattributeval_set']) for shape in result['shapes']: - shape['attributes'] = [ - OrderedDict([(key, attr[key]) for key in attr_keys]) for attr in shape['trackedshapeattributeval_set'] - ] + shape['attributes'] = _convert_attributes(shape['trackedshapeattributeval_set']) shape.pop('trackedshapeattributeval_set', None) - if track.get('elements', None) is not None and track['parent'] is None: result['elements'] = [convert_track(element) for element in track['elements']] - return result return convert_track(instance)