Skip to content

Commit

Permalink
Move annotation formats to dataset manager (#1256)
Browse files Browse the repository at this point in the history
* Move formats to dataset manager

* Unify datataset export and anno export implementations

* Add track_id to TrackedShape, export tracked shapes

* Replace MOT format

* Replace LabelMe format

* Add new formats to dm

* Add dm tests

* Extend TrackedShape

* Enable dm test in CI

* Fix tests

* Add import

* Fix tests

* Fix mot track ids

* Fix mot format

* Update attribute logic in labelme tests

* Use common code in yolo

* Put datumaro in path in settings

* Expect labels file in MOT next to annotations file

* Add MOT format description

* Add import

* Add labelme format description

* Linter fix

* Linter fix2

* Compare attributes ordered

* Update docs

* Update tests
  • Loading branch information
zhiltsov-max committed Mar 30, 2020
1 parent e87ec38 commit 887c6f0
Show file tree
Hide file tree
Showing 25 changed files with 705 additions and 511 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ before_script:

script:
- docker-compose -f docker-compose.yml -f docker-compose.ci.yml run cvat_ci /bin/bash -c 'python3 manage.py test cvat/apps utils/cli'
- docker-compose -f docker-compose.yml -f docker-compose.ci.yml run cvat_ci /bin/bash -c 'python3 manage.py test --pattern="_tests.py" cvat/apps/dataset_manager'
- docker-compose -f docker-compose.yml -f docker-compose.ci.yml run cvat_ci /bin/bash -c 'python3 manage.py test datumaro/'
- docker-compose -f docker-compose.yml -f docker-compose.ci.yml run cvat_ci /bin/bash -c 'cd cvat-data && npm install && cd ../cvat-core && npm install && npm run test && npm run coveralls'
71 changes: 66 additions & 5 deletions cvat/apps/annotation/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<!--lint disable list-item-indent-->
<!--lint disable no-duplicate-headings-->
## Description

The purpose of this application is to add support for multiple annotation formats for CVAT.
Expand Down Expand Up @@ -525,10 +527,10 @@ python create_pascal_tf_record.py --data_dir <path to VOCdevkit> --set train --y
│   └── Segmentation/
│   └── default.txt # list of image names without extension
├── SegmentationClass/ # merged class masks
│   ── image1.png
│   ── image1.png
│   └── image2.png
└── SegmentationObject/ # merged instance masks
── image1.png
── image1.png
└── image2.png
```
Mask is a png image with several (RGB) channels where each pixel has own color which corresponds to a label.
Expand Down Expand Up @@ -557,11 +559,70 @@ python create_pascal_tf_record.py --data_dir <path to VOCdevkit> --set train --y
│   └── Segmentation/
│   └── <any_subset_name>.txt
├── SegmentationClass/
│   ── image1.png
│   ── image1.png
│   └── image2.png
└── SegmentationObject/
── image.png
── image1.png
└── image2.png
```
- supported shapes: Polygons
- additional comments: the CVAT task should be created with the full label set that may be in the annotation files
- additional comments: the CVAT task should be created with the full label set that may be in the annotation files

### [MOT sequence](https://arxiv.org/pdf/1906.04567.pdf)
#### Dumper
- downloaded file: a zip archive of the following structure:
```bash
taskname.zip/
├── img1/
| ├── imgage1.jpg
| └── imgage2.jpg
└── gt/
├── labels.txt
└── gt.txt

# labels.txt
cat
dog
person
...

# gt.txt
# frame_id, track_id, x, y, w, h, "not ignored", class_id, visibility, <skipped>
1,1,1363,569,103,241,1,1,0.86014
...

```
- supported annotations: Rectangle shapes and tracks
- supported attributes: `visibility` (number), `ignored` (checkbox)

#### Loader
- uploaded file: a zip archive of the structure above or:
```bash
taskname.zip/
├── labels.txt # optional, mandatory for non-official labels
└── gt.txt
```
- supported annotations: Rectangle tracks

### [LabelMe](http://labelme.csail.mit.edu/Release3.0)
#### Dumper
- downloaded file: a zip archive of the following structure:
```bash
taskname.zip/
├── img1.jpg
└── img1.xml
```
- supported annotations: Rectangles, Polygons (with attributes)

#### Loader
- uploaded file: a zip archive of the following structure:
```bash
taskname.zip/
├── Masks/
| ├── img1_mask1.png
| └── img1_mask2.png
├── img1.xml
├── img2.xml
└── img3.xml
```
- supported annotations: Rectangles, Polygons, Masks (as polygons)
22 changes: 16 additions & 6 deletions cvat/apps/annotation/annotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ class Annotation:
Attribute = namedtuple('Attribute', 'name, value')
LabeledShape = namedtuple('LabeledShape', 'type, frame, label, points, occluded, attributes, group, z_order')
LabeledShape.__new__.__defaults__ = (0, 0)
TrackedShape = namedtuple('TrackedShape', 'type, points, occluded, frame, attributes, outside, keyframe, z_order')
TrackedShape.__new__.__defaults__ = (0, )
TrackedShape = namedtuple('TrackedShape', 'type, frame, points, occluded, outside, keyframe, attributes, group, z_order, label, track_id')
TrackedShape.__new__.__defaults__ = (0, 0, None, 0)
Track = namedtuple('Track', 'label, group, shapes')
Tag = namedtuple('Tag', 'frame, label, attributes, group')
Tag.__new__.__defaults__ = (0, )
Expand Down Expand Up @@ -272,11 +272,14 @@ def _export_tracked_shape(self, shape):
return Annotation.TrackedShape(
type=shape["type"],
frame=self._db_task.data.start_frame + shape["frame"] * self._frame_step,
label=self._get_label_name(shape["label_id"]),
points=shape["points"],
occluded=shape["occluded"],
z_order=shape.get("z_order", 0),
group=shape.get("group", 0),
outside=shape.get("outside", False),
keyframe=shape.get("keyframe", True),
z_order=shape["z_order"],
track_id=shape["track_id"],
attributes=self._export_attributes(shape["attributes"]),
)

Expand Down Expand Up @@ -318,7 +321,11 @@ def _get_frame(annotations, shape):
annotations = {}
data_manager = DataManager(self._annotation_ir)
for shape in sorted(data_manager.to_shapes(self._db_task.data.size), key=lambda shape: shape.get("z_order", 0)):
_get_frame(annotations, shape).labeled_shapes.append(self._export_labeled_shape(shape))
if 'track_id' in shape:
exported_shape = self._export_tracked_shape(shape)
else:
exported_shape = self._export_labeled_shape(shape)
_get_frame(annotations, shape).labeled_shapes.append(exported_shape)

for tag in self._annotation_ir.tags:
_get_frame(annotations, tag).tags.append(self._export_tag(tag))
Expand All @@ -332,14 +339,17 @@ def shapes(self):

@property
def tracks(self):
for track in self._annotation_ir.tracks:
for idx, track in enumerate(self._annotation_ir.tracks):
tracked_shapes = TrackManager.get_interpolated_shapes(track, 0, self._db_task.data.size)
for tracked_shape in tracked_shapes:
tracked_shape["attributes"] += track["attributes"]
tracked_shape["track_id"] = idx
tracked_shape["group"] = track["group"]
tracked_shape["label_id"] = track["label_id"]

yield Annotation.Track(
label=self._get_label_name(track["label_id"]),
group=track['group'],
group=track["group"],
shapes=[self._export_tracked_shape(shape) for shape in tracked_shapes],
)

Expand Down
6 changes: 2 additions & 4 deletions cvat/apps/annotation/format.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@

def register_format(format_file):
source_code = open(format_file, 'r').read()
global_vars = {
"__builtins__": {},
}
global_vars = {}
exec(source_code, global_vars)
if "format_spec" not in global_vars or not isinstance(global_vars["format_spec"], dict):
raise Exception("Could not find \'format_spec\' definition in format file specification")
raise Exception("Could not find 'format_spec' definition in format file specification")

format_spec = deepcopy(global_vars["format_spec"])
format_spec["handler_file"] = File(open(format_file))
Expand Down
Loading

0 comments on commit 887c6f0

Please sign in to comment.