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

Fix export annotations to COCO keypoints #5794

Merged
merged 11 commits into from
Mar 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ Tracks can be exported/imported to/from Datumaro and Sly Pointcloud formats (<ht
- SiamMask and TransT serverless functions (<https://github.com/opencv/cvat/pull/5658>)
- Сreating a project or task with the same labels (<https://github.com/opencv/cvat/pull/5700>)
- \[Server API\] Ability to rename label to an existing name (<https://github.com/opencv/cvat/pull/5662>)
- Parsing skeleton sublabels containing spaces results in an error in dataset export (<https://github.com/opencv/cvat/pull/5794>)
- Missing CVAT_BASE_URL in docker-compose.yml (<https://github.com/opencv/cvat/pull/5792>)

### Security
Expand Down
19 changes: 11 additions & 8 deletions cvat/apps/dataset_manager/bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@
#
# SPDX-License-Identifier: MIT

from functools import reduce
import os.path as osp
import re
import sys
import numpy as np
from collections import namedtuple
from functools import reduce
from pathlib import Path
from types import SimpleNamespace
from typing import (Any, Callable, DefaultDict, Dict, List, Literal, Mapping,
NamedTuple, OrderedDict, Set, Tuple, Union)

import datumaro as dm
import defusedxml.ElementTree as ET
import numpy as np
import rq
from attr import attrib, attrs
from datumaro.components.media import PointCloud
Expand All @@ -28,7 +28,7 @@
from cvat.apps.engine.models import Label, LabelType, Project, ShapeType, Task

from .annotation import AnnotationIR, AnnotationManager, TrackManager
from .formats.transformations import EllipsesToMasks, CVATRleToCOCORle
from .formats.transformations import CVATRleToCOCORle, EllipsesToMasks

CVAT_INTERNAL_ATTRIBUTES = {'occluded', 'outside', 'keyframe', 'track_id', 'rotation'}

Expand Down Expand Up @@ -1221,10 +1221,13 @@ def _load_categories(labels: list):
label_categories.attributes.add(attr['name'])

if label['type'] == str(LabelType.SKELETON):
labels_from = list(map(int, re.findall(r'data-node-from="(\d+)"', label['svg'])))
labels_to = list(map(int, re.findall(r'data-node-to="(\d+)"', label['svg'])))
sublabels = re.findall(r'data-label-name="(\w+)"', label['svg'])
joints = zip(labels_from, labels_to)
joints = []
sublabels = []
for el in ET.fromstring('<root>' + label.get('svg', '') + '</root>'):
if el.tag == 'line':
joints.append([int(el.attrib['data-node-from']), int(el.attrib['data-node-to'])])
elif el.tag == 'circle':
sublabels.append(el.attrib['data-label-name'])

point_categories.add(label_id, sublabels, joints)

Expand Down
4 changes: 2 additions & 2 deletions tests/python/rest_api/test_jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ def test_member_update_job_annotations(
users = find_users(role=role, org=org)
jobs = jobs_by_org[org]
filtered_jobs = filter_jobs_with_shapes(jobs)
username, jid = find_job_staff_user(filtered_jobs, users, job_staff, [18])
username, jid = find_job_staff_user(filtered_jobs, users, job_staff, [18, 22])

data = request_data(jid)
self._check_respone(username, jid, expect_success, data, org=org)
Expand All @@ -393,7 +393,7 @@ def test_non_member_update_job_annotations(
users = find_users(privilege=privilege, exclude_org=org)
jobs = jobs_by_org[org]
filtered_jobs = filter_jobs_with_shapes(jobs)
username, jid = find_job_staff_user(filtered_jobs, users, False, [18])
username, jid = find_job_staff_user(filtered_jobs, users, False, [18, 22])

data = request_data(jid)
self._check_respone(username, jid, expect_success, data, org=org)
Expand Down
8 changes: 8 additions & 0 deletions tests/python/rest_api/test_projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,14 @@ def test_can_import_export_annotations_with_rotation(self):

assert task1_rotation == task2_rotation

def test_can_export_dataset_with_skeleton_labels_with_spaces(self):
# https://github.com/opencv/cvat/issues/5257
# https://github.com/opencv/cvat/issues/5600
username = "admin1"
project_id = 11

self._test_export_project(username, project_id, "COCO Keypoints 1.0")


@pytest.mark.usefixtures("restore_db_per_function")
class TestPatchProjectLabel:
Expand Down
12 changes: 6 additions & 6 deletions tests/python/rest_api/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ def test_member_update_task_annotation(
):
users = find_users(role=role, org=org)
tasks = tasks_by_org[org]
username, tid = find_task_staff_user(tasks, users, task_staff, [12, 14])
username, tid = find_task_staff_user(tasks, users, task_staff, [12, 14, 18])

data = request_data(tid)
with make_api_client(username) as api_client:
Expand Down Expand Up @@ -971,7 +971,7 @@ def _get_task_labels(self, pid, user, **kwargs) -> List[models.Label]:
)

def test_can_delete_label(self, tasks, labels, admin_user):
task = [t for t in tasks if t["labels"]["count"] > 0][0]
task = [t for t in tasks if t["project_id"] is None and t["labels"]["count"] > 0][0]
label = deepcopy([l for l in labels if l.get("task_id") == task["id"]][0])
label_payload = {"id": label["id"], "deleted": True}

Expand Down Expand Up @@ -1003,7 +1003,7 @@ def test_can_delete_skeleton_label(self, tasks, labels, admin_user):
assert DeepDiff(resulting_labels, task_labels, ignore_order=True) == {}

def test_can_rename_label(self, tasks, labels, admin_user):
task = [t for t in tasks if t["labels"]["count"] > 0][0]
task = [t for t in tasks if t["project_id"] is None and t["labels"]["count"] > 0][0]
task_labels = deepcopy([l for l in labels if l.get("task_id") == task["id"]])
task_labels[0].update({"name": "new name"})

Expand All @@ -1025,7 +1025,7 @@ def test_cannot_rename_label_to_duplicate_name(self, tasks, labels, admin_user):
assert "All label names must be unique" in response.text

def test_cannot_add_foreign_label(self, tasks, labels, admin_user):
task = list(tasks)[0]
task = [t for t in tasks if t["project_id"] is None][0]
new_label = deepcopy(
[
l
Expand All @@ -1040,7 +1040,7 @@ def test_cannot_add_foreign_label(self, tasks, labels, admin_user):
assert f"Not found label with id #{new_label['id']} to change" in response.text

def test_admin_can_add_label(self, tasks, admin_user):
task = list(tasks)[0]
task = [t for t in tasks if t["project_id"] is None][0]
new_label = {"name": "new name"}

response = patch_method(admin_user, f'/tasks/{task["id"]}', {"labels": [new_label]})
Expand All @@ -1063,7 +1063,7 @@ def test_non_task_staff_privileged_org_members_can_add_label(
for user, task in product(users, tasks)
if not is_task_staff(user["id"], task["id"])
and task["organization"]
and is_org_member(user["id"], task["organization"])
and is_org_member(user["id"], task["organization"] and task["project_id"] is None)
)

new_label = {"name": "new name"}
Expand Down
182 changes: 182 additions & 0 deletions tests/python/shared/assets/annotations.json
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,97 @@
],
"tracks": [],
"version": 0
},
"22": {
"shapes": [
{
"attributes": [],
"elements": [
{
"attributes": [],
"frame": 0,
"group": 0,
"id": 52,
"label_id": 49,
"occluded": false,
"outside": false,
"points": [
326.2062528608664,
107.42983682983868
],
"rotation": 0.0,
"source": "manual",
"type": "points",
"z_order": 0
},
{
"attributes": [],
"frame": 0,
"group": 0,
"id": 50,
"label_id": 47,
"occluded": false,
"outside": false,
"points": [
136.46993006993034,
138.72697241590762
],
"rotation": 0.0,
"source": "manual",
"type": "points",
"z_order": 0
},
{
"attributes": [],
"frame": 0,
"group": 0,
"id": 51,
"label_id": 48,
"occluded": false,
"outside": false,
"points": [
192.9001336620433,
421.9659673659692
],
"rotation": 0.0,
"source": "manual",
"type": "points",
"z_order": 0
},
{
"attributes": [],
"frame": 0,
"group": 0,
"id": 53,
"label_id": 50,
"occluded": false,
"outside": false,
"points": [
412.07832167832197,
337.46374412038085
],
"rotation": 0.0,
"source": "manual",
"type": "points",
"z_order": 0
}
],
"frame": 0,
"group": 0,
"id": 49,
"label_id": 46,
"occluded": false,
"outside": false,
"points": [],
"rotation": 0.0,
"source": "manual",
"type": "skeleton",
"z_order": 0
}
],
"tags": [],
"tracks": [],
"version": 0
}
},
"task": {
Expand Down Expand Up @@ -1634,6 +1725,97 @@
],
"tracks": [],
"version": 0
},
"18": {
"shapes": [
{
"attributes": [],
"elements": [
{
"attributes": [],
"frame": 0,
"group": 0,
"id": 52,
"label_id": 49,
"occluded": false,
"outside": false,
"points": [
326.2062528608664,
107.42983682983868
],
"rotation": 0.0,
"source": "manual",
"type": "points",
"z_order": 0
},
{
"attributes": [],
"frame": 0,
"group": 0,
"id": 50,
"label_id": 47,
"occluded": false,
"outside": false,
"points": [
136.46993006993034,
138.72697241590762
],
"rotation": 0.0,
"source": "manual",
"type": "points",
"z_order": 0
},
{
"attributes": [],
"frame": 0,
"group": 0,
"id": 51,
"label_id": 48,
"occluded": false,
"outside": false,
"points": [
192.9001336620433,
421.9659673659692
],
"rotation": 0.0,
"source": "manual",
"type": "points",
"z_order": 0
},
{
"attributes": [],
"frame": 0,
"group": 0,
"id": 53,
"label_id": 50,
"occluded": false,
"outside": false,
"points": [
412.07832167832197,
337.46374412038085
],
"rotation": 0.0,
"source": "manual",
"type": "points",
"z_order": 0
}
],
"frame": 0,
"group": 0,
"id": 49,
"label_id": 46,
"occluded": false,
"outside": false,
"points": [],
"rotation": 0.0,
"source": "manual",
"type": "skeleton",
"z_order": 0
}
],
"tags": [],
"tracks": [],
"version": 0
}
}
}
Binary file modified tests/python/shared/assets/cvat_db/cvat_data.tar.bz2
Binary file not shown.
Loading