OTVision 0.6.2__py3-none-any.whl → 0.6.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- OTVision/__init__.py +0 -10
- OTVision/application/buffer.py +2 -1
- OTVision/application/detect/current_object_detector.py +1 -2
- OTVision/application/detect/detected_frame_factory.py +4 -3
- OTVision/application/detect/detected_frame_producer.py +1 -2
- OTVision/detect/builder.py +1 -1
- OTVision/detect/detected_frame_buffer.py +4 -1
- OTVision/detect/otdet.py +3 -2
- OTVision/detect/video_input_source.py +1 -2
- OTVision/detect/yolo.py +2 -2
- OTVision/domain/detect_producer_consumer.py +1 -1
- OTVision/domain/detection.py +128 -7
- OTVision/domain/frame.py +155 -1
- OTVision/domain/object_detection.py +1 -2
- OTVision/track/model/filebased/frame_chunk.py +11 -11
- OTVision/track/model/tracking_interfaces.py +24 -30
- OTVision/track/parser/chunk_parser_plugins.py +6 -6
- OTVision/track/track.py +1 -1
- OTVision/track/tracker/filebased_tracking.py +7 -12
- OTVision/track/tracker/tracker_plugin_iou.py +10 -14
- OTVision/version.py +1 -1
- {otvision-0.6.2.dist-info → otvision-0.6.4.dist-info}/METADATA +5 -4
- {otvision-0.6.2.dist-info → otvision-0.6.4.dist-info}/RECORD +25 -27
- OTVision/track/model/detection.py +0 -141
- OTVision/track/model/frame.py +0 -149
- {otvision-0.6.2.dist-info → otvision-0.6.4.dist-info}/WHEEL +0 -0
- {otvision-0.6.2.dist-info → otvision-0.6.4.dist-info}/licenses/LICENSE +0 -0
OTVision/__init__.py
CHANGED
|
@@ -18,13 +18,3 @@ OTVision init module
|
|
|
18
18
|
#
|
|
19
19
|
# You should have received a copy of the GNU General Public License
|
|
20
20
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
# TODO: Might need to change this
|
|
24
|
-
from .convert.convert import main as convert
|
|
25
|
-
from .track.track import main as track
|
|
26
|
-
from .transform.transform import main as transform
|
|
27
|
-
|
|
28
|
-
# from .view.view import main as view
|
|
29
|
-
|
|
30
|
-
__all__: list = ["track", "convert", "transform"]
|
OTVision/application/buffer.py
CHANGED
|
@@ -22,7 +22,8 @@ class Buffer[T, SUBJECT_TYPE, OBSERVING_TYPE](Observable[SUBJECT_TYPE], Filter[T
|
|
|
22
22
|
return self._buffer
|
|
23
23
|
|
|
24
24
|
def _reset_buffer(self) -> None:
|
|
25
|
-
self._buffer
|
|
25
|
+
del self._buffer
|
|
26
|
+
self._buffer = list()
|
|
26
27
|
|
|
27
28
|
def on_flush(self, event: OBSERVING_TYPE) -> None:
|
|
28
29
|
buffered_elements = self._get_buffered_elements()
|
|
@@ -2,8 +2,7 @@ from typing import Generator
|
|
|
2
2
|
|
|
3
3
|
from OTVision.abstraction.pipes_and_filter import Filter
|
|
4
4
|
from OTVision.application.get_current_config import GetCurrentConfig
|
|
5
|
-
from OTVision.domain.
|
|
6
|
-
from OTVision.domain.frame import Frame
|
|
5
|
+
from OTVision.domain.frame import DetectedFrame, Frame
|
|
7
6
|
from OTVision.domain.object_detection import ObjectDetector, ObjectDetectorFactory
|
|
8
7
|
|
|
9
8
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from OTVision.domain.detection import
|
|
2
|
-
from OTVision.domain.frame import Frame, FrameKeys
|
|
1
|
+
from OTVision.domain.detection import Detection
|
|
2
|
+
from OTVision.domain.frame import DetectedFrame, Frame, FrameKeys
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class DetectedFrameFactory:
|
|
@@ -20,7 +20,8 @@ class DetectedFrameFactory:
|
|
|
20
20
|
|
|
21
21
|
return DetectedFrame(
|
|
22
22
|
source=frame[FrameKeys.source],
|
|
23
|
-
|
|
23
|
+
no=frame[FrameKeys.frame],
|
|
24
24
|
occurrence=frame[FrameKeys.occurrence],
|
|
25
25
|
detections=detections,
|
|
26
|
+
image=frame[FrameKeys.data],
|
|
26
27
|
)
|
|
@@ -2,8 +2,7 @@ from typing import Generator
|
|
|
2
2
|
|
|
3
3
|
from OTVision.abstraction.pipes_and_filter import Filter
|
|
4
4
|
from OTVision.domain.detect_producer_consumer import DetectedFrameProducer
|
|
5
|
-
from OTVision.domain.
|
|
6
|
-
from OTVision.domain.frame import Frame
|
|
5
|
+
from OTVision.domain.frame import DetectedFrame, Frame
|
|
7
6
|
from OTVision.domain.input_source_detect import InputSourceDetect
|
|
8
7
|
|
|
9
8
|
|
OTVision/detect/builder.py
CHANGED
|
@@ -42,7 +42,7 @@ from OTVision.detect.yolo import YoloDetectionConverter, YoloFactory
|
|
|
42
42
|
from OTVision.domain.cli import DetectCliParser
|
|
43
43
|
from OTVision.domain.current_config import CurrentConfig
|
|
44
44
|
from OTVision.domain.detect_producer_consumer import DetectedFrameProducer
|
|
45
|
-
from OTVision.domain.
|
|
45
|
+
from OTVision.domain.frame import DetectedFrame
|
|
46
46
|
from OTVision.domain.input_source_detect import InputSourceDetect
|
|
47
47
|
from OTVision.domain.object_detection import ObjectDetectorFactory
|
|
48
48
|
|
|
@@ -2,7 +2,7 @@ from dataclasses import dataclass
|
|
|
2
2
|
from datetime import datetime, timedelta
|
|
3
3
|
|
|
4
4
|
from OTVision.application.buffer import Buffer
|
|
5
|
-
from OTVision.domain.
|
|
5
|
+
from OTVision.domain.frame import DetectedFrame
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
@dataclass
|
|
@@ -55,3 +55,6 @@ class DetectedFrameBuffer(Buffer[DetectedFrame, DetectedFrameBufferEvent, FlushE
|
|
|
55
55
|
source_metadata=event.source_metadata, frames=elements
|
|
56
56
|
)
|
|
57
57
|
)
|
|
58
|
+
|
|
59
|
+
def buffer(self, to_buffer: DetectedFrame) -> None:
|
|
60
|
+
self._buffer.append(to_buffer.without_image())
|
OTVision/detect/otdet.py
CHANGED
|
@@ -4,7 +4,8 @@ from pathlib import Path
|
|
|
4
4
|
from typing import Self
|
|
5
5
|
|
|
6
6
|
from OTVision import dataformat, version
|
|
7
|
-
from OTVision.domain.detection import
|
|
7
|
+
from OTVision.domain.detection import Detection
|
|
8
|
+
from OTVision.domain.frame import DetectedFrame
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
@dataclass
|
|
@@ -74,7 +75,7 @@ class OtdetBuilder:
|
|
|
74
75
|
converted_detections = [
|
|
75
76
|
self.__convert_detection(detection) for detection in frame.detections
|
|
76
77
|
]
|
|
77
|
-
data[str(frame.
|
|
78
|
+
data[str(frame.no)] = {
|
|
78
79
|
dataformat.DETECTIONS: converted_detections,
|
|
79
80
|
dataformat.OCCURRENCE: frame.occurrence.timestamp(),
|
|
80
81
|
}
|
|
@@ -7,7 +7,6 @@ import av
|
|
|
7
7
|
from tqdm import tqdm
|
|
8
8
|
|
|
9
9
|
from OTVision.abstraction.observer import Subject
|
|
10
|
-
from OTVision.application.configure_logger import logger
|
|
11
10
|
from OTVision.application.detect.detection_file_save_path_provider import (
|
|
12
11
|
DetectionFileSavePathProvider,
|
|
13
12
|
)
|
|
@@ -130,7 +129,7 @@ class VideoSource(InputSourceDetect):
|
|
|
130
129
|
counter += 1
|
|
131
130
|
self.notify_observers(video_file, video_fps)
|
|
132
131
|
except Exception as e:
|
|
133
|
-
|
|
132
|
+
log.error(f"Error processing {video_file}", exc_info=e)
|
|
134
133
|
|
|
135
134
|
def __collect_files_to_detect(self) -> list[Path]:
|
|
136
135
|
filetypes = self._current_config.filetypes.video_filetypes.to_list()
|
OTVision/detect/yolo.py
CHANGED
|
@@ -33,8 +33,8 @@ from OTVision.abstraction.pipes_and_filter import Filter
|
|
|
33
33
|
from OTVision.application.detect.detected_frame_factory import DetectedFrameFactory
|
|
34
34
|
from OTVision.application.get_current_config import GetCurrentConfig
|
|
35
35
|
from OTVision.config import DetectConfig
|
|
36
|
-
from OTVision.domain.detection import
|
|
37
|
-
from OTVision.domain.frame import Frame, FrameKeys
|
|
36
|
+
from OTVision.domain.detection import Detection
|
|
37
|
+
from OTVision.domain.frame import DetectedFrame, Frame, FrameKeys
|
|
38
38
|
from OTVision.domain.object_detection import ObjectDetector, ObjectDetectorFactory
|
|
39
39
|
from OTVision.helpers.log import LOGGER_NAME
|
|
40
40
|
|
OTVision/domain/detection.py
CHANGED
|
@@ -1,9 +1,34 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
from OTVision.dataformat import (
|
|
4
|
+
CLASS,
|
|
5
|
+
CONFIDENCE,
|
|
6
|
+
FINISHED,
|
|
7
|
+
FIRST,
|
|
8
|
+
INTERPOLATED_DETECTION,
|
|
9
|
+
TRACK_ID,
|
|
10
|
+
H,
|
|
11
|
+
W,
|
|
12
|
+
X,
|
|
13
|
+
Y,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
TrackId = int
|
|
3
17
|
|
|
4
18
|
|
|
5
19
|
@dataclass(frozen=True, repr=True)
|
|
6
20
|
class Detection:
|
|
21
|
+
"""Detection data without track context data.
|
|
22
|
+
|
|
23
|
+
Attributes:
|
|
24
|
+
label (str): Assigned label, e.g. vehicle class.
|
|
25
|
+
conf (float): Confidence of detected class.
|
|
26
|
+
x (float): X-coordinate of detection center.
|
|
27
|
+
y (float): Y-coordinate of detection center.
|
|
28
|
+
w (float): Width of detection.
|
|
29
|
+
h (float): Height of detection.
|
|
30
|
+
"""
|
|
31
|
+
|
|
7
32
|
label: str
|
|
8
33
|
conf: float
|
|
9
34
|
x: float
|
|
@@ -11,10 +36,106 @@ class Detection:
|
|
|
11
36
|
w: float
|
|
12
37
|
h: float
|
|
13
38
|
|
|
39
|
+
def of_track(self, id: TrackId, is_first: bool) -> "TrackedDetection":
|
|
40
|
+
"""Convert to TrackedDetection by adding track information.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
id (TrackId): id of assigned track.
|
|
44
|
+
is_first (bool): whether this detection is first of track.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
TrackedDetection: This detection data with additional track information.
|
|
48
|
+
"""
|
|
49
|
+
return TrackedDetection(
|
|
50
|
+
self.label,
|
|
51
|
+
self.conf,
|
|
52
|
+
self.x,
|
|
53
|
+
self.y,
|
|
54
|
+
self.w,
|
|
55
|
+
self.h,
|
|
56
|
+
is_first,
|
|
57
|
+
id,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def to_otdet(self) -> dict:
|
|
61
|
+
return {
|
|
62
|
+
CLASS: self.label,
|
|
63
|
+
CONFIDENCE: self.conf,
|
|
64
|
+
X: self.x,
|
|
65
|
+
Y: self.y,
|
|
66
|
+
W: self.w,
|
|
67
|
+
H: self.h,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass(frozen=True, repr=True)
|
|
72
|
+
class TrackedDetection(Detection):
|
|
73
|
+
"""Detection with additional track data.
|
|
74
|
+
At the time a detection is tracked,
|
|
75
|
+
it might not be known whether it is the last of a track.
|
|
76
|
+
|
|
77
|
+
Attributes:
|
|
78
|
+
is_first (bool): whether this detection is the first in the track.
|
|
79
|
+
track_id (TrackId): id of the assigned track.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
is_first: bool
|
|
83
|
+
track_id: TrackId
|
|
84
|
+
|
|
85
|
+
def finish(self, is_last: bool, is_discarded: bool) -> "FinishedDetection":
|
|
86
|
+
return FinishedDetection.from_tracked_detection(self, is_last, is_discarded)
|
|
87
|
+
|
|
88
|
+
def as_last_detection(self, is_discarded: bool) -> "FinishedDetection":
|
|
89
|
+
return FinishedDetection.from_tracked_detection(
|
|
90
|
+
self, is_last=True, is_discarded=is_discarded
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
def as_intermediate_detection(self, is_discarded: bool) -> "FinishedDetection":
|
|
94
|
+
return FinishedDetection.from_tracked_detection(
|
|
95
|
+
self, is_last=False, is_discarded=is_discarded
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@dataclass(frozen=True, repr=True)
|
|
100
|
+
class FinishedDetection(TrackedDetection):
|
|
101
|
+
"""Detection data with extended track information including is_finished.
|
|
102
|
+
|
|
103
|
+
Attributes:
|
|
104
|
+
is_last (bool): whether this detection is the last in the track.
|
|
105
|
+
is_discarded (bool): whether the detections's track was discarded.
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
is_last: bool
|
|
109
|
+
is_discarded: bool
|
|
110
|
+
|
|
111
|
+
@classmethod
|
|
112
|
+
def from_tracked_detection(
|
|
113
|
+
cls, tracked_detection: TrackedDetection, is_last: bool, is_discarded: bool
|
|
114
|
+
) -> "FinishedDetection":
|
|
115
|
+
td = tracked_detection
|
|
116
|
+
return cls(
|
|
117
|
+
label=td.label,
|
|
118
|
+
conf=td.conf,
|
|
119
|
+
x=td.x,
|
|
120
|
+
y=td.y,
|
|
121
|
+
w=td.w,
|
|
122
|
+
h=td.h,
|
|
123
|
+
is_first=td.is_first,
|
|
124
|
+
track_id=td.track_id,
|
|
125
|
+
is_last=is_last,
|
|
126
|
+
is_discarded=is_discarded,
|
|
127
|
+
)
|
|
14
128
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
129
|
+
def to_dict(self) -> dict:
|
|
130
|
+
return {
|
|
131
|
+
CLASS: self.label,
|
|
132
|
+
CONFIDENCE: self.conf,
|
|
133
|
+
X: self.x,
|
|
134
|
+
Y: self.y,
|
|
135
|
+
W: self.w,
|
|
136
|
+
H: self.h,
|
|
137
|
+
INTERPOLATED_DETECTION: False,
|
|
138
|
+
FIRST: self.is_first,
|
|
139
|
+
FINISHED: self.is_last,
|
|
140
|
+
TRACK_ID: self.track_id,
|
|
141
|
+
}
|
OTVision/domain/frame.py
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
1
2
|
from datetime import datetime
|
|
2
|
-
from typing import Literal, Optional, TypedDict
|
|
3
|
+
from typing import Callable, Literal, Optional, Sequence, TypedDict
|
|
3
4
|
|
|
4
5
|
from numpy import ndarray
|
|
5
6
|
|
|
7
|
+
from OTVision.dataformat import FRAME, OCCURRENCE, TRACK_ID
|
|
8
|
+
from OTVision.domain.detection import (
|
|
9
|
+
Detection,
|
|
10
|
+
FinishedDetection,
|
|
11
|
+
TrackedDetection,
|
|
12
|
+
TrackId,
|
|
13
|
+
)
|
|
14
|
+
|
|
6
15
|
|
|
7
16
|
class FrameKeys:
|
|
8
17
|
"""Keys to access Frame dictionary."""
|
|
@@ -27,3 +36,148 @@ class Frame(TypedDict):
|
|
|
27
36
|
frame: int
|
|
28
37
|
source: str
|
|
29
38
|
occurrence: datetime
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
FrameNo = int
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True, kw_only=True)
|
|
45
|
+
class DetectedFrame:
|
|
46
|
+
"""Frame metadata, optional image and respective detections.
|
|
47
|
+
|
|
48
|
+
Attributes:
|
|
49
|
+
no (FrameNo): Frame number.
|
|
50
|
+
occurrence (datetime): Time stamp, at which frame was recorded.
|
|
51
|
+
source (str): Source from where frame was obtained, e.g. video file path.
|
|
52
|
+
detections (Sequence[Detection]): A sequence of Detections occurring in frame.
|
|
53
|
+
image (Optional[ndarray]): Optional image data of frame.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
no: FrameNo
|
|
57
|
+
occurrence: datetime
|
|
58
|
+
source: str
|
|
59
|
+
detections: Sequence[Detection]
|
|
60
|
+
image: Optional[ndarray] = None
|
|
61
|
+
|
|
62
|
+
def without_image(self) -> "DetectedFrame":
|
|
63
|
+
return DetectedFrame(
|
|
64
|
+
no=self.no,
|
|
65
|
+
occurrence=self.occurrence,
|
|
66
|
+
source=self.source,
|
|
67
|
+
detections=self.detections,
|
|
68
|
+
image=None,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
IsLastFrame = Callable[[FrameNo, TrackId], bool]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@dataclass(frozen=True, kw_only=True)
|
|
76
|
+
class TrackedFrame(DetectedFrame):
|
|
77
|
+
"""Frame metadata with tracked detections.
|
|
78
|
+
Also provides additional aggregated information about:
|
|
79
|
+
observed, finished and unfinished tracks.
|
|
80
|
+
|
|
81
|
+
Attributes:
|
|
82
|
+
detections (Sequence[TrackedDetection]): overrides Frame.detections with more
|
|
83
|
+
specific type of detection.
|
|
84
|
+
observed_tracks (set[TrackId]): set of tracks of which detection occur in this
|
|
85
|
+
frame.
|
|
86
|
+
finished_tracks (set[TrackId]): track ids of tracks observed in this or prior
|
|
87
|
+
to this frame that can now be considered finished. These track ids should
|
|
88
|
+
no longer be observed/assigned in future frames. (successfully completed)
|
|
89
|
+
discarded_tracks (set[TrackId]): track ids, that are now considered discarded.
|
|
90
|
+
The corresponding tracks are no longer pursued, previous TrackedDetections
|
|
91
|
+
of these tracks are also considered discarded. Discarded tracks may be
|
|
92
|
+
observed but not finished.(unsuccessful, incomplete)
|
|
93
|
+
unfinished_tracks (set[TrackId]): observed tracks that are not yet finished
|
|
94
|
+
and were not discarded.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
detections: Sequence[TrackedDetection]
|
|
98
|
+
finished_tracks: set[TrackId]
|
|
99
|
+
discarded_tracks: set[TrackId]
|
|
100
|
+
observed_tracks: set[TrackId] = field(init=False)
|
|
101
|
+
unfinished_tracks: set[TrackId] = field(init=False)
|
|
102
|
+
|
|
103
|
+
def __post_init__(self) -> None:
|
|
104
|
+
"""
|
|
105
|
+
Derive observed and unfinished tracks from tracked detections and finished
|
|
106
|
+
track information.
|
|
107
|
+
"""
|
|
108
|
+
observed = {d.track_id for d in self.detections}
|
|
109
|
+
object.__setattr__(self, "observed_tracks", observed)
|
|
110
|
+
|
|
111
|
+
unfinished = {
|
|
112
|
+
o
|
|
113
|
+
for o in self.observed_tracks
|
|
114
|
+
if o not in self.finished_tracks and o not in self.discarded_tracks
|
|
115
|
+
}
|
|
116
|
+
object.__setattr__(self, "unfinished_tracks", unfinished)
|
|
117
|
+
|
|
118
|
+
def finish(
|
|
119
|
+
self,
|
|
120
|
+
is_last: IsLastFrame,
|
|
121
|
+
discarded_tracks: set[TrackId],
|
|
122
|
+
keep_discarded: bool = False,
|
|
123
|
+
) -> "FinishedFrame":
|
|
124
|
+
"""Turn this TrackedFrame into a finished frame
|
|
125
|
+
by adding is_finished information to all its detections.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
is_last (IsLastFrame): function to determine whether
|
|
129
|
+
a track is finished in a certain frame.
|
|
130
|
+
discarded_tracks (set[TrackId]): list of tracks considered discarded.
|
|
131
|
+
Used to mark corresponding tracks.
|
|
132
|
+
keep_discarded (bool): whether FinishedDetections marked as discarded
|
|
133
|
+
should be kept in detections list. Defaults to False.
|
|
134
|
+
Returns:
|
|
135
|
+
FinishedFrame: frame with FinishedDetections
|
|
136
|
+
"""
|
|
137
|
+
if keep_discarded:
|
|
138
|
+
detections = [
|
|
139
|
+
det.finish(
|
|
140
|
+
is_last=is_last(self.no, det.track_id),
|
|
141
|
+
is_discarded=(det.track_id in discarded_tracks),
|
|
142
|
+
)
|
|
143
|
+
for det in self.detections
|
|
144
|
+
]
|
|
145
|
+
else:
|
|
146
|
+
detections = [
|
|
147
|
+
det.finish(is_last=is_last(self.no, det.track_id), is_discarded=False)
|
|
148
|
+
for det in self.detections
|
|
149
|
+
if (det.track_id not in discarded_tracks)
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
return FinishedFrame(
|
|
153
|
+
no=self.no,
|
|
154
|
+
occurrence=self.occurrence,
|
|
155
|
+
source=self.source,
|
|
156
|
+
finished_tracks=self.finished_tracks,
|
|
157
|
+
detections=detections,
|
|
158
|
+
image=self.image,
|
|
159
|
+
discarded_tracks=discarded_tracks,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@dataclass(frozen=True, kw_only=True)
|
|
164
|
+
class FinishedFrame(TrackedFrame):
|
|
165
|
+
"""TrackedFrame with FinishedDetections.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
detections (Sequence[FinishedDetection]): overrides TrackedFrame.detections
|
|
169
|
+
with more specific detection type.
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
detections: Sequence[FinishedDetection]
|
|
173
|
+
|
|
174
|
+
def to_detection_dicts(self) -> list[dict]:
|
|
175
|
+
frame_metadata = {FRAME: self.no, OCCURRENCE: self.occurrence.timestamp()}
|
|
176
|
+
|
|
177
|
+
# add frame metadata to each detection dict
|
|
178
|
+
detection_dict_list = [
|
|
179
|
+
{**detection.to_dict(), **frame_metadata} for detection in self.detections
|
|
180
|
+
]
|
|
181
|
+
|
|
182
|
+
detection_dict_list.sort(key=lambda det: det[TRACK_ID])
|
|
183
|
+
return detection_dict_list
|
|
@@ -2,8 +2,7 @@ from abc import ABC, abstractmethod
|
|
|
2
2
|
from typing import Generator
|
|
3
3
|
|
|
4
4
|
from OTVision.config import DetectConfig
|
|
5
|
-
from OTVision.domain.
|
|
6
|
-
from OTVision.domain.frame import Frame
|
|
5
|
+
from OTVision.domain.frame import DetectedFrame, Frame
|
|
7
6
|
|
|
8
7
|
|
|
9
8
|
class ObjectDetectorMetadata(ABC):
|
|
@@ -6,15 +6,15 @@ from typing import Sequence
|
|
|
6
6
|
from tqdm import tqdm
|
|
7
7
|
|
|
8
8
|
from OTVision.dataformat import FRAME, INPUT_FILE_PATH, TRACK_ID
|
|
9
|
-
from OTVision.
|
|
10
|
-
from OTVision.
|
|
9
|
+
from OTVision.domain.detection import TrackId
|
|
10
|
+
from OTVision.domain.frame import (
|
|
11
|
+
DetectedFrame,
|
|
11
12
|
FinishedFrame,
|
|
12
|
-
Frame,
|
|
13
13
|
FrameNo,
|
|
14
14
|
IsLastFrame,
|
|
15
15
|
TrackedFrame,
|
|
16
|
-
TrackId,
|
|
17
16
|
)
|
|
17
|
+
from OTVision.track.model.filebased.frame_group import FrameGroup, get_output_file
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
@dataclass(frozen=True)
|
|
@@ -25,13 +25,13 @@ class FrameChunk:
|
|
|
25
25
|
Attributes:
|
|
26
26
|
file (Path): common file path source of Frames.
|
|
27
27
|
metadata (dict): otdet metadata.
|
|
28
|
-
frames (Sequence[
|
|
28
|
+
frames (Sequence[DetectedFrame]): a sequence of untracked Frames.
|
|
29
29
|
frame_group_id (int): id of FrameGroup this FrameCHunk is part of.
|
|
30
30
|
"""
|
|
31
31
|
|
|
32
32
|
file: Path
|
|
33
33
|
metadata: dict
|
|
34
|
-
frames: Sequence[
|
|
34
|
+
frames: Sequence[DetectedFrame]
|
|
35
35
|
frame_group_id: int
|
|
36
36
|
|
|
37
37
|
def check_output_file_exists(self, with_suffix: str) -> bool:
|
|
@@ -49,7 +49,7 @@ class TrackedChunk(FrameChunk):
|
|
|
49
49
|
Attributes:
|
|
50
50
|
is_last_chunk (bool): whether this chunk is the last of
|
|
51
51
|
subsequently (related/connected) chunks.
|
|
52
|
-
frames (Sequence[TrackedFrame
|
|
52
|
+
frames (Sequence[TrackedFrame]): overrides frames
|
|
53
53
|
with more specific frame type.
|
|
54
54
|
finished_tracks (set[TrackId]): aggregates finished tracks
|
|
55
55
|
of given TrackedFrames.
|
|
@@ -62,7 +62,7 @@ class TrackedChunk(FrameChunk):
|
|
|
62
62
|
"""
|
|
63
63
|
|
|
64
64
|
is_last_chunk: bool
|
|
65
|
-
frames: Sequence[TrackedFrame
|
|
65
|
+
frames: Sequence[TrackedFrame] = field(init=False)
|
|
66
66
|
|
|
67
67
|
finished_tracks: set[TrackId] = field(init=False)
|
|
68
68
|
observed_tracks: set[TrackId] = field(init=False)
|
|
@@ -75,7 +75,7 @@ class TrackedChunk(FrameChunk):
|
|
|
75
75
|
file: Path,
|
|
76
76
|
metadata: dict,
|
|
77
77
|
is_last_chunk: bool,
|
|
78
|
-
frames: Sequence[TrackedFrame
|
|
78
|
+
frames: Sequence[TrackedFrame],
|
|
79
79
|
frame_group_id: int,
|
|
80
80
|
) -> None:
|
|
81
81
|
|
|
@@ -165,11 +165,11 @@ class FinishedChunk(TrackedChunk):
|
|
|
165
165
|
"""A chunk of FinishedFrames.
|
|
166
166
|
|
|
167
167
|
Attributes:
|
|
168
|
-
frames (Sequence[FinishedFrame
|
|
168
|
+
frames (Sequence[FinishedFrame]): overrides frames
|
|
169
169
|
with more specific frame type.
|
|
170
170
|
"""
|
|
171
171
|
|
|
172
|
-
frames: Sequence[FinishedFrame
|
|
172
|
+
frames: Sequence[FinishedFrame]
|
|
173
173
|
|
|
174
174
|
def to_detection_dicts(self) -> list[dict]:
|
|
175
175
|
chunk_metadata = {INPUT_FILE_PATH: self.file.as_posix()}
|
|
@@ -1,46 +1,40 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
from typing import Generic, Iterator, TypeVar
|
|
3
3
|
|
|
4
|
-
from OTVision.
|
|
4
|
+
from OTVision.domain.detection import TrackId
|
|
5
|
+
from OTVision.domain.frame import (
|
|
6
|
+
DetectedFrame,
|
|
5
7
|
FinishedFrame,
|
|
6
|
-
Frame,
|
|
7
8
|
FrameNo,
|
|
8
9
|
IsLastFrame,
|
|
9
10
|
TrackedFrame,
|
|
10
|
-
TrackId,
|
|
11
11
|
)
|
|
12
12
|
|
|
13
|
-
S = TypeVar("S") # Source type (e.g., Path, URL, str, etc.)
|
|
14
|
-
# -> would look nicer in python 3.12
|
|
15
|
-
|
|
16
13
|
ID_GENERATOR = Iterator[TrackId]
|
|
17
14
|
|
|
18
15
|
|
|
19
|
-
class Tracker(ABC
|
|
16
|
+
class Tracker(ABC):
|
|
20
17
|
"""Tracker interface for processing a stream of Frames
|
|
21
18
|
to add tracking information, creating a lazy stream (generator)
|
|
22
19
|
of TrackedFrames.
|
|
23
20
|
|
|
24
21
|
Implementing class can specify template method:
|
|
25
22
|
track_frame for processing a single frame.
|
|
26
|
-
|
|
27
|
-
Args:
|
|
28
|
-
Generic (S): generic type of Frame source (e.g. file path, or stream url)
|
|
29
23
|
"""
|
|
30
24
|
|
|
31
25
|
def track(
|
|
32
|
-
self, frames: Iterator[
|
|
33
|
-
) -> Iterator[TrackedFrame
|
|
26
|
+
self, frames: Iterator[DetectedFrame], id_generator: ID_GENERATOR
|
|
27
|
+
) -> Iterator[TrackedFrame]:
|
|
34
28
|
"""Process the given stream of Frames,
|
|
35
29
|
yielding TrackedFrames one by one as a lazy stream of TrackedFrames.
|
|
36
30
|
|
|
37
31
|
Args:
|
|
38
|
-
frames (Iterator[
|
|
32
|
+
frames (Iterator[DetectedFrame]): (lazy) stream of Frames
|
|
39
33
|
with untracked Detections.
|
|
40
34
|
id_generator (ID_GENERATOR): provider of new (unique) track ids.
|
|
41
35
|
|
|
42
36
|
Yields:
|
|
43
|
-
Iterator[TrackedFrame
|
|
37
|
+
Iterator[TrackedFrame]: (lazy) stream of TrackedFrames with
|
|
44
38
|
TrackedDetections
|
|
45
39
|
"""
|
|
46
40
|
for frame in frames:
|
|
@@ -49,19 +43,19 @@ class Tracker(ABC, Generic[S]):
|
|
|
49
43
|
@abstractmethod
|
|
50
44
|
def track_frame(
|
|
51
45
|
self,
|
|
52
|
-
frame:
|
|
46
|
+
frame: DetectedFrame,
|
|
53
47
|
id_generator: ID_GENERATOR,
|
|
54
|
-
) -> TrackedFrame
|
|
48
|
+
) -> TrackedFrame:
|
|
55
49
|
"""Process single Frame with untracked Detections,
|
|
56
50
|
by adding tracking information,
|
|
57
51
|
creating a TrackedFrame with TrackedDetections.
|
|
58
52
|
|
|
59
53
|
Args:
|
|
60
|
-
frame (
|
|
54
|
+
frame (DetectedFrame): the Frame to be tracked.
|
|
61
55
|
id_generator (ID_GENERATOR): provider of new (unique) track ids.
|
|
62
56
|
|
|
63
57
|
Returns:
|
|
64
|
-
TrackedFrame
|
|
58
|
+
TrackedFrame: TrackedFrame with TrackedDetections
|
|
65
59
|
"""
|
|
66
60
|
pass
|
|
67
61
|
|
|
@@ -268,42 +262,42 @@ class UnfinishedTracksBuffer(ABC, Generic[C, F]):
|
|
|
268
262
|
return finished_containers
|
|
269
263
|
|
|
270
264
|
|
|
271
|
-
class UnfinishedFramesBuffer(UnfinishedTracksBuffer[TrackedFrame
|
|
265
|
+
class UnfinishedFramesBuffer(UnfinishedTracksBuffer[TrackedFrame, FinishedFrame]):
|
|
272
266
|
"""UnfinishedTracksBuffer implementation for Frames as Detection container."""
|
|
273
267
|
|
|
274
|
-
def __init__(self, tracker: Tracker
|
|
268
|
+
def __init__(self, tracker: Tracker, keep_discarded: bool = False):
|
|
275
269
|
super().__init__(keep_discarded)
|
|
276
270
|
self._tracker = tracker
|
|
277
271
|
|
|
278
272
|
def track(
|
|
279
|
-
self, frames: Iterator[
|
|
280
|
-
) -> Iterator[FinishedFrame
|
|
273
|
+
self, frames: Iterator[DetectedFrame], id_generator: ID_GENERATOR
|
|
274
|
+
) -> Iterator[FinishedFrame]:
|
|
281
275
|
tracked_frame_stream = self._tracker.track(frames, id_generator)
|
|
282
276
|
return self.track_and_finish(tracked_frame_stream)
|
|
283
277
|
|
|
284
|
-
def _get_last_track_frames(self, container: TrackedFrame
|
|
278
|
+
def _get_last_track_frames(self, container: TrackedFrame) -> dict[TrackId, int]:
|
|
285
279
|
return {o: container.no for o in container.observed_tracks}
|
|
286
280
|
|
|
287
|
-
def _get_unfinished_tracks(self, container: TrackedFrame
|
|
281
|
+
def _get_unfinished_tracks(self, container: TrackedFrame) -> set[TrackId]:
|
|
288
282
|
return container.unfinished_tracks
|
|
289
283
|
|
|
290
|
-
def _get_observed_tracks(self, container: TrackedFrame
|
|
284
|
+
def _get_observed_tracks(self, container: TrackedFrame) -> set[TrackId]:
|
|
291
285
|
return container.observed_tracks
|
|
292
286
|
|
|
293
|
-
def _get_newly_finished_tracks(self, container: TrackedFrame
|
|
287
|
+
def _get_newly_finished_tracks(self, container: TrackedFrame) -> set[TrackId]:
|
|
294
288
|
return container.finished_tracks
|
|
295
289
|
|
|
296
|
-
def _get_newly_discarded_tracks(self, container: TrackedFrame
|
|
290
|
+
def _get_newly_discarded_tracks(self, container: TrackedFrame) -> set[TrackId]:
|
|
297
291
|
return container.discarded_tracks
|
|
298
292
|
|
|
299
|
-
def _get_last_frame_of_container(self, container: TrackedFrame
|
|
293
|
+
def _get_last_frame_of_container(self, container: TrackedFrame) -> FrameNo:
|
|
300
294
|
return container.no
|
|
301
295
|
|
|
302
296
|
def _finish(
|
|
303
297
|
self,
|
|
304
|
-
container: TrackedFrame
|
|
298
|
+
container: TrackedFrame,
|
|
305
299
|
is_last: IsLastFrame,
|
|
306
300
|
discarded_tracks: set[TrackId],
|
|
307
301
|
keep_discarded: bool,
|
|
308
|
-
) -> FinishedFrame
|
|
302
|
+
) -> FinishedFrame:
|
|
309
303
|
return container.finish(is_last, discarded_tracks, keep_discarded)
|
|
@@ -16,15 +16,15 @@ from OTVision.dataformat import (
|
|
|
16
16
|
X,
|
|
17
17
|
Y,
|
|
18
18
|
)
|
|
19
|
+
from OTVision.domain.detection import Detection
|
|
20
|
+
from OTVision.domain.frame import DetectedFrame
|
|
19
21
|
from OTVision.helpers.date import (
|
|
20
22
|
parse_date_string_to_utc_datime,
|
|
21
23
|
parse_timestamp_string_to_utc_datetime,
|
|
22
24
|
)
|
|
23
25
|
from OTVision.helpers.files import denormalize_bbox, read_json
|
|
24
|
-
from OTVision.track.model.detection import Detection
|
|
25
26
|
from OTVision.track.model.filebased.frame_chunk import ChunkParser, FrameChunk
|
|
26
27
|
from OTVision.track.model.filebased.frame_group import FrameGroup
|
|
27
|
-
from OTVision.track.model.frame import Frame
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
class JsonChunkParser(ChunkParser):
|
|
@@ -47,7 +47,7 @@ class JsonChunkParser(ChunkParser):
|
|
|
47
47
|
|
|
48
48
|
def convert(
|
|
49
49
|
self, file: Path, frame_offset: int, input: dict[int, dict[str, Any]]
|
|
50
|
-
) -> list[
|
|
50
|
+
) -> list[DetectedFrame]:
|
|
51
51
|
detection_parser = DetectionParser()
|
|
52
52
|
frames = []
|
|
53
53
|
|
|
@@ -58,10 +58,10 @@ class JsonChunkParser(ChunkParser):
|
|
|
58
58
|
occurrence: datetime = parse_datetime(value[OCCURRENCE])
|
|
59
59
|
data_detections = value[DETECTIONS]
|
|
60
60
|
detections = detection_parser.convert(data_detections)
|
|
61
|
-
parsed_frame =
|
|
62
|
-
int(key) + frame_offset,
|
|
61
|
+
parsed_frame = DetectedFrame(
|
|
62
|
+
no=int(key) + frame_offset,
|
|
63
63
|
occurrence=occurrence,
|
|
64
|
-
source=file,
|
|
64
|
+
source=str(file),
|
|
65
65
|
detections=detections,
|
|
66
66
|
image=None,
|
|
67
67
|
)
|
OTVision/track/track.py
CHANGED
|
@@ -111,7 +111,7 @@ def main(
|
|
|
111
111
|
log.warning(f"No files of type '{filetypes}' found to track!")
|
|
112
112
|
return
|
|
113
113
|
|
|
114
|
-
iou_tracker: IouTracker
|
|
114
|
+
iou_tracker: IouTracker = IouTracker(
|
|
115
115
|
parameters=IouParameters(sigma_l, sigma_h, sigma_iou, t_min, t_miss_max)
|
|
116
116
|
)
|
|
117
117
|
|
|
@@ -6,6 +6,8 @@ from more_itertools import peekable
|
|
|
6
6
|
from tqdm import tqdm
|
|
7
7
|
|
|
8
8
|
from OTVision.config import CONFIG, DEFAULT_FILETYPE, OVERWRITE, TRACK
|
|
9
|
+
from OTVision.domain.detection import TrackId
|
|
10
|
+
from OTVision.domain.frame import DetectedFrame, FrameNo, IsLastFrame, TrackedFrame
|
|
9
11
|
from OTVision.helpers.log import LOGGER_NAME
|
|
10
12
|
from OTVision.track.model.filebased.frame_chunk import (
|
|
11
13
|
ChunkParser,
|
|
@@ -14,13 +16,6 @@ from OTVision.track.model.filebased.frame_chunk import (
|
|
|
14
16
|
TrackedChunk,
|
|
15
17
|
)
|
|
16
18
|
from OTVision.track.model.filebased.frame_group import FrameGroup, FrameGroupParser
|
|
17
|
-
from OTVision.track.model.frame import (
|
|
18
|
-
Frame,
|
|
19
|
-
FrameNo,
|
|
20
|
-
IsLastFrame,
|
|
21
|
-
TrackedFrame,
|
|
22
|
-
TrackId,
|
|
23
|
-
)
|
|
24
19
|
from OTVision.track.model.tracking_interfaces import (
|
|
25
20
|
ID_GENERATOR,
|
|
26
21
|
Tracker,
|
|
@@ -30,18 +25,18 @@ from OTVision.track.model.tracking_interfaces import (
|
|
|
30
25
|
log = logging.getLogger(LOGGER_NAME)
|
|
31
26
|
|
|
32
27
|
|
|
33
|
-
class ChunkBasedTracker(Tracker
|
|
28
|
+
class ChunkBasedTracker(Tracker):
|
|
34
29
|
|
|
35
|
-
def __init__(self, tracker: Tracker
|
|
30
|
+
def __init__(self, tracker: Tracker, chunkParser: ChunkParser) -> None:
|
|
36
31
|
super().__init__()
|
|
37
32
|
self._chunk_parser = chunkParser
|
|
38
33
|
self._tracker = tracker
|
|
39
34
|
|
|
40
35
|
def track_frame(
|
|
41
36
|
self,
|
|
42
|
-
frames:
|
|
37
|
+
frames: DetectedFrame,
|
|
43
38
|
id_generator: ID_GENERATOR,
|
|
44
|
-
) -> TrackedFrame
|
|
39
|
+
) -> TrackedFrame:
|
|
45
40
|
return self._tracker.track_frame(frames, id_generator)
|
|
46
41
|
|
|
47
42
|
def track_chunk(
|
|
@@ -82,7 +77,7 @@ class GroupedFilesTracker(ChunkBasedTracker):
|
|
|
82
77
|
|
|
83
78
|
def __init__(
|
|
84
79
|
self,
|
|
85
|
-
tracker: Tracker
|
|
80
|
+
tracker: Tracker,
|
|
86
81
|
chunk_parser: ChunkParser,
|
|
87
82
|
frame_group_parser: FrameGroupParser,
|
|
88
83
|
id_generator_factory: ID_GENERATOR_FACTORY,
|
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
|
|
3
|
-
from OTVision.
|
|
4
|
-
from OTVision.
|
|
5
|
-
from OTVision.track.model.tracking_interfaces import
|
|
6
|
-
ID_GENERATOR,
|
|
7
|
-
FrameNo,
|
|
8
|
-
S,
|
|
9
|
-
Tracker,
|
|
10
|
-
TrackId,
|
|
11
|
-
)
|
|
3
|
+
from OTVision.domain.detection import Detection, TrackedDetection, TrackId
|
|
4
|
+
from OTVision.domain.frame import DetectedFrame, FrameNo, TrackedFrame
|
|
5
|
+
from OTVision.track.model.tracking_interfaces import ID_GENERATOR, Tracker
|
|
12
6
|
|
|
13
7
|
|
|
14
8
|
@dataclass(frozen=True)
|
|
@@ -72,7 +66,9 @@ class ActiveIouTrack:
|
|
|
72
66
|
last_frame: FrameNo
|
|
73
67
|
track_age: int
|
|
74
68
|
|
|
75
|
-
def __init__(
|
|
69
|
+
def __init__(
|
|
70
|
+
self, id: TrackId, frame: "DetectedFrame", detection: "Detection"
|
|
71
|
+
) -> None:
|
|
76
72
|
self.id = id
|
|
77
73
|
self.frame_no = [frame.no]
|
|
78
74
|
self.bboxes = [BoundingBox.from_xywh(detection)]
|
|
@@ -85,7 +81,7 @@ class ActiveIouTrack:
|
|
|
85
81
|
self.last_frame = frame.no
|
|
86
82
|
self.track_age = 0
|
|
87
83
|
|
|
88
|
-
def add_detection(self, frame:
|
|
84
|
+
def add_detection(self, frame: DetectedFrame, detection: Detection) -> None:
|
|
89
85
|
self.frame_no.append(frame.no)
|
|
90
86
|
self.bboxes.append(BoundingBox.from_xywh(detection))
|
|
91
87
|
self.center.append(Coordinate.center_of(detection))
|
|
@@ -141,7 +137,7 @@ def iou(
|
|
|
141
137
|
return size_intersection / size_union
|
|
142
138
|
|
|
143
139
|
|
|
144
|
-
class IouTracker(Tracker
|
|
140
|
+
class IouTracker(Tracker):
|
|
145
141
|
|
|
146
142
|
def __init__(self, parameters: IouParameters):
|
|
147
143
|
super().__init__()
|
|
@@ -169,8 +165,8 @@ class IouTracker(Tracker[S]):
|
|
|
169
165
|
return self.parameters.t_miss_max
|
|
170
166
|
|
|
171
167
|
def track_frame(
|
|
172
|
-
self, frame:
|
|
173
|
-
) -> TrackedFrame
|
|
168
|
+
self, frame: DetectedFrame, id_generator: ID_GENERATOR
|
|
169
|
+
) -> TrackedFrame:
|
|
174
170
|
|
|
175
171
|
detections = [d for d in frame.detections if d.conf >= self.sigma_l]
|
|
176
172
|
tracked_detections: list[TrackedDetection] = []
|
OTVision/version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: OTVision
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.4
|
|
4
4
|
Summary: OTVision is a core module of the OpenTrafficCam framework to perform object detection and tracking.
|
|
5
5
|
Project-URL: Homepage, https://opentrafficcam.org/
|
|
6
6
|
Project-URL: Documentation, https://opentrafficcam.org/overview/
|
|
@@ -22,8 +22,9 @@ Requires-Dist: geopandas==1.0.1
|
|
|
22
22
|
Requires-Dist: ijson==3.3.0
|
|
23
23
|
Requires-Dist: more-itertools==10.5.0
|
|
24
24
|
Requires-Dist: moviepy==1.0.3
|
|
25
|
-
Requires-Dist: numpy==1.26.4
|
|
26
|
-
Requires-Dist:
|
|
25
|
+
Requires-Dist: numpy==1.26.4; sys_platform == 'win32'
|
|
26
|
+
Requires-Dist: numpy==2.1.1; sys_platform != 'win32'
|
|
27
|
+
Requires-Dist: opencv-python==4.11.0.86
|
|
27
28
|
Requires-Dist: pandas==2.2.3
|
|
28
29
|
Requires-Dist: pyyaml==6.0.2
|
|
29
30
|
Requires-Dist: setuptools==74.0.0
|
|
@@ -31,7 +32,7 @@ Requires-Dist: torch==2.3.1
|
|
|
31
32
|
Requires-Dist: torchvision==0.18.1
|
|
32
33
|
Requires-Dist: tqdm==4.67.1
|
|
33
34
|
Requires-Dist: ujson==5.10.0
|
|
34
|
-
Requires-Dist: ultralytics==8.3.
|
|
35
|
+
Requires-Dist: ultralytics==8.3.94
|
|
35
36
|
Description-Content-Type: text/markdown
|
|
36
37
|
|
|
37
38
|
# OTVision
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
OTVision/__init__.py,sha256=
|
|
1
|
+
OTVision/__init__.py,sha256=CLnfgTlVHM4_nzDacvy06Z_Crc3hU6usd0mUyEvBf24,781
|
|
2
2
|
OTVision/config.py,sha256=x_Bh3mrfV434puVchuG0zLsTtjIl-_C0-ZbmRW3tyFM,24371
|
|
3
3
|
OTVision/dataformat.py,sha256=BHF7qHzyNb80hI1EKfwcdJ9bgG_X4bp_hCXzdg7_MSA,1941
|
|
4
|
-
OTVision/version.py,sha256=
|
|
4
|
+
OTVision/version.py,sha256=yVwTz7eqQw6SQVvUKkYsgB6HAwAyCSDykNLqZF7h1bA,175
|
|
5
5
|
OTVision/abstraction/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
6
|
OTVision/abstraction/observer.py,sha256=ZFGxUUjI3wUpf5ogXg2yDe-QjCcXre6SxH5zOogOx2U,1350
|
|
7
7
|
OTVision/abstraction/pipes_and_filter.py,sha256=rzYaWDalXnlMbfpkgI91a0FL0q1llUlCRuIQsidYDv8,940
|
|
8
8
|
OTVision/application/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
OTVision/application/buffer.py,sha256=
|
|
9
|
+
OTVision/application/buffer.py,sha256=eoK3Rfvc_72cQD3EAASzmxhznjmnIFFh3LX9fqAzXyU,1166
|
|
10
10
|
OTVision/application/configure_logger.py,sha256=RmRgYkvSXWqpFX3N2n0h2LDna3uL8AY-yfVZU66t5q4,611
|
|
11
11
|
OTVision/application/frame_count_provider.py,sha256=zN_75IM-w9Xlc5rT8OArhiWhPHR8mUfFhdzhSmQQuaM,693
|
|
12
12
|
OTVision/application/get_config.py,sha256=fRJZUWJvlTed6qriw82-B-05CAqfBma6NQ23WrKqYdE,771
|
|
13
13
|
OTVision/application/get_current_config.py,sha256=RPVexRhJxY4ul8sU0W2OJaj98eTJxMlP-CiioQZCpEI,299
|
|
14
14
|
OTVision/application/update_current_config.py,sha256=1p-F00myUofn-S97NMMfJ9SbemJiLKc_FS3ob_27YjM,321
|
|
15
15
|
OTVision/application/detect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
-
OTVision/application/detect/current_object_detector.py,sha256=
|
|
16
|
+
OTVision/application/detect/current_object_detector.py,sha256=J4U5k1s902agN90FYR4M_gO8MYYa_5XkYGREjStWCxQ,1343
|
|
17
17
|
OTVision/application/detect/current_object_detector_metadata.py,sha256=xai0UBEzxr-rxXCc8mTmNDECds7mdsw2sem5HZxvQ4Q,1017
|
|
18
|
-
OTVision/application/detect/detected_frame_factory.py,sha256=
|
|
19
|
-
OTVision/application/detect/detected_frame_producer.py,sha256=
|
|
18
|
+
OTVision/application/detect/detected_frame_factory.py,sha256=Rl6ElFYulgWPK_XZQSXZcT6caF8hvfzMKsOFefYO_pY,963
|
|
19
|
+
OTVision/application/detect/detected_frame_producer.py,sha256=LD4AnQG04YGE68TpxmaRuWRZeCZfhp8oixk6SHTru7c,906
|
|
20
20
|
OTVision/application/detect/detection_file_save_path_provider.py,sha256=nUyzgR7imrH8PkUl_72kdUDiolPXq1_RQqbpFwLI5Cs,2165
|
|
21
21
|
OTVision/application/detect/factory.py,sha256=UWQHqpaT6FyhMTzTIBSGR06_Hxo8HKM2-IlSpP_djYA,926
|
|
22
22
|
OTVision/application/detect/get_detect_cli_args.py,sha256=gezr17im8SwbuXW1suCodWRrFs8lSljNKu76SbWBgkY,265
|
|
@@ -25,26 +25,26 @@ OTVision/application/detect/update_detect_config_with_cli_args.py,sha256=NP4asZx
|
|
|
25
25
|
OTVision/convert/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
26
|
OTVision/convert/convert.py,sha256=vRIFcrAUf9MUWAuvF_q7JQ9aN89B1ouBOCtXrCLuDp8,11013
|
|
27
27
|
OTVision/detect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
-
OTVision/detect/builder.py,sha256=
|
|
28
|
+
OTVision/detect/builder.py,sha256=ZFSUnDmx742DqxuEG3N8Hf7ynvE0jLkxvRYAApbB3zA,6967
|
|
29
29
|
OTVision/detect/cli.py,sha256=qsmbt43j0KAyj8XVuo-QEHxQZJ80DVmpDGZ-d1lE14I,6025
|
|
30
30
|
OTVision/detect/detect.py,sha256=YaVS-DJXdEmh-OzwE31UPNl2uk7mcFyO_CKKTgMeiuM,1328
|
|
31
|
-
OTVision/detect/detected_frame_buffer.py,sha256=
|
|
32
|
-
OTVision/detect/otdet.py,sha256=
|
|
31
|
+
OTVision/detect/detected_frame_buffer.py,sha256=BsyPQ9nI6QdN40ZZKA-eXTxRU_noYuDsH2UhzSdVkQY,1444
|
|
32
|
+
OTVision/detect/otdet.py,sha256=6yYSNHayzBBVu4PBaMobkh7jBhQKQ6KbnQPOefwoJh8,4508
|
|
33
33
|
OTVision/detect/otdet_file_writer.py,sha256=9xeBD5I44Yp3DblZWpnOQuBVRWDQ2eObyevmBid2Wyw,4252
|
|
34
34
|
OTVision/detect/pyav_frame_count_provider.py,sha256=w7p9iM3F2fljV8SD7q491gQhIHANbVczqtalcUiKj-E,453
|
|
35
35
|
OTVision/detect/timestamper.py,sha256=4wsbkv-ex4zGhPCcYWqQQ2JbqVVPCmVzoT2U6efsy-o,5232
|
|
36
|
-
OTVision/detect/video_input_source.py,sha256=
|
|
37
|
-
OTVision/detect/yolo.py,sha256=
|
|
36
|
+
OTVision/detect/video_input_source.py,sha256=dVH2gPRV5yW4mpoIK6S0iQKWZJCwIhMnPLabJwDtbWI,8495
|
|
37
|
+
OTVision/detect/yolo.py,sha256=YkeE1A-NULsOgx0y2SH16nzBNBYAAd-TdQLzYGfuBH8,9845
|
|
38
38
|
OTVision/detect/plugin_av/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
39
39
|
OTVision/detect/plugin_av/rotate_frame.py,sha256=4wJqTYI2HRlfa4p2Ffap33vLmKIzE_EwFvQraEkQ4R8,1055
|
|
40
40
|
OTVision/domain/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
41
41
|
OTVision/domain/cli.py,sha256=XRIDSv4TJvXxwJw1UVznHSpaeUIRiikCabyGuiwh_2o,1049
|
|
42
42
|
OTVision/domain/current_config.py,sha256=TKb6Nuei8n2v6k873w0AkDNAnvSwqphdi5h6hlZ4Y5k,274
|
|
43
|
-
OTVision/domain/detect_producer_consumer.py,sha256=
|
|
44
|
-
OTVision/domain/detection.py,sha256=
|
|
45
|
-
OTVision/domain/frame.py,sha256=
|
|
43
|
+
OTVision/domain/detect_producer_consumer.py,sha256=gD7NwscQZLmCxMDZpZkGql0oMrpGHDBBNvdTXs58Vvw,855
|
|
44
|
+
OTVision/domain/detection.py,sha256=SZLP-87XE3NcTkeYz7GTqp4oPMiqI1P5gILp1_yHtxY,3761
|
|
45
|
+
OTVision/domain/frame.py,sha256=qHduCRbBTgzGLIuu7MlLWvhzphPlD3V0nrjlEApmr00,6211
|
|
46
46
|
OTVision/domain/input_source_detect.py,sha256=9DzkTg5dh7_KmxE9oxdmxrcTYhvZY8hHLZwhrh7Gz2o,1245
|
|
47
|
-
OTVision/domain/object_detection.py,sha256=
|
|
47
|
+
OTVision/domain/object_detection.py,sha256=k2VoQlaUhnpAUtX-OdbgDdt4qN0o7zjFGA5rZn1gneY,1251
|
|
48
48
|
OTVision/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
49
49
|
OTVision/helpers/date.py,sha256=XK0fmXMombQ2CJ2_RXjk-bc-R8qqXRK2rDmCMBi0_G0,854
|
|
50
50
|
OTVision/helpers/files.py,sha256=g_ix1e_ti1ZlW4zOulvkUiupcE8KJV_OHGNGXQTQ71I,18478
|
|
@@ -54,23 +54,21 @@ OTVision/helpers/log.py,sha256=fOSMTXQRQ3_3zzYL8pDlx85IXPwyDsI2WGpK-V_R47Q,4985
|
|
|
54
54
|
OTVision/helpers/machine.py,sha256=8Bz_Eg7PS0IL4riOVeJcEIi5D9E8Ju8-JomTkW975p8,2166
|
|
55
55
|
OTVision/helpers/video.py,sha256=xyI35CiWXqoeGd3HeLhZUPxrLz8GccWyzHusxoweJr4,1480
|
|
56
56
|
OTVision/track/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
57
|
-
OTVision/track/track.py,sha256=
|
|
57
|
+
OTVision/track/track.py,sha256=hrmvwIR4nV6wA8DUuYPSCrDXr7cAP13WXo_gZDBX7hE,5054
|
|
58
58
|
OTVision/track/exporter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
59
59
|
OTVision/track/exporter/filebased_exporter.py,sha256=7LFoHcqrlNCd0KDQZA5qI2sZFlxecazJ6sBwdVvvdWE,947
|
|
60
60
|
OTVision/track/model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
61
|
-
OTVision/track/model/detection.py,sha256=SZLP-87XE3NcTkeYz7GTqp4oPMiqI1P5gILp1_yHtxY,3761
|
|
62
|
-
OTVision/track/model/frame.py,sha256=p07Va0zhXPvkXSZR9ewgMuBK0XHD7AFNRA4HhCfaew0,5290
|
|
63
61
|
OTVision/track/model/track_exporter.py,sha256=FUuMsdNqWmjqRQasqB6A_0h73NAt-Mt8V93O_ajCHf4,3228
|
|
64
|
-
OTVision/track/model/tracking_interfaces.py,sha256=
|
|
62
|
+
OTVision/track/model/tracking_interfaces.py,sha256=UCCikNv3RQScyHj0Db9jvDS3RaxZXPKLmS1VAYynZ4U,10785
|
|
65
63
|
OTVision/track/model/filebased/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
66
|
-
OTVision/track/model/filebased/frame_chunk.py,sha256=
|
|
64
|
+
OTVision/track/model/filebased/frame_chunk.py,sha256=rXhQCHXWGJbePy5ZW3JZCdltGz5mZxFdcrW0mgez-2k,6771
|
|
67
65
|
OTVision/track/model/filebased/frame_group.py,sha256=f-hXS1Vc5U_qf2cgNbYVeSTZ3dg5NUJhasOEHuuX1HE,2977
|
|
68
66
|
OTVision/track/parser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
69
|
-
OTVision/track/parser/chunk_parser_plugins.py,sha256=
|
|
67
|
+
OTVision/track/parser/chunk_parser_plugins.py,sha256=ojBv_mCOXABmygxz5mhXzeysur3ECmT4a4VlGkeT11k,3108
|
|
70
68
|
OTVision/track/parser/frame_group_parser_plugins.py,sha256=N5jS6Aq2Aq4gc0ouX0UOBqCUagNLRQRjS_81k_OzZi4,4575
|
|
71
69
|
OTVision/track/tracker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
72
|
-
OTVision/track/tracker/filebased_tracking.py,sha256=
|
|
73
|
-
OTVision/track/tracker/tracker_plugin_iou.py,sha256=
|
|
70
|
+
OTVision/track/tracker/filebased_tracking.py,sha256=bYE1_NX08dy3KDrBpD8aTobtQqg9Id8h4WudZgUEZ5Q,6546
|
|
71
|
+
OTVision/track/tracker/tracker_plugin_iou.py,sha256=4e4DUOClwn8cYYyloUj2ux7DTCVeAs7WMptmAeDMNWY,7234
|
|
74
72
|
OTVision/transform/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
75
73
|
OTVision/transform/get_homography.py,sha256=29waW61uzCCB7tlBAS2zck9sliAxZqnjjOa4jOOHHIc,5970
|
|
76
74
|
OTVision/transform/reference_points_picker.py,sha256=LOmwzCaqbJnXVhaTSaZtkCzTMDamYjbTpY8I99pN0rg,16578
|
|
@@ -83,7 +81,7 @@ OTVision/view/view_helpers.py,sha256=a5yV_6ZxO5bxsSymOmxdHqzOEv0VFq4wFBopVRGuVRo
|
|
|
83
81
|
OTVision/view/view_track.py,sha256=vmfMqpbUfnzg_EsWiL-IIKNOApVF09dzSojHpUfYY6M,5393
|
|
84
82
|
OTVision/view/view_transform.py,sha256=HvRd8g8geKRy0OoiZUDn_oC3SJC5nuXhZf3uZelfGKg,5473
|
|
85
83
|
OTVision/view/helpers/OTC.ico,sha256=G9kwlDtgBXmXO3yxW6Z-xVFV2q4nUGuz9E1VPHSu_I8,21662
|
|
86
|
-
otvision-0.6.
|
|
87
|
-
otvision-0.6.
|
|
88
|
-
otvision-0.6.
|
|
89
|
-
otvision-0.6.
|
|
84
|
+
otvision-0.6.4.dist-info/METADATA,sha256=CRrwlIK6lpWLhfxKM2iDakGS3T2wpFkMsmbyfk52ASA,2361
|
|
85
|
+
otvision-0.6.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
86
|
+
otvision-0.6.4.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
87
|
+
otvision-0.6.4.dist-info/RECORD,,
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
|
|
3
|
-
from OTVision.dataformat import (
|
|
4
|
-
CLASS,
|
|
5
|
-
CONFIDENCE,
|
|
6
|
-
FINISHED,
|
|
7
|
-
FIRST,
|
|
8
|
-
INTERPOLATED_DETECTION,
|
|
9
|
-
TRACK_ID,
|
|
10
|
-
H,
|
|
11
|
-
W,
|
|
12
|
-
X,
|
|
13
|
-
Y,
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
TrackId = int
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@dataclass(frozen=True, repr=True)
|
|
20
|
-
class Detection:
|
|
21
|
-
"""Detection data without track context data.
|
|
22
|
-
|
|
23
|
-
Attributes:
|
|
24
|
-
label (str): Assigned label, e.g. vehicle class.
|
|
25
|
-
conf (float): Confidence of detected class.
|
|
26
|
-
x (float): X-coordinate of detection center.
|
|
27
|
-
y (float): Y-coordinate of detection center.
|
|
28
|
-
w (float): Width of detection.
|
|
29
|
-
h (float): Height of detection.
|
|
30
|
-
"""
|
|
31
|
-
|
|
32
|
-
label: str
|
|
33
|
-
conf: float
|
|
34
|
-
x: float
|
|
35
|
-
y: float
|
|
36
|
-
w: float
|
|
37
|
-
h: float
|
|
38
|
-
|
|
39
|
-
def of_track(self, id: TrackId, is_first: bool) -> "TrackedDetection":
|
|
40
|
-
"""Convert to TrackedDetection by adding track information.
|
|
41
|
-
|
|
42
|
-
Args:
|
|
43
|
-
id (TrackId): id of assigned track.
|
|
44
|
-
is_first (bool): whether this detection is first of track.
|
|
45
|
-
|
|
46
|
-
Returns:
|
|
47
|
-
TrackedDetection: This detection data with additional track information.
|
|
48
|
-
"""
|
|
49
|
-
return TrackedDetection(
|
|
50
|
-
self.label,
|
|
51
|
-
self.conf,
|
|
52
|
-
self.x,
|
|
53
|
-
self.y,
|
|
54
|
-
self.w,
|
|
55
|
-
self.h,
|
|
56
|
-
is_first,
|
|
57
|
-
id,
|
|
58
|
-
)
|
|
59
|
-
|
|
60
|
-
def to_otdet(self) -> dict:
|
|
61
|
-
return {
|
|
62
|
-
CLASS: self.label,
|
|
63
|
-
CONFIDENCE: self.conf,
|
|
64
|
-
X: self.x,
|
|
65
|
-
Y: self.y,
|
|
66
|
-
W: self.w,
|
|
67
|
-
H: self.h,
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
@dataclass(frozen=True, repr=True)
|
|
72
|
-
class TrackedDetection(Detection):
|
|
73
|
-
"""Detection with additional track data.
|
|
74
|
-
At the time a detection is tracked,
|
|
75
|
-
it might not be known whether it is the last of a track.
|
|
76
|
-
|
|
77
|
-
Attributes:
|
|
78
|
-
is_first (bool): whether this detection is the first in the track.
|
|
79
|
-
track_id (TrackId): id of the assigned track.
|
|
80
|
-
"""
|
|
81
|
-
|
|
82
|
-
is_first: bool
|
|
83
|
-
track_id: TrackId
|
|
84
|
-
|
|
85
|
-
def finish(self, is_last: bool, is_discarded: bool) -> "FinishedDetection":
|
|
86
|
-
return FinishedDetection.from_tracked_detection(self, is_last, is_discarded)
|
|
87
|
-
|
|
88
|
-
def as_last_detection(self, is_discarded: bool) -> "FinishedDetection":
|
|
89
|
-
return FinishedDetection.from_tracked_detection(
|
|
90
|
-
self, is_last=True, is_discarded=is_discarded
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
def as_intermediate_detection(self, is_discarded: bool) -> "FinishedDetection":
|
|
94
|
-
return FinishedDetection.from_tracked_detection(
|
|
95
|
-
self, is_last=False, is_discarded=is_discarded
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
@dataclass(frozen=True, repr=True)
|
|
100
|
-
class FinishedDetection(TrackedDetection):
|
|
101
|
-
"""Detection data with extended track information including is_finished.
|
|
102
|
-
|
|
103
|
-
Attributes:
|
|
104
|
-
is_last (bool): whether this detection is the last in the track.
|
|
105
|
-
is_discarded (bool): whether the detections's track was discarded.
|
|
106
|
-
"""
|
|
107
|
-
|
|
108
|
-
is_last: bool
|
|
109
|
-
is_discarded: bool
|
|
110
|
-
|
|
111
|
-
@classmethod
|
|
112
|
-
def from_tracked_detection(
|
|
113
|
-
cls, tracked_detection: TrackedDetection, is_last: bool, is_discarded: bool
|
|
114
|
-
) -> "FinishedDetection":
|
|
115
|
-
td = tracked_detection
|
|
116
|
-
return cls(
|
|
117
|
-
label=td.label,
|
|
118
|
-
conf=td.conf,
|
|
119
|
-
x=td.x,
|
|
120
|
-
y=td.y,
|
|
121
|
-
w=td.w,
|
|
122
|
-
h=td.h,
|
|
123
|
-
is_first=td.is_first,
|
|
124
|
-
track_id=td.track_id,
|
|
125
|
-
is_last=is_last,
|
|
126
|
-
is_discarded=is_discarded,
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
def to_dict(self) -> dict:
|
|
130
|
-
return {
|
|
131
|
-
CLASS: self.label,
|
|
132
|
-
CONFIDENCE: self.conf,
|
|
133
|
-
X: self.x,
|
|
134
|
-
Y: self.y,
|
|
135
|
-
W: self.w,
|
|
136
|
-
H: self.h,
|
|
137
|
-
INTERPOLATED_DETECTION: False,
|
|
138
|
-
FIRST: self.is_first,
|
|
139
|
-
FINISHED: self.is_last,
|
|
140
|
-
TRACK_ID: self.track_id,
|
|
141
|
-
}
|
OTVision/track/model/frame.py
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass, field
|
|
2
|
-
from datetime import datetime
|
|
3
|
-
from typing import Callable, Generic, Optional, Sequence, TypeVar
|
|
4
|
-
|
|
5
|
-
from PIL.Image import Image
|
|
6
|
-
|
|
7
|
-
from OTVision.dataformat import FRAME, OCCURRENCE, TRACK_ID
|
|
8
|
-
from OTVision.track.model.detection import (
|
|
9
|
-
Detection,
|
|
10
|
-
FinishedDetection,
|
|
11
|
-
TrackedDetection,
|
|
12
|
-
TrackId,
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
FrameNo = int
|
|
16
|
-
S = TypeVar("S")
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@dataclass(frozen=True)
|
|
20
|
-
class Frame(Generic[S]):
|
|
21
|
-
"""Frame metadata, optional image and respective detections.
|
|
22
|
-
|
|
23
|
-
Attributes:
|
|
24
|
-
no (FrameNo): Frame number.
|
|
25
|
-
occurrence (datetime): Time stamp, at which frame was recorded.
|
|
26
|
-
source (S): Generic source from where frame was obtained, e.g. video file path.
|
|
27
|
-
detections (Sequence[Detection]): A sequence of Detections occurring in frame.
|
|
28
|
-
image (Optional[Image]): Optional image data of frame.
|
|
29
|
-
"""
|
|
30
|
-
|
|
31
|
-
no: FrameNo
|
|
32
|
-
occurrence: datetime
|
|
33
|
-
source: S
|
|
34
|
-
detections: Sequence[Detection]
|
|
35
|
-
image: Optional[Image]
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
IsLastFrame = Callable[[FrameNo, TrackId], bool]
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
@dataclass(frozen=True)
|
|
42
|
-
class TrackedFrame(Frame[S]):
|
|
43
|
-
"""Frame metadata with tracked detections.
|
|
44
|
-
Also provides additional aggregated information about:
|
|
45
|
-
observed, finished and unfinished tracks.
|
|
46
|
-
|
|
47
|
-
Attributes:
|
|
48
|
-
detections (Sequence[TrackedDetection]): overrides Frame.detections with more
|
|
49
|
-
specific type of detection.
|
|
50
|
-
observed_tracks (set[TrackId]): set of tracks of which detection occur in this
|
|
51
|
-
frame.
|
|
52
|
-
finished_tracks (set[TrackId]): track ids of tracks observed in this or prior
|
|
53
|
-
to this frame that can now be considered finished. These track ids should
|
|
54
|
-
no longer be observed/assigned in future frames. (successfully completed)
|
|
55
|
-
discarded_tracks (set[TrackId]): track ids, that are now considered discarded.
|
|
56
|
-
The corresponding tracks are no longer pursued, previous TrackedDetections
|
|
57
|
-
of these tracks are also considered discarded. Discarded tracks may be
|
|
58
|
-
observed but not finished.(unsuccessful, incomplete)
|
|
59
|
-
unfinished_tracks (set[TrackId]): observed tracks that are not yet finished
|
|
60
|
-
and were not discarded.
|
|
61
|
-
"""
|
|
62
|
-
|
|
63
|
-
detections: Sequence[TrackedDetection]
|
|
64
|
-
finished_tracks: set[TrackId]
|
|
65
|
-
discarded_tracks: set[TrackId]
|
|
66
|
-
observed_tracks: set[TrackId] = field(init=False)
|
|
67
|
-
unfinished_tracks: set[TrackId] = field(init=False)
|
|
68
|
-
|
|
69
|
-
def __post_init__(self) -> None:
|
|
70
|
-
"""
|
|
71
|
-
Derive observed and unfinished tracks from tracked detections and finished
|
|
72
|
-
track information.
|
|
73
|
-
"""
|
|
74
|
-
observed = {d.track_id for d in self.detections}
|
|
75
|
-
object.__setattr__(self, "observed_tracks", observed)
|
|
76
|
-
|
|
77
|
-
unfinished = {
|
|
78
|
-
o
|
|
79
|
-
for o in self.observed_tracks
|
|
80
|
-
if o not in self.finished_tracks and o not in self.discarded_tracks
|
|
81
|
-
}
|
|
82
|
-
object.__setattr__(self, "unfinished_tracks", unfinished)
|
|
83
|
-
|
|
84
|
-
def finish(
|
|
85
|
-
self,
|
|
86
|
-
is_last: IsLastFrame,
|
|
87
|
-
discarded_tracks: set[TrackId],
|
|
88
|
-
keep_discarded: bool = False,
|
|
89
|
-
) -> "FinishedFrame":
|
|
90
|
-
"""Turn this TrackedFrame into a finished frame
|
|
91
|
-
by adding is_finished information to all its detections.
|
|
92
|
-
|
|
93
|
-
Args:
|
|
94
|
-
is_last (IsLastFrame): function to determine whether
|
|
95
|
-
a track is finished in a certain frame.
|
|
96
|
-
discarded_tracks (set[TrackId]): list of tracks considered discarded.
|
|
97
|
-
Used to mark corresponding tracks.
|
|
98
|
-
keep_discarded (bool): whether FinishedDetections marked as discarded
|
|
99
|
-
should be kept in detections list. Defaults to False.
|
|
100
|
-
Returns:
|
|
101
|
-
FinishedFrame: frame with FinishedDetections
|
|
102
|
-
"""
|
|
103
|
-
if keep_discarded:
|
|
104
|
-
detections = [
|
|
105
|
-
det.finish(
|
|
106
|
-
is_last=is_last(self.no, det.track_id),
|
|
107
|
-
is_discarded=(det.track_id in discarded_tracks),
|
|
108
|
-
)
|
|
109
|
-
for det in self.detections
|
|
110
|
-
]
|
|
111
|
-
else:
|
|
112
|
-
detections = [
|
|
113
|
-
det.finish(is_last=is_last(self.no, det.track_id), is_discarded=False)
|
|
114
|
-
for det in self.detections
|
|
115
|
-
if (det.track_id not in discarded_tracks)
|
|
116
|
-
]
|
|
117
|
-
|
|
118
|
-
return FinishedFrame(
|
|
119
|
-
no=self.no,
|
|
120
|
-
occurrence=self.occurrence,
|
|
121
|
-
source=self.source,
|
|
122
|
-
finished_tracks=self.finished_tracks,
|
|
123
|
-
detections=detections,
|
|
124
|
-
image=self.image,
|
|
125
|
-
discarded_tracks=discarded_tracks,
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
@dataclass(frozen=True)
|
|
130
|
-
class FinishedFrame(TrackedFrame[S]):
|
|
131
|
-
"""TrackedFrame with FinishedDetections.
|
|
132
|
-
|
|
133
|
-
Args:
|
|
134
|
-
detections (Sequence[FinishedDetection]): overrides TrackedFrame.detections
|
|
135
|
-
with more specific detection type.
|
|
136
|
-
"""
|
|
137
|
-
|
|
138
|
-
detections: Sequence[FinishedDetection]
|
|
139
|
-
|
|
140
|
-
def to_detection_dicts(self) -> list[dict]:
|
|
141
|
-
frame_metadata = {FRAME: self.no, OCCURRENCE: self.occurrence.timestamp()}
|
|
142
|
-
|
|
143
|
-
# add frame metadata to each detection dict
|
|
144
|
-
detection_dict_list = [
|
|
145
|
-
{**detection.to_dict(), **frame_metadata} for detection in self.detections
|
|
146
|
-
]
|
|
147
|
-
|
|
148
|
-
detection_dict_list.sort(key=lambda det: det[TRACK_ID])
|
|
149
|
-
return detection_dict_list
|
|
File without changes
|
|
File without changes
|