Fixed uploading track annotations for multi-segment tasks (#1396)

* fixed uploading annotation for overlapped segments

* fixed dump of tracks for multisegment task

* Update CHANGELOG.md

* fixed comments

* fixed comments

* Update cvat/apps/engine/data_manager.py

Co-Authored-By: Nikita Manovich <40690625+nmanovic@users.noreply.github.com>

* drop start shapes with outside==True for splitted track

* code cleanup

* fixed typo

* fix

Co-authored-by: Nikita Manovich <40690625+nmanovic@users.noreply.github.com>
main
Andrey Zhavoronkov 6 years ago committed by GitHub
parent ee6deba304
commit 227ab05e73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -80,6 +80,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- AttributeError: 'tuple' object has no attribute 'read' in ReID algorithm (https://github.com/opencv/cvat/issues/1403) - AttributeError: 'tuple' object has no attribute 'read' in ReID algorithm (https://github.com/opencv/cvat/issues/1403)
- Wrong semi-automatic segmentation near edges of an image (https://github.com/opencv/cvat/issues/1403) - Wrong semi-automatic segmentation near edges of an image (https://github.com/opencv/cvat/issues/1403)
- Git repos paths (https://github.com/opencv/cvat/pull/1400) - Git repos paths (https://github.com/opencv/cvat/pull/1400)
- Uploading annotations for tasks with multiple jobs (https://github.com/opencv/cvat/pull/1396)
## [1.0.0-alpha] - 2020-03-31 ## [1.0.0-alpha] - 2020-03-31
### Added ### Added

@ -77,13 +77,73 @@ class AnnotationIR:
if serializer.is_valid(raise_exception=True): if serializer.is_valid(raise_exception=True):
return serializer.data return serializer.data
@staticmethod
def _is_shape_inside(shape, start, stop):
return start <= int(shape['frame']) <= stop
@staticmethod
def _is_track_inside(track, start, stop):
# a <= b
def has_overlap(a, b):
return 0 <= min(b, stop) - max(a, start)
prev_shape = None
for shape in track['shapes']:
if prev_shape and not prev_shape['outside'] and \
has_overlap(prev_shape['frame'], shape['frame']):
return True
prev_shape = shape
if not prev_shape['outside'] and prev_shape['frame'] <= stop:
return True
return False
@staticmethod
def _slice_track(track_, start, stop):
def filter_track_shapes(shapes):
shapes = [s for s in shapes if AnnotationIR._is_shape_inside(s, start, stop)]
drop_count = 0
for s in shapes:
if s['outside']:
drop_count += 1
else:
break
# Need to leave the last shape if all shapes are outside
if drop_count == len(shapes):
drop_count -= 1
return shapes[drop_count:]
track = copy.deepcopy(track_)
segment_shapes = filter_track_shapes(track['shapes'])
if len(segment_shapes) < len(track['shapes']):
interpolated_shapes = TrackManager.get_interpolated_shapes(track, start, stop)
scoped_shapes = filter_track_shapes(interpolated_shapes)
if scoped_shapes:
if not scoped_shapes[0]['keyframe']:
segment_shapes.insert(0, scoped_shapes[0])
if not scoped_shapes[-1]['keyframe']:
segment_shapes.append(scoped_shapes[-1])
# Should delete 'interpolation_shapes' and 'keyframe' keys because
# Track and TrackedShape models don't expect these fields
del track['interpolated_shapes']
for shape in segment_shapes:
del shape['keyframe']
track['shapes'] = segment_shapes
track['frame'] = track['shapes'][0]['frame']
return track
#makes a data copy from specified frame interval #makes a data copy from specified frame interval
def slice(self, start, stop): def slice(self, start, stop):
is_frame_inside = lambda x: (start <= int(x['frame']) <= stop)
splitted_data = AnnotationIR() splitted_data = AnnotationIR()
splitted_data.tags = copy.deepcopy(list(filter(is_frame_inside, self.tags))) splitted_data.tags = [copy.deepcopy(t) for t in self.tags if self._is_shape_inside(t, start, stop)]
splitted_data.shapes = copy.deepcopy(list(filter(is_frame_inside, self.shapes))) splitted_data.shapes = [copy.deepcopy(s) for s in self.shapes if self._is_shape_inside(s, start, stop)]
splitted_data.tracks = copy.deepcopy(list(filter(lambda y: len(list(filter(is_frame_inside, y['shapes']))), self.tracks))) splitted_data.tracks = [self._slice_track(t, start, stop) for t in self.tracks if self._is_track_inside(t, start, stop)]
return splitted_data return splitted_data

@ -6,7 +6,6 @@ import os
from enum import Enum from enum import Enum
from collections import OrderedDict from collections import OrderedDict
from django.utils import timezone from django.utils import timezone
from PIL import Image
from django.conf import settings from django.conf import settings
from django.db import transaction from django.db import transaction

@ -290,6 +290,15 @@ class TrackManager(ObjectManager):
shape["frame"] = end_frame shape["frame"] = end_frame
shape["outside"] = True shape["outside"] = True
obj["shapes"].append(shape) obj["shapes"].append(shape)
# Need to update cached interpolated shapes
# because key shapes were changed
if obj.get("interpolated_shapes"):
last_interpolated_shape = obj["interpolated_shapes"][-1]
for frame in range(last_interpolated_shape["frame"] + 1, end_frame):
last_interpolated_shape = copy.deepcopy(last_interpolated_shape)
last_interpolated_shape["frame"] = frame
obj["interpolated_shapes"].append(last_interpolated_shape)
obj["interpolated_shapes"].append(shape)
@staticmethod @staticmethod
def normalize_shape(shape): def normalize_shape(shape):

Loading…
Cancel
Save