Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Project export with 3d tasks #3502

Merged
merged 80 commits into from
Aug 17, 2021
Merged
Show file tree
Hide file tree
Changes from 72 commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
c53e062
init
ActiveChooN Jun 22, 2021
46e89b0
Fixed typos
ActiveChooN Jun 23, 2021
22fafe3
Merge branch 'develop' into dk/project-export
ActiveChooN Jun 24, 2021
7f4a349
Fixed cache time value
ActiveChooN Jun 24, 2021
4a5bc3a
Added MOTS PNG
ActiveChooN Jun 28, 2021
c57ab56
Merge branch 'develop' into dk/project-export
ActiveChooN Jul 1, 2021
04d14c5
temp
ActiveChooN Jul 2, 2021
f6b833f
Another temp
ActiveChooN Jul 7, 2021
d45bec8
WIP on UI
ActiveChooN Jul 7, 2021
b23a9ba
Added working modal
ActiveChooN Jul 8, 2021
4c22e11
Moved task export to modal
ActiveChooN Jul 9, 2021
a4d6094
Fixed vscode files
ActiveChooN Jul 9, 2021
aadc83c
Removed task actions
ActiveChooN Jul 9, 2021
bcd2034
Fixed image_maker calling
ActiveChooN Jul 12, 2021
3aa16a1
Separated DataExtractor class
ActiveChooN Jul 12, 2021
b59ab03
wip
ActiveChooN Jul 14, 2021
5d9b512
Added export with CVAT format
ActiveChooN Jul 15, 2021
a914d05
Fixed format resetting
ActiveChooN Jul 15, 2021
3b480fa
Fixed dir creating
ActiveChooN Jul 15, 2021
2647eb9
Fixed export in CVAT formats
ActiveChooN Jul 16, 2021
15d226a
Added simple server tests, fixed datumaro format export
ActiveChooN Jul 20, 2021
3da98c4
Reverted settings change
ActiveChooN Jul 20, 2021
d70ee77
Merge branch 'develop' into dk/project-export
ActiveChooN Jul 20, 2021
b8b3d9c
Fixed merge
ActiveChooN Jul 20, 2021
e1f8db6
Fixed some comments
ActiveChooN Jul 21, 2021
98c9083
Update cvat/apps/dataset_manager/bindings.py
ActiveChooN Jul 21, 2021
84f8b7a
Fixed comment
ActiveChooN Jul 21, 2021
4f8d28d
Merge branch 'dk/project-export' of https://github.com/openvinotoolki…
ActiveChooN Jul 21, 2021
29ffac1
Added restrictions for 3d tasks
ActiveChooN Jul 22, 2021
4e2aac1
Fixed validator
ActiveChooN Jul 22, 2021
5a48dfc
Merge branch 'develop' into dk/project-export
ActiveChooN Jul 23, 2021
ff19e71
Fixed linter and tests
ActiveChooN Jul 23, 2021
3e6d7df
Merge remote-tracking branch 'origin/develop' into dk/project-export
ActiveChooN Jul 26, 2021
dc45b9f
Fixed comments
ActiveChooN Jul 27, 2021
6c683ac
Added file extesion to the form
ActiveChooN Jul 27, 2021
abdb59f
Added notification
ActiveChooN Jul 27, 2021
f4c4e87
Fixed header
ActiveChooN Jul 27, 2021
76c073c
Added default format for tasks
ActiveChooN Jul 27, 2021
370f337
Fixed test
ActiveChooN Jul 28, 2021
141f6a6
Merge branch 'develop' into dk/project-export
ActiveChooN Jul 29, 2021
c076584
Fixed notification
ActiveChooN Jul 29, 2021
1f5f6f2
Added some classes
dvkruchinin Jul 29, 2021
7d1b9cf
Tests adaptation
dvkruchinin Jul 29, 2021
3be269c
Merge branch 'dk/project-export' of https://github.com/openvinotoolki…
dvkruchinin Jul 29, 2021
f129f76
Rename classes
dvkruchinin Jul 29, 2021
dc4b56a
The next tests adapdation
dvkruchinin Jul 29, 2021
cd67f5b
Additional tests adaptations.
dvkruchinin Jul 29, 2021
c955995
Some rework main.yaml
dvkruchinin Jul 29, 2021
7e9fcaa
Added debug
dvkruchinin Jul 29, 2021
4be0540
Debug removed
dvkruchinin Jul 29, 2021
415e275
Fixed late binding problem
ActiveChooN Jul 30, 2021
e6c95a2
Fixed 3d default format
ActiveChooN Jul 30, 2021
4a66693
Merge branch 'develop' into dk/project-export
ActiveChooN Jul 30, 2021
c7ed863
Merge pull request #3485 from dvkruchinin/dkru/project-export-adjast-…
ActiveChooN Jul 30, 2021
0687808
Added support for project with 3d tasks
ActiveChooN Aug 2, 2021
7e412f4
Fixed test
ActiveChooN Aug 2, 2021
4fe5598
Merge branch 'develop' into dk/project-export
ActiveChooN Aug 3, 2021
e635db3
Update cvat/apps/dataset_manager/views.py
ActiveChooN Aug 3, 2021
e9f1fc1
Fixed function name
ActiveChooN Aug 3, 2021
0a06baf
Revert "Added support for project with 3d tasks"
ActiveChooN Aug 3, 2021
1387b2f
Changed defaulted subset
ActiveChooN Aug 3, 2021
23d47dc
Added support for project with 3d tasks
ActiveChooN Aug 2, 2021
cbb7a1e
Update cvat/apps/dataset_manager/bindings.py
ActiveChooN Aug 3, 2021
566e490
Fixed project extractor
ActiveChooN Aug 3, 2021
42a974e
Merge branch 'develop' into dk/project-export
ActiveChooN Aug 4, 2021
4e4b37d
Merge branch 'dk/project-export' into dk/project-export-3d
ActiveChooN Aug 4, 2021
333bc88
Fixed project export cache invalidation
ActiveChooN Aug 5, 2021
9952d51
Merge branch 'develop' into dk/project-export
ActiveChooN Aug 5, 2021
f5c89ef
Added CHANGELOG, increased versions
ActiveChooN Aug 5, 2021
a73122f
Merge branch 'develop' into dk/project-export
Aug 6, 2021
5e4f248
Merge remote-tracking branch 'origin/dk/project-export' into dk/proje…
ActiveChooN Aug 6, 2021
bbaf8d6
Merge branch 'develop' into dk/project-export-3d
ActiveChooN Aug 6, 2021
ddf8c3b
separated user info function
ActiveChooN Aug 9, 2021
bed869c
Fixed subset missing
ActiveChooN Aug 10, 2021
d2c03c9
Added prefetch
ActiveChooN Aug 10, 2021
88a3ace
Added CHANGELOG
ActiveChooN Aug 10, 2021
f1cb156
Fixed CHANGELOG
ActiveChooN Aug 10, 2021
ac2790f
Added dimension to project
ActiveChooN Aug 12, 2021
84f328c
Fixed serilazer getting
ActiveChooN Aug 12, 2021
579d2b4
Fixed comments
ActiveChooN Aug 17, 2021
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
113 changes: 66 additions & 47 deletions cvat/apps/dataset_manager/bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ class ProjectData(InstanceLabelData):
Track = NamedTuple('Track', [('label', str), ('group', int), ('source', str), ('shapes', List[TrackedShape]), ('task_id', int)])
Tag = NamedTuple('Tag', [('frame', int), ('label', str), ('attributes', List[InstanceLabelData.Attribute]), ('source', str), ('group', int), ('task_id', int)])
Tag.__new__.__defaults__ = (0, )
Frame = NamedTuple('Frame', [('task_id', int), ('subset', str), ('idx', int), ('frame', int), ('name', str), ('width', int), ('height', int), ('labeled_shapes', List[Union[LabeledShape, TrackedShape]]), ('tags', List[Tag])])
Frame = NamedTuple('Frame', [('task_id', int), ('subset', str), ('idx', int), ('id', int), ('frame', int), ('name', str), ('width', int), ('height', int), ('labeled_shapes', List[Union[LabeledShape, TrackedShape]]), ('tags', List[Tag])])
ActiveChooN marked this conversation as resolved.
Show resolved Hide resolved

