2
2
from operator import itemgetter
3
3
from typing import Dict , Generator , List , Tuple , Union
4
4
from collections import defaultdict
5
+ import warnings
5
6
6
7
from pydantic import BaseModel
7
8
8
- from ...annotation_types .annotation import ClassificationAnnotation , ObjectAnnotation , VideoClassificationAnnotation
9
+ from ...annotation_types .annotation import ClassificationAnnotation , ObjectAnnotation , VideoClassificationAnnotation , VideoObjectAnnotation
9
10
from ...annotation_types .collection import LabelCollection , LabelGenerator
10
11
from ...annotation_types .data import ImageData , TextData , VideoData
11
12
from ...annotation_types .label import Label
15
16
16
17
from .metric import NDScalarMetric , NDMetricAnnotation , NDConfusionMatrixMetric
17
18
from .classification import NDChecklistSubclass , NDClassification , NDClassificationType , NDRadioSubclass
18
- from .objects import NDObject , NDObjectType
19
+ from .objects import NDObject , NDObjectType , NDSegments
19
20
20
21
21
22
class NDLabel (BaseModel ):
22
23
annotations : List [Union [NDObjectType , NDClassificationType ,
23
- NDConfusionMatrixMetric , NDScalarMetric ]]
24
+ NDConfusionMatrixMetric , NDScalarMetric ,
25
+ NDSegments ]]
24
26
25
27
def to_common (self ) -> LabelGenerator :
26
28
grouped_annotations = defaultdict (list )
@@ -37,15 +39,20 @@ def from_common(cls,
37
39
yield from cls ._create_video_annotations (label )
38
40
39
41
def _generate_annotations (
40
- self , grouped_annotations : Dict [str , List [Union [NDObjectType ,
41
- NDClassificationType ,
42
- NDConfusionMatrixMetric ,
43
- NDScalarMetric ]]]
42
+ self ,
43
+ grouped_annotations : Dict [str ,
44
+ List [Union [NDObjectType , NDClassificationType ,
45
+ NDConfusionMatrixMetric ,
46
+ NDScalarMetric , NDSegments ]]]
44
47
) -> Generator [Label , None , None ]:
45
48
for data_row_id , annotations in grouped_annotations .items ():
46
49
annots = []
47
50
for annotation in annotations :
48
- if isinstance (annotation , NDObjectType .__args__ ):
51
+ if isinstance (annotation , NDSegments ):
52
+ annots .extend (
53
+ NDSegments .to_common (annotation , annotation .schema_id ))
54
+
55
+ elif isinstance (annotation , NDObjectType .__args__ ):
49
56
annots .append (NDObject .to_common (annotation ))
50
57
elif isinstance (annotation , NDClassificationType .__args__ ):
51
58
annots .extend (NDClassification .to_common (annotation ))
@@ -55,7 +62,6 @@ def _generate_annotations(
55
62
else :
56
63
raise TypeError (
57
64
f"Unsupported annotation. { type (annotation )} " )
58
-
59
65
data = self ._infer_media_type (annotations )(uid = data_row_id )
60
66
yield Label (annotations = annots , data = data )
61
67
@@ -65,7 +71,7 @@ def _infer_media_type(
65
71
types = {type (annotation ) for annotation in annotations }
66
72
if TextEntity in types :
67
73
return TextData
68
- elif VideoClassificationAnnotation in types :
74
+ elif VideoClassificationAnnotation in types or VideoObjectAnnotation in types :
69
75
return VideoData
70
76
else :
71
77
return ImageData
@@ -83,26 +89,46 @@ def _get_consecutive_frames(
83
89
def _create_video_annotations (
84
90
cls , label : Label
85
91
) -> Generator [Union [NDChecklistSubclass , NDRadioSubclass ], None , None ]:
92
+
86
93
video_annotations = defaultdict (list )
87
94
for annot in label .annotations :
88
- if isinstance (annot , VideoClassificationAnnotation ):
95
+ if isinstance (
96
+ annot ,
97
+ (VideoClassificationAnnotation , VideoObjectAnnotation )):
89
98
video_annotations [annot .feature_schema_id ].append (annot )
90
99
91
100
for annotation_group in video_annotations .values ():
92
101
consecutive_frames = cls ._get_consecutive_frames (
93
102
sorted ([annotation .frame for annotation in annotation_group ]))
94
- annotation = annotation_group [0 ]
95
- frames_data = []
96
- for frames in consecutive_frames :
97
- frames_data .append ({'start' : frames [0 ], 'end' : frames [- 1 ]})
98
- annotation .extra .update ({'frames' : frames_data })
99
- yield NDClassification .from_common (annotation , label .data )
103
+
104
+ if isinstance (annotation_group [0 ], VideoClassificationAnnotation ):
105
+ annotation = annotation_group [0 ]
106
+ frames_data = []
107
+ for frames in consecutive_frames :
108
+ frames_data .append ({'start' : frames [0 ], 'end' : frames [- 1 ]})
109
+ annotation .extra .update ({'frames' : frames_data })
110
+ yield NDClassification .from_common (annotation , label .data )
111
+
112
+ elif isinstance (annotation_group [0 ], VideoObjectAnnotation ):
113
+ warnings .warn (
114
+ """Nested classifications are not currently supported
115
+ for video object annotations
116
+ and will not import alongside the object annotations.""" )
117
+ segments = []
118
+ for start_frame , end_frame in consecutive_frames :
119
+ segment = []
120
+ for annotation in annotation_group :
121
+ if annotation .keyframe and start_frame <= annotation .frame <= end_frame :
122
+ segment .append (annotation )
123
+ segments .append (segment )
124
+ yield NDObject .from_common (segments , label .data )
100
125
101
126
@classmethod
102
127
def _create_non_video_annotations (cls , label : Label ):
103
128
non_video_annotations = [
104
129
annot for annot in label .annotations
105
- if not isinstance (annot , VideoClassificationAnnotation )
130
+ if not isinstance (annot , (VideoClassificationAnnotation ,
131
+ VideoObjectAnnotation ))
106
132
]
107
133
for annotation in non_video_annotations :
108
134
if isinstance (annotation , ClassificationAnnotation ):
0 commit comments