Skip to content

Commit 3aa4abf

Browse files
telenachosnmanovic
authored andcommitted
Adding dump and load support for MOT CSV format. (#830)
* Adding dump and load support for MOT CSV format. * Updated test cases to use correct track annotations for MOT format. * Removed behaviour of MOT loader which would duplicate the last track shape prior to setting outside=True.
1 parent 112bab1 commit 3aa4abf

File tree

5 files changed

+115
-0
lines changed

5 files changed

+115
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ https://github.com/opencv/cvat/issues/750).
1313
- Mask-RCNN Auto Annotation Script in OpenVINO format
1414
- Yolo Auto Annotation Script
1515
- Auto segmentation using Mask_RCNN component (Keras+Tensorflow Mask R-CNN Segmentation)
16+
- Added MOT CSV format support
1617
- Ability to dump/load annotations in LabelMe format from UI
1718

1819
### Changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Format selection is possible after clicking on the Upload annotation / Dump anno
4343
| [MS COCO Object Detection](http://cocodataset.org/#format-data) | X | X |
4444
| PNG mask | X | |
4545
| [TFrecord](https://www.tensorflow.org/tutorials/load_data/tf_records) | X | X |
46+
| [MOT](https://motchallenge.net/) | X | X |
4647
| [LabelMe](http://labelme.csail.mit.edu/Release3.0) | X | X |
4748

4849
## Links

cvat/apps/annotation/mot.py

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# SPDX-License-Identifier: MIT
2+
format_spec = {
3+
"name": "MOT",
4+
"dumpers": [
5+
{
6+
"display_name": "{name} {format} {version}",
7+
"format": "CSV",
8+
"version": "1.0",
9+
"handler": "dump"
10+
},
11+
],
12+
"loaders": [
13+
{
14+
"display_name": "{name} {format} {version}",
15+
"format": "CSV",
16+
"version": "1.0",
17+
"handler": "load",
18+
}
19+
],
20+
}
21+
22+
23+
MOT = [
24+
"frame_id",
25+
"track_id",
26+
"xtl",
27+
"ytl",
28+
"width",
29+
"height",
30+
"confidence",
31+
"class_id",
32+
"visibility"
33+
]
34+
35+
36+
def dump(file_object, annotations):
37+
""" Export track shapes in MOT CSV format. Due to limitations of the MOT
38+
format, this process only supports rectangular interpolation mode
39+
annotations.
40+
"""
41+
import csv
42+
import io
43+
44+
# csv requires a text buffer
45+
with io.TextIOWrapper(file_object, encoding="utf-8") as csv_file:
46+
writer = csv.DictWriter(csv_file, fieldnames=MOT)
47+
for i, track in enumerate(annotations.tracks):
48+
for shape in track.shapes:
49+
# MOT doesn't support polygons or 'outside' property
50+
if shape.type != 'rectangle':
51+
continue
52+
writer.writerow({
53+
"frame_id": shape.frame,
54+
"track_id": i,
55+
"xtl": shape.points[0],
56+
"ytl": shape.points[1],
57+
"width": shape.points[2] - shape.points[0],
58+
"height": shape.points[3] - shape.points[1],
59+
"confidence": 1,
60+
"class_id": track.label,
61+
"visibility": 1 - int(shape.occluded)
62+
})
63+
64+
65+
def load(file_object, annotations):
66+
""" Read MOT CSV format and convert objects to annotated tracks.
67+
"""
68+
import csv
69+
import io
70+
tracks = {}
71+
# csv requires a text buffer
72+
with io.TextIOWrapper(file_object, encoding="utf-8") as csv_file:
73+
reader = csv.DictReader(csv_file, fieldnames=MOT)
74+
for row in reader:
75+
# create one shape per row
76+
xtl = float(row["xtl"])
77+
ytl = float(row["ytl"])
78+
xbr = xtl + float(row["width"])
79+
ybr = ytl + float(row["height"])
80+
shape = annotations.TrackedShape(
81+
type="rectangle",
82+
points=[xtl, ytl, xbr, ybr],
83+
occluded=float(row["visibility"]) == 0,
84+
outside=False,
85+
keyframe=False,
86+
z_order=0,
87+
frame=int(row["frame_id"]),
88+
attributes=[],
89+
)
90+
# build trajectories as lists of shapes in track dict
91+
track_id = int(row["track_id"])
92+
if track_id not in tracks:
93+
tracks[track_id] = annotations.Track(row["class_id"], track_id, [])
94+
tracks[track_id].shapes.append(shape)
95+
for track in tracks.values():
96+
# Set outside=True for the last shape since MOT has no support
97+
# for this flag
98+
last = annotations.TrackedShape(
99+
type=track.shapes[-1].type,
100+
points=track.shapes[-1].points,
101+
occluded=track.shapes[-1].occluded,
102+
outside=True,
103+
keyframe=track.shapes[-1].keyframe,
104+
z_order=track.shapes[-1].z_order,
105+
frame=track.shapes[-1].frame,
106+
attributes=track.shapes[-1].attributes,
107+
)
108+
track.shapes[-1] = last
109+
annotations.add_track(track)

cvat/apps/annotation/settings.py

+1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@
1212
os.path.join(path_prefix, 'coco.py'),
1313
os.path.join(path_prefix, 'mask.py'),
1414
os.path.join(path_prefix, 'tfrecord.py'),
15+
os.path.join(path_prefix, 'mot.py'),
1516
os.path.join(path_prefix, 'labelme.py'),
1617
)

cvat/apps/engine/tests/test_rest_api.py

+3
Original file line numberDiff line numberDiff line change
@@ -2662,6 +2662,9 @@ def _get_initial_annotation(annotation_format):
26622662
annotations["shapes"] = rectangle_shapes_with_attrs + rectangle_shapes_wo_attrs + polygon_shapes_wo_attrs
26632663
annotations["tracks"] = rectangle_tracks_with_attrs + rectangle_tracks_wo_attrs
26642664

2665+
elif annotation_format == "MOT CSV 1.0":
2666+
annotations["tracks"] = rectangle_tracks_wo_attrs
2667+
26652668
return annotations
26662669

26672670
response = self._get_annotation_formats(annotator)

0 commit comments

Comments
 (0)