def __init__(self, annotation_irs: Mapping[str, AnnotationIR], db_project: Project, host: str, create_callback: Callable = None):
self._annotation_irs = annotation_irs
Expand Down Expand Up @@ -581,6 +581,7 @@ def _init_frame_info(self):
else:
self._frame_info.update({(task.id, self.rel_frame_id(task.id, db_image.frame)): {
"path": mangle_image_name(db_image.path, defaulted_subset, original_names),
"id": db_image.id,
"width": db_image.width,
"height": db_image.height,
"subset": defaulted_subset
Expand Down Expand Up @@ -683,6 +684,7 @@ def get_frame(task_id: int, idx: int) -> ProjectData.Frame:
task_id=task_id,
subset=frame_info["subset"],
idx=idx,
id=frame_info.get('id',0),
frame=abs_frame,
name=frame_info["path"],
height=frame_info["height"],
Expand Down Expand Up @@ -797,20 +799,25 @@ def categories(self) -> dict:
raise NotImplementedError()

@staticmethod
def _load_categories(labels: list):
def _load_categories(meta: dict, dimension):
categories: Dict[datumaro.AnnotationType, datumaro.Categories] = {}

label_categories = datumaro.LabelCategories(attributes=['occluded'])

for _, label in labels:
user_info = {}
if dimension == DimensionType.DIM_3D:
user_info = {"name": meta['owner']['username'],
ActiveChooN marked this conversation as resolved.
Show resolved Hide resolved
"createdAt": meta['created'],
"updatedAt": meta['updated']}
for _, label in meta['labels']:
label_categories.add(label['name'])
for _, attr in label['attributes']:
label_categories.attributes.add(attr['name'])

categories[datumaro.AnnotationType.label] = label_categories

return categories
categories[datumaro.AnnotationType.label] = label_categories

return categories, user_info

def _read_cvat_anno(self, cvat_frame_anno: Union[ProjectData.Frame, TaskData.Frame], labels: list):
categories = self.categories()
Expand All @@ -827,7 +834,7 @@ def map_label(name): return label_cat.find(name)[0]
class CvatTaskDataExtractor(datumaro.SourceExtractor, CVATDataExtractorMixin):
def __init__(self, task_data, include_images=False, format_type=None, dimension=DimensionType.DIM_2D):
super().__init__()
self._categories, self._user = self._load_categories(task_data, dimension=dimension)
self._categories, self._user = self._load_categories(task_data.meta['task'], dimension=dimension)
self._dimension = dimension
self._format_type = format_type
dm_items = []
Expand Down Expand Up @@ -893,11 +900,9 @@ def _make_image(i, **kwargs):
attributes["createdAt"] = self._user["createdAt"]
attributes["updatedAt"] = self._user["updatedAt"]
attributes["labels"] = []
index = 0
for _, label in task_data.meta['task']['labels']:
attributes["labels"].append({"label_id": index, "name": label["name"], "color": label["color"]})
for (idx, (_, label)) in enumerate(task_data.meta['task']['labels']):
attributes["labels"].append({"label_id": idx, "name": label["name"], "color": label["color"]})
attributes["track_id"] = -1
index += 1

dm_item = datumaro.DatasetItem(id=osp.split(frame_data.name)[-1].split('.')[0],
Copy link
Contributor

@zhiltsov-max zhiltsov-max Aug 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Fix to my reported problem 3: Use either os.path.splitext() or str.rsplit(s, maxsplit=1)

  2. Use of split() here does not conform to 2d formats and established CVAT behavior here, because 2d formats preserve the relative path information (to handle the problem with frames with same names in different dirs). Yes, it was discussed in the original 3d PR, but they refused to change it.

annotations=dm_anno, point_cloud=dm_image[0], related_images=dm_image[1],
Expand All @@ -907,27 +912,6 @@ def _make_image(i, **kwargs):

self._items = dm_items

@staticmethod
def _load_categories(cvat_anno, dimension): # pylint: disable=arguments-differ
categories = {}

label_categories = datumaro.LabelCategories(attributes=['occluded'])

user_info = {}
if dimension == DimensionType.DIM_3D:
user_info = {"name": cvat_anno.meta['task']['owner']['username'],
"createdAt": cvat_anno.meta['task']['created'],
"updatedAt": cvat_anno.meta['task']['updated']}
for _, label in cvat_anno.meta['task']['labels']:
label_categories.add(label['name'])
for _, attr in label['attributes']:
label_categories.attributes.add(attr['name'])


categories[datumaro.AnnotationType.label] = label_categories

return categories, user_info

def _read_cvat_anno(self, cvat_frame_anno: TaskData.Frame, labels: list):
categories = self.categories()
label_cat = categories[datumaro.AnnotationType.label]
Expand All @@ -940,9 +924,11 @@ def map_label(name): return label_cat.find(name)[0]
return convert_cvat_anno_to_dm(cvat_frame_anno, label_attrs, map_label, self._format_type, self._dimension)

class CVATProjectDataExtractor(datumaro.Extractor, CVATDataExtractorMixin):
Copy link
Contributor

@zhiltsov-max zhiltsov-max Aug 6, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think of implementing this class as (roughly):

task_datasets = { task_id: make_task_dataset(task) for task_id, task in tasks }
# append name mangling and other transforms like
for task_id, task_dataset in task_datasets.items():
  task_dataset.transform('rename', ...)

dataset = Dataset.from_extractors(*task_datasets)

def __init__(self, project_data: ProjectData, include_images: bool = False):
def __init__(self, project_data: ProjectData, include_images: bool = False, format_type: str = None, dimension: DimensionType = DimensionType.DIM_2D):
super().__init__()
self._categories = self._load_categories(project_data.meta['project']['labels'])
self._categories, self._user = self._load_categories(project_data.meta['project'], dimension)
self._dimension = dimension
self._format_type = format_type

dm_items: List[datumaro.DatasetItem] = []

Expand All @@ -952,12 +938,27 @@ def __init__(self, project_data: ProjectData, include_images: bool = False):
for task in project_data.tasks:
is_video = task.mode == 'interpolation'
ext_per_task[task.id] = FrameProvider.VIDEO_FRAME_EXT if is_video else ''
if include_images:
frame_provider = FrameProvider(task.data)
if self._dimension == DimensionType.DIM_3D:
def image_maker_factory(task):
def _make_image(i, **kwargs):
loader = osp.join(
task.data.get_upload_dirname(), kwargs['path'],
)
related_images = []
image = Img.objects.get(id=i)
ActiveChooN marked this conversation as resolved.
Show resolved Hide resolved
for i in image.related_files.all():
path = osp.realpath(str(i.path))
if osp.isfile(path):
related_images.append(path)
return loader, related_images
return _make_image
image_maker_per_task[task.id] = image_maker_factory(task)
elif include_images:
if is_video:
# optimization for videos: use numpy arrays instead of bytes
# some formats or transforms can require image data
def image_maker_factory(frame_provider):
def image_maker_factory(task):
frame_provider = FrameProvider(task.data)
def _make_image(i, **kwargs):
loader = lambda _: frame_provider.get_frame(i,
quality=frame_provider.Quality.ORIGINAL,
Expand All @@ -966,30 +967,48 @@ def _make_image(i, **kwargs):
return _make_image
else:
# for images use encoded data to avoid recoding
def image_maker_factory(frame_provider):
def image_maker_factory(task):
frame_provider = FrameProvider(task.data)
def _make_image(i, **kwargs):
loader = lambda _: frame_provider.get_frame(i,
quality=frame_provider.Quality.ORIGINAL,
out_type=frame_provider.Type.BUFFER)[0].getvalue()
return ByteImage(data=loader, **kwargs)
return _make_image
image_maker_per_task[task.id] = image_maker_factory(frame_provider)
image_maker_per_task[task.id] = image_maker_factory(task)

for frame_data in project_data.group_by_frame(include_empty=True):
image_args = {
'path': frame_data.name + ext_per_task[frame_data.task_id],
'size': (frame_data.height, frame_data.width),
}
if include_images:
if self._dimension == DimensionType.DIM_3D:
dm_image = image_maker_per_task[frame_data.task_id](frame_data.id, **image_args)
elif include_images:
dm_image = image_maker_per_task[frame_data.task_id](frame_data.idx, **image_args)
ActiveChooN marked this conversation as resolved.
Show resolved Hide resolved
else:
dm_image = Image(**image_args)
dm_anno = self._read_cvat_anno(frame_data, project_data.meta['project']['labels'])
dm_item = datumaro.DatasetItem(id=osp.splitext(frame_data.name)[0],
annotations=dm_anno, image=dm_image,
subset=frame_data.subset,
attributes={'frame': frame_data.frame}
)
if self._dimension == DimensionType.DIM_2D:
dm_item = datumaro.DatasetItem(id=osp.splitext(frame_data.name)[0],
annotations=dm_anno, image=dm_image,
subset=frame_data.subset,
attributes={'frame': frame_data.frame}
)
else:
attributes = {'frame': frame_data.frame}
if format_type == "sly_pointcloud":
attributes["name"] = self._user["name"]
attributes["createdAt"] = self._user["createdAt"]
attributes["updatedAt"] = self._user["updatedAt"]
attributes["labels"] = []
for (idx, (_, label)) in enumerate(project_data.meta['project']['labels']):
attributes["labels"].append({"label_id": idx, "name": label["name"], "color": label["color"]})
attributes["track_id"] = -1

dm_item = datumaro.DatasetItem(id=osp.split(frame_data.name)[-1].split('.')[0],
annotations=dm_anno, point_cloud=dm_image[0], related_images=dm_image[1],
attributes=attributes)
dm_items.append(dm_item)

self._items = dm_items
Expand All @@ -1004,11 +1023,11 @@ def __len__(self):
return len(self._items)


def GetCVATDataExtractor(instance_data: Union[ProjectData, TaskData], include_images: bool=False):
def GetCVATDataExtractor(instance_data: Union[ProjectData, TaskData], include_images: bool = False, format_type: str = None, dimension: DimensionType = DimensionType.DIM_2D):
if isinstance(instance_data, ProjectData):
return CVATProjectDataExtractor(instance_data, include_images)
return CVATProjectDataExtractor(instance_data, include_images, format_type, dimension)
else:
return CvatTaskDataExtractor(instance_data, include_images)
return CvatTaskDataExtractor(instance_data, include_images, format_type, dimension)

class CvatImportError(Exception):
pass
Expand Down
7 changes: 2 additions & 5 deletions cvat/apps/dataset_manager/formats/pointcloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from datumaro.components.dataset import Dataset

from cvat.apps.dataset_manager.bindings import (CvatTaskDataExtractor, TaskData,
from cvat.apps.dataset_manager.bindings import (GetCVATDataExtractor,
import_dm_annotations)
from cvat.apps.dataset_manager.util import make_zip_archive
from cvat.apps.engine.models import DimensionType
Expand All @@ -18,10 +18,7 @@
@exporter(name='Sly Point Cloud Format', ext='ZIP', version='1.0', dimension=DimensionType.DIM_3D)
def _export_images(dst_file, task_data, save_images=False):

if not isinstance(task_data, TaskData):
raise Exception("Export to \"Sly Point Cloud\" format is working only with tasks temporarily")

dataset = Dataset.from_extractors(CvatTaskDataExtractor(
dataset = Dataset.from_extractors(GetCVATDataExtractor(
task_data, include_images=save_images, format_type='sly_pointcloud', dimension=DimensionType.DIM_3D), env=dm_env)

with TemporaryDirectory() as temp_dir:
Expand Down
7 changes: 2 additions & 5 deletions cvat/apps/dataset_manager/formats/velodynepoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from datumaro.components.dataset import Dataset

from cvat.apps.dataset_manager.bindings import CvatTaskDataExtractor, TaskData, \
from cvat.apps.dataset_manager.bindings import GetCVATDataExtractor, \
import_dm_annotations
from .registry import dm_env

Expand All @@ -20,10 +20,7 @@
@exporter(name='Kitti Raw Format', ext='ZIP', version='1.0', dimension=DimensionType.DIM_3D)
def _export_images(dst_file, task_data, save_images=False):

if not isinstance(task_data, TaskData):
raise Exception("Export to \"Kitti raw\" format is working only with tasks temporarily")

dataset = Dataset.from_extractors(CvatTaskDataExtractor(
dataset = Dataset.from_extractors(GetCVATDataExtractor(
task_data, include_images=save_images, format_type="kitti_raw", dimension=DimensionType.DIM_3D), env=dm_env)

with TemporaryDirectory() as temp_dir:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import { taskName, labelName } from '../../support/const_canvas3d';

context('Canvas 3D functionality. Cancel drawing.', () => {
const caseId = '85';
const screenshotsPath = 'cypress/screenshots/canvas3d_functionality/case_85_canvas3d_functionality_cuboid_сancel_drawing.js';
const screenshotsPath =
'cypress/screenshots/canvas3d_functionality/case_85_canvas3d_functionality_cuboid_сancel_drawing.js';

before(() => {
cy.openTask(taskName)
cy.openTask(taskName);
cy.openJob();
cy.wait(1000); // Waiting for the point cloud to display
});
Expand Down