Fix project import with skeletons (#4867)

main
Anastasia Yasakova 3 years ago committed by GitHub
parent 9e67bcb53c
commit fd666d00ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
non-ascii paths while adding files from "Connected file share" (issue #4428) non-ascii paths while adding files from "Connected file share" (issue #4428)
- Removed unnecessary volumes defined in docker-compose.serverless.yml - Removed unnecessary volumes defined in docker-compose.serverless.yml
(<https://github.com/openvinotoolkit/cvat/pull/4659>) (<https://github.com/openvinotoolkit/cvat/pull/4659>)
- Project import with skeletons (<https://github.com/opencv/cvat/pull/4867>)
### Security ### Security
- TDB - TDB

@ -524,9 +524,7 @@ class TaskData(InstanceLabelData):
] ]
shape['attributes'] = [self._import_attribute(label_id, attrib, mutable=True) shape['attributes'] = [self._import_attribute(label_id, attrib, mutable=True)
for attrib in shape['attributes'] for attrib in shape['attributes']
if self._get_mutable_attribute_id(label_id, attrib.name) or ( if self._get_mutable_attribute_id(label_id, attrib.name)
self.soft_attribute_import and attrib.name not in CVAT_INTERNAL_ATTRIBUTES
)
] ]
shape['points'] = list(map(float, shape['points'])) shape['points'] = list(map(float, shape['points']))
@ -1101,9 +1099,9 @@ class CVATDataExtractorMixin:
def _read_cvat_anno(self, cvat_frame_anno: Union[ProjectData.Frame, TaskData.Frame], labels: list): def _read_cvat_anno(self, cvat_frame_anno: Union[ProjectData.Frame, TaskData.Frame], labels: list):
categories = self.categories() categories = self.categories()
label_cat = categories[dm.AnnotationType.label] label_cat = categories[dm.AnnotationType.label]
def map_label(name): return label_cat.find(name)[0] def map_label(name, parent=''): return label_cat.find(name, parent)[0]
label_attrs = { label_attrs = {
label['name']: label['attributes'] label.get('parent', '') + label['name']: label['attributes']
for _, label in labels for _, label in labels
} }
@ -1198,9 +1196,9 @@ class CvatTaskDataExtractor(dm.SourceExtractor, CVATDataExtractorMixin):
def _read_cvat_anno(self, cvat_frame_anno: TaskData.Frame, labels: list): def _read_cvat_anno(self, cvat_frame_anno: TaskData.Frame, labels: list):
categories = self.categories() categories = self.categories()
label_cat = categories[dm.AnnotationType.label] label_cat = categories[dm.AnnotationType.label]
def map_label(name, parent=""): return label_cat.find(name, parent)[0] def map_label(name, parent=''): return label_cat.find(name, parent)[0]
label_attrs = { label_attrs = {
label.get("parent", "") + label['name']: label['attributes'] label.get('parent', '') + label['name']: label['attributes']
for _, label in labels for _, label in labels
} }
@ -1562,6 +1560,7 @@ def import_dm_annotations(dm_dataset: dm.Dataset, instance_data: Union[TaskData,
for n, v in ann.attributes.items() for n, v in ann.attributes.items()
] ]
points = []
if ann.type in shapes: if ann.type in shapes:
points = [] points = []
if ann.type == dm.AnnotationType.cuboid_3d: if ann.type == dm.AnnotationType.cuboid_3d:
@ -1647,6 +1646,11 @@ def import_dm_annotations(dm_dataset: dm.Dataset, instance_data: Union[TaskData,
if ann.type == dm.AnnotationType.skeleton: if ann.type == dm.AnnotationType.skeleton:
for element in ann.elements: for element in ann.elements:
element_keyframe = dm.util.cast(element.attributes.get('keyframe', None), bool) is True
element_outside = dm.util.cast(element.attributes.pop('outside', None), bool) is True
if not element_keyframe and not element_outside:
continue
if element.label not in tracks[track_id]['elements']: if element.label not in tracks[track_id]['elements']:
tracks[track_id]['elements'][element.label] = instance_data.Track( tracks[track_id]['elements'][element.label] = instance_data.Track(
label=label_cat.items[element.label].name, label=label_cat.items[element.label].name,
@ -1659,7 +1663,6 @@ def import_dm_annotations(dm_dataset: dm.Dataset, instance_data: Union[TaskData,
for n, v in element.attributes.items() for n, v in element.attributes.items()
] ]
element_occluded = dm.util.cast(element.attributes.pop('occluded', None), bool) is True element_occluded = dm.util.cast(element.attributes.pop('occluded', None), bool) is True
element_outside = dm.util.cast(element.attributes.pop('outside', None), bool) is True
element_source = element.attributes.pop('source').lower() \ element_source = element.attributes.pop('source').lower() \
if element.attributes.get('source', '').lower() in {'auto', 'manual'} else 'manual' if element.attributes.get('source', '').lower() in {'auto', 'manual'} else 'manual'
tracks[track_id]['elements'][element.label].shapes.append(instance_data.TrackedShape( tracks[track_id]['elements'][element.label].shapes.append(instance_data.TrackedShape(

@ -14,7 +14,7 @@ from typing import Callable
from datumaro.components.annotation import (AnnotationType, Bbox, Label, from datumaro.components.annotation import (AnnotationType, Bbox, Label,
LabelCategories, Points, Polygon, LabelCategories, Points, Polygon,
PolyLine) PolyLine, Skeleton)
from datumaro.components.dataset import Dataset, DatasetItem from datumaro.components.dataset import Dataset, DatasetItem
from datumaro.components.extractor import (DEFAULT_SUBSET_NAME, Extractor, from datumaro.components.extractor import (DEFAULT_SUBSET_NAME, Extractor,
Importer) Importer)
@ -119,23 +119,34 @@ class CvatExtractor(Extractor):
items = OrderedDict() items = OrderedDict()
track = None track = None
track_element = None
track_shapes = None
shape = None shape = None
shape_element = None
tag = None tag = None
attributes = None attributes = None
element_attributes = None
image = None image = None
subset = None subset = None
for ev, el in context: for ev, el in context:
if ev == 'start': if ev == 'start':
if el.tag == 'track': if el.tag == 'track':
frame_size = tasks_info[int(el.attrib.get('task_id'))]['frame_size'] if el.attrib.get('task_id') else tuple(tasks_info.values())[0]['frame_size'] if track:
track = { track_element = {
'id': el.attrib['id'], 'id': el.attrib['id'],
'label': el.attrib.get('label'), 'label': el.attrib.get('label'),
'group': int(el.attrib.get('group_id', 0)), }
'height': frame_size[0], else:
'width': frame_size[1], frame_size = tasks_info[int(el.attrib.get('task_id'))]['frame_size'] if el.attrib.get('task_id') else tuple(tasks_info.values())[0]['frame_size']
} track = {
subset = el.attrib.get('subset') 'id': el.attrib['id'],
'label': el.attrib.get('label'),
'group': int(el.attrib.get('group_id', 0)),
'height': frame_size[0],
'width': frame_size[1],
}
subset = el.attrib.get('subset')
track_shapes = {}
elif el.tag == 'image': elif el.tag == 'image':
image = { image = {
'name': el.attrib.get('name'), 'name': el.attrib.get('name'),
@ -145,16 +156,28 @@ class CvatExtractor(Extractor):
} }
subset = el.attrib.get('subset') subset = el.attrib.get('subset')
elif el.tag in cls._SUPPORTED_SHAPES and (track or image): elif el.tag in cls._SUPPORTED_SHAPES and (track or image):
attributes = {} if shape and shape['type'] == 'skeleton':
shape = { element_attributes = {}
'type': None, shape_element = {
'attributes': attributes, 'type': 'rectangle' if el.tag == 'box' else el.tag,
} 'attributes': element_attributes,
if track: }
shape.update(track) shape_element.update(image)
shape['track_id'] = int(track['id']) else:
if image: attributes = {}
shape.update(image) shape = {
'type': 'rectangle' if el.tag == 'box' else el.tag,
'attributes': attributes,
}
shape['elements'] = []
if track_element:
shape.update(track_element)
shape['track_id'] = int(track_element['id'])
elif track:
shape.update(track)
shape['track_id'] = int(track['id'])
if image:
shape.update(image)
elif el.tag == 'tag' and image: elif el.tag == 'tag' and image:
attributes = {} attributes = {}
tag = { tag = {
@ -165,7 +188,19 @@ class CvatExtractor(Extractor):
} }
subset = el.attrib.get('subset') subset = el.attrib.get('subset')
elif ev == 'end': elif ev == 'end':
if el.tag == 'attribute' and attributes is not None: if el.tag == 'attribute' and element_attributes is not None and shape_element is not None:
attr_value = el.text or ''
attr_type = attribute_types.get(el.attrib['name'])
if el.text in ['true', 'false']:
attr_value = attr_value == 'true'
elif attr_type is not None and attr_type != 'text':
try:
attr_value = float(attr_value)
except ValueError:
pass
element_attributes[el.attrib['name']] = attr_value
if el.tag == 'attribute' and attributes is not None and shape_element is None:
attr_value = el.text or '' attr_value = el.text or ''
attr_type = attribute_types.get(el.attrib['name']) attr_type = attribute_types.get(el.attrib['name'])
if el.text in ['true', 'false']: if el.text in ['true', 'false']:
@ -176,6 +211,37 @@ class CvatExtractor(Extractor):
except ValueError: except ValueError:
pass pass
attributes[el.attrib['name']] = attr_value attributes[el.attrib['name']] = attr_value
elif el.tag in cls._SUPPORTED_SHAPES and shape["type"] == "skeleton" and el.tag != "skeleton":
shape_element['label'] = el.attrib.get('label')
shape_element['group'] = int(el.attrib.get('group_id', 0))
shape_element['type'] = el.tag
shape_element['z_order'] = int(el.attrib.get('z_order', 0))
if el.tag == 'box':
shape_element['points'] = list(map(float, [
el.attrib['xtl'], el.attrib['ytl'],
el.attrib['xbr'], el.attrib['ybr'],
]))
else:
shape_element['points'] = []
for pair in el.attrib['points'].split(';'):
shape_element['points'].extend(map(float, pair.split(',')))
if el.tag == 'points' and el.attrib.get('occluded') == '1':
shape_element['visibility'] = [Points.Visibility.hidden] * (len(shape_element['points']) // 2)
else:
shape_element['occluded'] = (el.attrib.get('occluded') == '1')
if el.tag == 'points' and el.attrib.get('outside') == '1':
shape_element['visibility'] = [Points.Visibility.absent] * (len(shape_element['points']) // 2)
else:
shape_element['outside'] = (el.attrib.get('outside') == '1')
shape['elements'].append(shape_element)
shape_element = None
elif el.tag in cls._SUPPORTED_SHAPES: elif el.tag in cls._SUPPORTED_SHAPES:
if track is not None: if track is not None:
shape['frame'] = el.attrib['frame'] shape['frame'] = el.attrib['frame']
@ -194,15 +260,22 @@ class CvatExtractor(Extractor):
el.attrib['xtl'], el.attrib['ytl'], el.attrib['xtl'], el.attrib['ytl'],
el.attrib['xbr'], el.attrib['ybr'], el.attrib['xbr'], el.attrib['ybr'],
])) ]))
elif el.tag == 'skeleton':
shape['points'] = []
else: else:
shape['points'] = [] shape['points'] = []
for pair in el.attrib['points'].split(';'): for pair in el.attrib['points'].split(';'):
shape['points'].extend(map(float, pair.split(','))) shape['points'].extend(map(float, pair.split(',')))
if track_element:
track_shapes[shape['frame']]['elements'].append(shape)
elif track:
track_shapes[shape['frame']] = shape
else:
frame_desc = items.get((subset, shape['frame']), {'annotations': []})
frame_desc['annotations'].append(
cls._parse_shape_ann(shape, categories))
items[(subset, shape['frame'])] = frame_desc
frame_desc = items.get((subset, shape['frame']), {'annotations': []})
frame_desc['annotations'].append(
cls._parse_shape_ann(shape, categories))
items[(subset, shape['frame'])] = frame_desc
shape = None shape = None
elif el.tag == 'tag': elif el.tag == 'tag':
@ -212,7 +285,15 @@ class CvatExtractor(Extractor):
items[(subset, tag['frame'])] = frame_desc items[(subset, tag['frame'])] = frame_desc
tag = None tag = None
elif el.tag == 'track': elif el.tag == 'track':
track = None if track_element:
track_element = None
else:
for track_shape in track_shapes.values():
frame_desc = items.get((subset, track_shape['frame']), {'annotations': []})
frame_desc['annotations'].append(
cls._parse_shape_ann(track_shape, categories))
items[(subset, track_shape['frame'])] = frame_desc
track = None
elif el.tag == 'image': elif el.tag == 'image':
frame_desc = items.get((subset, image['frame']), {'annotations': []}) frame_desc = items.get((subset, image['frame']), {'annotations': []})
frame_desc.update({ frame_desc.update({
@ -377,7 +458,8 @@ class CvatExtractor(Extractor):
id=ann_id, attributes=attributes, group=group) id=ann_id, attributes=attributes, group=group)
elif ann_type == 'points': elif ann_type == 'points':
return Points(points, label=label_id, z_order=z_order, visibility = ann.get('visibility', None)
return Points(points, visibility, label=label_id, z_order=z_order,
id=ann_id, attributes=attributes, group=group) id=ann_id, attributes=attributes, group=group)
elif ann_type == 'box': elif ann_type == 'box':
@ -386,6 +468,14 @@ class CvatExtractor(Extractor):
return Bbox(x, y, w, h, label=label_id, z_order=z_order, return Bbox(x, y, w, h, label=label_id, z_order=z_order,
id=ann_id, attributes=attributes, group=group) id=ann_id, attributes=attributes, group=group)
elif ann_type == 'skeleton':
elements = []
for element in ann.get('elements', []):
elements.append(cls._parse_shape_ann(element, categories))
return Skeleton(elements, label=label_id, z_order=z_order,
id=ann_id, attributes=attributes, group=group)
else: else:
raise NotImplementedError("Unknown annotation type '%s'" % ann_type) raise NotImplementedError("Unknown annotation type '%s'" % ann_type)
@ -963,7 +1053,10 @@ def dump_as_cvat_interpolation(dumper, annotations):
elements=[], elements=[],
) for element in shape.elements] ) for element in shape.elements]
} }
if isinstance(annotations, ProjectData): track['task_id'] = shape.task_id if isinstance(annotations, ProjectData):
track['task_id'] = shape.task_id
for element in track['elements']:
element.task_id = shape.task_id
dump_track(counter, annotations.Track(**track)) dump_track(counter, annotations.Track(**track))
counter += 1 counter += 1

@ -708,43 +708,6 @@
], ],
"tracks": [] "tracks": []
}, },
"WiderFace 1.0": {
"version": 0,
"tags": [
{
"frame": 0,
"label_id": null,
"group": 0,
"source": "manual",
"attributes": []
}
],
"shapes": [
{
"type": "rectangle",
"occluded": false,
"z_order": 0,
"points": [7.55, 9.75, 16.44, 15.85],
"frame": 0,
"label_id": null,
"group": 0,
"source": "manual",
"attributes": []
},
{
"type": "rectangle",
"occluded": true,
"z_order": 0,
"points": [3.55, 27.75, 11.33, 33.71],
"frame": 0,
"label_id": null,
"group": 0,
"source": "manual",
"attributes": []
}
],
"tracks": []
},
"VGGFace2 1.0": { "VGGFace2 1.0": {
"version": 0, "version": 0,
"tags": [], "tags": [],
@ -1085,7 +1048,6 @@
"points": [66.45, 147.08, 182.16, 204.56], "points": [66.45, 147.08, 182.16, 204.56],
"frame": 0, "frame": 0,
"outside": false, "outside": false,
"outside": true,
"attributes": [] "attributes": []
}, },
{ {
@ -1215,50 +1177,6 @@
} }
] ]
}, },
"CVAT for video 1.1 polygon": {
"version": 0,
"tags": [],
"shapes": [],
"tracks": [
{
"frame": 0,
"label_id": null,
"group": 1,
"source": "manual",
"shapes": [
{
"type": "polygon",
"occluded": false,
"z_order": 0,
"points": [24.62, 13.01, 34.88, 20.03, 18.14, 18.08],
"frame": 0,
"outside": false,
"attributes": []
},
{
"type": "polygon",
"occluded": false,
"z_order": 0,
"points": [24.62, 13.01, 34.88, 20.03, 18.14, 18.08],
"frame": 1,
"outside": true,
"attributes": []
},
{
"type": "polygon",
"occluded": false,
"z_order": 0,
"points": [24.62, 13.01, 34.88, 20.03, 18.14, 18.08],
"frame": 2,
"outside": false,
"keyframe": true,
"attributes": []
}
],
"attributes": []
}
]
},
"CVAT for video 1.1 attributes in tracks": { "CVAT for video 1.1 attributes in tracks": {
"version": 0, "version": 0,
"tags": [], "tags": [],

@ -173,34 +173,35 @@ class _TaskBackupBase(_BackupBase):
source, dest = attribute.pop('spec_id'), 'name' source, dest = attribute.pop('spec_id'), 'name'
attribute[dest] = label_mapping[label]['attributes'][source] attribute[dest] = label_mapping[label]['attributes'][source]
def _update_label(shape): def _update_label(shape, parent_label=''):
if 'label_id' in shape: if 'label_id' in shape:
source, dest = shape.pop('label_id'), 'label' source = shape.pop('label_id')
shape['label'] = label_mapping[source]['value']
elif 'label' in shape: elif 'label' in shape:
source, dest = shape.pop('label'), 'label_id' source = parent_label + shape.pop('label')
shape[dest] = label_mapping[source]['value'] shape['label_id'] = label_mapping[source]['value']
return source return source
def _prepare_shapes(shapes): def _prepare_shapes(shapes, parent_label=''):
for shape in shapes: for shape in shapes:
label = _update_label(shape) label = _update_label(shape, parent_label)
for attr in shape['attributes']: for attr in shape['attributes']:
_update_attribute(attr, label) _update_attribute(attr, label)
_prepare_shapes(shape.get('elements', [])) _prepare_shapes(shape.get('elements', []), label)
self._prepare_meta(allowed_fields, shape) self._prepare_meta(allowed_fields, shape)
def _prepare_tracks(tracks): def _prepare_tracks(tracks, parent_label=''):
for track in tracks: for track in tracks:
label = _update_label(track) label = _update_label(track, parent_label)
for shape in track['shapes']: for shape in track['shapes']:
for attr in shape['attributes']: for attr in shape['attributes']:
_update_attribute(attr, label) _update_attribute(attr, label)
self._prepare_meta(allowed_fields, shape) self._prepare_meta(allowed_fields, shape)
_prepare_tracks(track.get('elements', [])) _prepare_tracks(track.get('elements', []), label)
for attr in track['attributes']: for attr in track['attributes']:
_update_attribute(attr, label) _update_attribute(attr, label)
@ -427,7 +428,7 @@ class _ImporterBase():
sublabels = label.pop('sublabels', []) sublabels = label.pop('sublabels', [])
db_label = models.Label.objects.create(**label_relation, parent=parent_label, **label) db_label = models.Label.objects.create(**label_relation, parent=parent_label, **label)
label_mapping[label_name] = { label_mapping[(parent_label.name if parent_label else '') + label_name] = {
'value': db_label.id, 'value': db_label.id,
'attributes': {}, 'attributes': {},
} }
@ -444,7 +445,7 @@ class _ImporterBase():
attribute_serializer = AttributeSerializer(data=attribute) attribute_serializer = AttributeSerializer(data=attribute)
attribute_serializer.is_valid(raise_exception=True) attribute_serializer.is_valid(raise_exception=True)
db_attribute = attribute_serializer.save(label=db_label) db_attribute = attribute_serializer.save(label=db_label)
label_mapping[label_name]['attributes'][attribute_name] = db_attribute.id label_mapping[(parent_label.name if parent_label else '') + label_name]['attributes'][attribute_name] = db_attribute.id
return label_mapping return label_mapping

@ -816,12 +816,13 @@ class ProjectWriteSerializer(serializers.ModelSerializer):
sublabels = label.pop('sublabels', []) sublabels = label.pop('sublabels', [])
svg = label.pop('svg', '') svg = label.pop('svg', '')
db_label = LabelSerializer.update_instance(label, instance, parent_label) db_label = LabelSerializer.update_instance(label, instance, parent_label)
update_labels(sublabels, parent_label=db_label) if not label.get('deleted'):
update_labels(sublabels, parent_label=db_label)
if label.get('id') is None and db_label.type == str(models.LabelType.SKELETON): if label.get('id') is None and db_label.type == str(models.LabelType.SKELETON):
for db_sublabel in list(db_label.sublabels.all()): for db_sublabel in list(db_label.sublabels.all()):
svg = svg.replace(f'data-label-name="{db_sublabel.name}"', f'data-label-id="{db_sublabel.id}"') svg = svg.replace(f'data-label-name="{db_sublabel.name}"', f'data-label-id="{db_sublabel.id}"')
models.Skeleton.objects.create(root=db_label, svg=svg) models.Skeleton.objects.create(root=db_label, svg=svg)
update_labels(labels) update_labels(labels)

@ -9,10 +9,10 @@ This is the native CVAT annotation format. It supports all CVAT annotations
features, so it can be used to make data backups. features, so it can be used to make data backups.
- supported annotations CVAT for Images: Rectangles, Polygons, Polylines, - supported annotations CVAT for Images: Rectangles, Polygons, Polylines,
Points, Cuboids, Tags, Tracks Points, Cuboids, Skeletons, Tags, Tracks
- supported annotations CVAT for Videos: Rectangles, Polygons, Polylines, - supported annotations CVAT for Videos: Rectangles, Polygons, Polylines,
Points, Cuboids, Tracks Points, Cuboids, Skeletons, Tracks
- attributes are supported - attributes are supported

@ -38,6 +38,7 @@ In annotation mode each image tag has `width` and `height` attributes for the sa
<labels> <labels>
<label> <label>
<name>String: name of the label (e.g. car, person)</name> <name>String: name of the label (e.g. car, person)</name>
<type>String: any, bbox, cuboid, cuboid_3d, ellipse, polygon, polyline, points, skeleton, tag</type>
<attributes> <attributes>
<attribute> <attribute>
<name>String: attribute name</name> <name>String: attribute name</name>
@ -49,6 +50,8 @@ ex. value 2
ex. value 3</values> ex. value 3</values>
</attribute> </attribute>
</attributes> </attributes>
<svg>String: label representation in svg, only for skeletons</svg>
<parent>String: label parent name, only for skeletons</parent>
</label> </label>
</labels> </labels>
<segments> <segments>
@ -81,7 +84,7 @@ On each image it is possible to have many different objects. Each object can hav
If an annotation task is created with `z_order` flag then each object will have `z_order` attribute which is used If an annotation task is created with `z_order` flag then each object will have `z_order` attribute which is used
to draw objects properly when they are intersected (if `z_order` is bigger the object is closer to camera). to draw objects properly when they are intersected (if `z_order` is bigger the object is closer to camera).
In previous versions of the format only `box` shape was available. In previous versions of the format only `box` shape was available.
In later releases `polygon`, `polyline`, `points` and `tags` were added. Please see below for more details: In later releases `polygon`, `polyline`, `points`, `skeletons` and `tags` were added. Please see below for more details:
```xml ```xml
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
@ -117,6 +120,14 @@ In later releases `polygon`, `polyline`, `points` and `tags` were added. Please
<attribute name="String: an attribute name">String: the attribute value</attribute> <attribute name="String: an attribute name">String: the attribute value</attribute>
... ...
</tag> </tag>
<skeleton label="String: the associated label" occluded="Number: 0 - False, 1 - True" z_order="Number: z-order of the object">
<points label="String: the associated label" occluded="Number: 0 - False, 1 - True" outside="Number: 0 - False, 1 - True" points="x0,y0;x1,y1" z_order="0">
<attribute name="String: an attribute name">String: the attribute value</attribute>
</points>
...
<attribute name="String: an attribute name">String: the attribute value</attribute>
...
</skeleton>
... ...
</image> </image>
... ...
@ -161,6 +172,34 @@ Example:
<attributes> <attributes>
</attributes> </attributes>
</label> </label>
<label>
<name>s1</name>
<type>skeleton</type>
<attributes>
</attributes>
<svg>&lt;line x1="36.87290954589844" y1="47.732025146484375" x2="86.87290954589844" y2="10.775501251220703" stroke="black" data-type="edge" data-node-from="2" stroke-width="0.5" data-node-to="3"&gt;&lt;/line&gt;&lt;line x1="25.167224884033203" y1="22.64841079711914" x2="36.87290954589844" y2="47.732025146484375" stroke="black" data-type="edge" data-node-from="1" stroke-width="0.5" data-node-to="2"&gt;&lt;/line&gt;&lt;circle r="1.5" stroke="black" fill="#b3b3b3" cx="25.167224884033203" cy="22.64841079711914" stroke-width="0.1" data-type="element node" data-element-id="1" data-node-id="1" data-label-name="1"&gt;&lt;/circle&gt;&lt;circle r="1.5" stroke="black" fill="#b3b3b3" cx="36.87290954589844" cy="47.732025146484375" stroke-width="0.1" data-type="element node" data-element-id="2" data-node-id="2" data-label-name="2"&gt;&lt;/circle&gt;&lt;circle r="1.5" stroke="black" fill="#b3b3b3" cx="86.87290954589844" cy="10.775501251220703" stroke-width="0.1" data-type="element node" data-element-id="3" data-node-id="3" data-label-name="3"&gt;&lt;/circle&gt;</svg>
</label>
<label>
<name>1</name>
<type>points</type>
<attributes>
</attributes>
<parent>s1</parent>
</label>
<label>
<name>2</name>
<type>points</type>
<attributes>
</attributes>
<parent>s1</parent>
</label>
<label>
<name>3</name>
<type>points</type>
<attributes>
</attributes>
<parent>s1</parent>
</label>
</labels> </labels>
<segments> <segments>
<segment> <segment>
@ -190,6 +229,14 @@ Example:
</points> </points>
<tag label="good_frame" source="manual"> <tag label="good_frame" source="manual">
</tag> </tag>
<skeleton label="s1" occluded="0" source="manual" points="" rotation="0.00" z_order="0">
<points label="1" occluded="0" source="manual" outside="0" points="54.47,94.81" z_order="0">
</points>
<points label="2" occluded="0" source="manual" outside="0" points="68.02,162.34" z_order="0">
</points>
<points label="3" occluded="0" source="manual" outside="0" points="125.87,62.85" z_order="0">
</points>
</skeleton>
</image> </image>
</annotations> </annotations>
``` ```
@ -206,7 +253,7 @@ cloned for each location (a known redundancy).
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<annotations> <annotations>
... ...
<track id="Number: id of the track (doesn't have any special meeting" label="String: the associated label" source="manual or auto"> <track id="Number: id of the track (doesn't have any special meeting)" label="String: the associated label" source="manual or auto">
<box frame="Number: frame" xtl="Number: float" ytl="Number: float" xbr="Number: float" ybr="Number: float" outside="Number: 0 - False, 1 - True" occluded="Number: 0 - False, 1 - True" keyframe="Number: 0 - False, 1 - True"> <box frame="Number: frame" xtl="Number: float" ytl="Number: float" xbr="Number: float" ybr="Number: float" outside="Number: 0 - False, 1 - True" occluded="Number: 0 - False, 1 - True" keyframe="Number: 0 - False, 1 - True">
<attribute name="String: an attribute name">String: the attribute value</attribute> <attribute name="String: an attribute name">String: the attribute value</attribute>
... ...
@ -222,6 +269,17 @@ cloned for each location (a known redundancy).
</points> </points>
... ...
</track> </track>
<track id="Number: id of the track (doesn't have any special meeting)" label="String: the associated label" source="manual or auto">
<skeleton frame="Number: frame" outside="Number: 0 - False, 1 - True" occluded="Number: 0 - False, 1 - True" keyframe="Number: 0 - False, 1 - True">
</skeleton>
...
<track id="Number: id of the track (doesn't have any special meeting)" label="String: the associated label" source="manual or auto">
<points frame="Number: frame" outside="Number: 0 - False, 1 - True" occluded="Number: 0 - False, 1 - True" keyframe="Number: 0 - False, 1 - True" points="x0,y0;x1,y1">
</points>
...
</track>
...
</track>
... ...
</annotations> </annotations>
``` ```
@ -254,6 +312,34 @@ Example:
<attributes> <attributes>
</attributes> </attributes>
</label> </label>
<label>
<name>s1</name>
<type>skeleton</type>
<attributes>
</attributes>
<svg>&lt;line x1="36.87290954589844" y1="47.732025146484375" x2="86.87290954589844" y2="10.775501251220703" stroke="black" data-type="edge" data-node-from="2" stroke-width="0.5" data-node-to="3"&gt;&lt;/line&gt;&lt;line x1="25.167224884033203" y1="22.64841079711914" x2="36.87290954589844" y2="47.732025146484375" stroke="black" data-type="edge" data-node-from="1" stroke-width="0.5" data-node-to="2"&gt;&lt;/line&gt;&lt;circle r="1.5" stroke="black" fill="#b3b3b3" cx="25.167224884033203" cy="22.64841079711914" stroke-width="0.1" data-type="element node" data-element-id="1" data-node-id="1" data-label-name="1"&gt;&lt;/circle&gt;&lt;circle r="1.5" stroke="black" fill="#b3b3b3" cx="36.87290954589844" cy="47.732025146484375" stroke-width="0.1" data-type="element node" data-element-id="2" data-node-id="2" data-label-name="2"&gt;&lt;/circle&gt;&lt;circle r="1.5" stroke="black" fill="#b3b3b3" cx="86.87290954589844" cy="10.775501251220703" stroke-width="0.1" data-type="element node" data-element-id="3" data-node-id="3" data-label-name="3"&gt;&lt;/circle&gt;</svg>
</label>
<label>
<name>1</name>
<type>points</type>
<attributes>
</attributes>
<parent>s1</parent>
</label>
<label>
<name>2</name>
<type>points</type>
<attributes>
</attributes>
<parent>s1</parent>
</label>
<label>
<name>3</name>
<type>points</type>
<attributes>
</attributes>
<parent>s1</parent>
</label>
</labels> </labels>
<segments> <segments>
<segment> <segment>
@ -288,5 +374,61 @@ Example:
<polygon frame="14" points="313.74,233.16;331.11,220.00;359.53,243.16;333.21,283.16;287.95,274.74" outside="1" occluded="0" keyframe="1"> <polygon frame="14" points="313.74,233.16;331.11,220.00;359.53,243.16;333.21,283.16;287.95,274.74" outside="1" occluded="0" keyframe="1">
</polygon> </polygon>
</track> </track>
<track id="1" label="s1" source="manual">
<skeleton frame="0" outside="0" occluded="0" keyframe="1" points="" z_order="0">
</skeleton>
<skeleton frame="1" outside="1" occluded="0" keyframe="1" points="" z_order="0">
</skeleton>
<skeleton frame="6" outside="0" occluded="0" keyframe="1" points="" z_order="0">
</skeleton>
<skeleton frame="7" outside="1" occluded="0" keyframe="1" points="" z_order="0">
</skeleton>
<skeleton frame="13" outside="0" occluded="0" keyframe="0" points="" z_order="0">
</skeleton>
<skeleton frame="14" outside="1" occluded="0" keyframe="1" points="" z_order="0">
</skeleton>
<track id="0" label="1" source="manual" task_id="20" subset="default_1">
<points frame="0" outside="0" occluded="0" keyframe="1" points="112.07,258.59" z_order="0">
</points>
<points frame="1" outside="1" occluded="0" keyframe="1" points="112.07,258.59" z_order="0">
</points>
<points frame="6" outside="0" occluded="0" keyframe="0" points="120.07,270.59" z_order="0">
</points>
<points frame="7" outside="1" occluded="0" keyframe="1" points="120.07,270.59" z_order="0">
</points>
<points frame="13" outside="0" occluded="0" keyframe="0" points="112.07,258.59" z_order="0">
</points>
<points frame="14" outside="1" occluded="0" keyframe="1" points="112.07,258.59" z_order="0">
</points>
</track>
<track id="1" label="2" source="manual" task_id="20" subset="default_1">
<points frame="0" outside="0" occluded="0" keyframe="1" points="127.87,333.23" z_order="0">
</points>
<points frame="1" outside="1" occluded="0" keyframe="1" points="127.87,333.23" z_order="0">
</points>
<points frame="6" outside="0" occluded="0" keyframe="0" points="140.87,350.23" z_order="0">
</points>
<points frame="7" outside="1" occluded="0" keyframe="1" points="140.87,350.23" z_order="0">
</points>
<points frame="13" outside="0" occluded="0" keyframe="0" points="127.87,333.23" z_order="0">
</points>
<points frame="14" outside="1" occluded="0" keyframe="1" points="127.87,333.23" z_order="0">
</points>
</track>
<track id="2" label="3" source="manual" task_id="20" subset="default_1">
<points frame="0" outside="0" occluded="0" keyframe="1" points="195.37,223.27" z_order="0">
</points>
<points frame="1" outside="1" occluded="0" keyframe="1" points="195.37,223.27" z_order="0">
</points>
<points frame="6" outside="0" occluded="0" keyframe="0" points="210.37,260.27" z_order="0">
</points>
<points frame="7" outside="1" occluded="0" keyframe="1" points="210.37,260.27" z_order="0">
</points>
<points frame="13" outside="0" occluded="0" keyframe="0" points="195.37,223.27" z_order="0">
</points>
<points frame="14" outside="1" occluded="0" keyframe="1" points="195.37,223.27" z_order="0">
</points>
</track>
</track>
</annotations> </annotations>
``` ```

Loading…
Cancel
Save