OTVision 0.6.2__py3-none-any.whl → 0.6.3__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 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"]
@@ -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.detection import DetectedFrame
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 DetectedFrame, Detection
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
- frame_number=frame[FrameKeys.frame],
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.detection import DetectedFrame
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
 
@@ -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.detection import DetectedFrame
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.detection import DetectedFrame
5
+ from OTVision.domain.frame import DetectedFrame
6
6
 
7
7
 
8
8
  @dataclass
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 DetectedFrame, Detection
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.frame_number)] = {
78
+ data[str(frame.no)] = {
78
79
  dataformat.DETECTIONS: converted_detections,
79
80
  dataformat.OCCURRENCE: frame.occurrence.timestamp(),
80
81
  }
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 DetectedFrame, Detection
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
 
@@ -1,7 +1,7 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from typing import Generator
3
3
 
4
- from OTVision.domain.detection import DetectedFrame
4
+ from OTVision.domain.frame import DetectedFrame
5
5
 
6
6
 
7
7
  class DetectedFrameConsumer(ABC):
@@ -1,9 +1,34 @@
1
1
  from dataclasses import dataclass
2
- from datetime import datetime
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
- @dataclass(frozen=True)
16
- class DetectedFrame:
17
- source: str
18
- frame_number: int
19
- occurrence: datetime
20
- detections: list[Detection]
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,139 @@ 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
+
63
+ IsLastFrame = Callable[[FrameNo, TrackId], bool]
64
+
65
+
66
+ @dataclass(frozen=True, kw_only=True)
67
+ class TrackedFrame(DetectedFrame):
68
+ """Frame metadata with tracked detections.
69
+ Also provides additional aggregated information about:
70
+ observed, finished and unfinished tracks.
71
+
72
+ Attributes:
73
+ detections (Sequence[TrackedDetection]): overrides Frame.detections with more
74
+ specific type of detection.
75
+ observed_tracks (set[TrackId]): set of tracks of which detection occur in this
76
+ frame.
77
+ finished_tracks (set[TrackId]): track ids of tracks observed in this or prior
78
+ to this frame that can now be considered finished. These track ids should
79
+ no longer be observed/assigned in future frames. (successfully completed)
80
+ discarded_tracks (set[TrackId]): track ids, that are now considered discarded.
81
+ The corresponding tracks are no longer pursued, previous TrackedDetections
82
+ of these tracks are also considered discarded. Discarded tracks may be
83
+ observed but not finished.(unsuccessful, incomplete)
84
+ unfinished_tracks (set[TrackId]): observed tracks that are not yet finished
85
+ and were not discarded.
86
+ """
87
+
88
+ detections: Sequence[TrackedDetection]
89
+ finished_tracks: set[TrackId]
90
+ discarded_tracks: set[TrackId]
91
+ observed_tracks: set[TrackId] = field(init=False)
92
+ unfinished_tracks: set[TrackId] = field(init=False)
93
+
94
+ def __post_init__(self) -> None:
95
+ """
96
+ Derive observed and unfinished tracks from tracked detections and finished
97
+ track information.
98
+ """
99
+ observed = {d.track_id for d in self.detections}
100
+ object.__setattr__(self, "observed_tracks", observed)
101
+
102
+ unfinished = {
103
+ o
104
+ for o in self.observed_tracks
105
+ if o not in self.finished_tracks and o not in self.discarded_tracks
106
+ }
107
+ object.__setattr__(self, "unfinished_tracks", unfinished)
108
+
109
+ def finish(
110
+ self,
111
+ is_last: IsLastFrame,
112
+ discarded_tracks: set[TrackId],
113
+ keep_discarded: bool = False,
114
+ ) -> "FinishedFrame":
115
+ """Turn this TrackedFrame into a finished frame
116
+ by adding is_finished information to all its detections.
117
+
118
+ Args:
119
+ is_last (IsLastFrame): function to determine whether
120
+ a track is finished in a certain frame.
121
+ discarded_tracks (set[TrackId]): list of tracks considered discarded.
122
+ Used to mark corresponding tracks.
123
+ keep_discarded (bool): whether FinishedDetections marked as discarded
124
+ should be kept in detections list. Defaults to False.
125
+ Returns:
126
+ FinishedFrame: frame with FinishedDetections
127
+ """
128
+ if keep_discarded:
129
+ detections = [
130
+ det.finish(
131
+ is_last=is_last(self.no, det.track_id),
132
+ is_discarded=(det.track_id in discarded_tracks),
133
+ )
134
+ for det in self.detections
135
+ ]
136
+ else:
137
+ detections = [
138
+ det.finish(is_last=is_last(self.no, det.track_id), is_discarded=False)
139
+ for det in self.detections
140
+ if (det.track_id not in discarded_tracks)
141
+ ]
142
+
143
+ return FinishedFrame(
144
+ no=self.no,
145
+ occurrence=self.occurrence,
146
+ source=self.source,
147
+ finished_tracks=self.finished_tracks,
148
+ detections=detections,
149
+ image=self.image,
150
+ discarded_tracks=discarded_tracks,
151
+ )
152
+
153
+
154
+ @dataclass(frozen=True, kw_only=True)
155
+ class FinishedFrame(TrackedFrame):
156
+ """TrackedFrame with FinishedDetections.
157
+
158
+ Args:
159
+ detections (Sequence[FinishedDetection]): overrides TrackedFrame.detections
160
+ with more specific detection type.
161
+ """
162
+
163
+ detections: Sequence[FinishedDetection]
164
+
165
+ def to_detection_dicts(self) -> list[dict]:
166
+ frame_metadata = {FRAME: self.no, OCCURRENCE: self.occurrence.timestamp()}
167
+
168
+ # add frame metadata to each detection dict
169
+ detection_dict_list = [
170
+ {**detection.to_dict(), **frame_metadata} for detection in self.detections
171
+ ]
172
+
173
+ detection_dict_list.sort(key=lambda det: det[TRACK_ID])
174
+ 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.detection import DetectedFrame
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.track.model.filebased.frame_group import FrameGroup, get_output_file
10
- from OTVision.track.model.frame import (
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[Frame[Path]]): a sequence of untracked Frames.
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[Frame[Path]]
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[Path]]): overrides frames
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[Path]] = field(init=False)
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[Path]],
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[Path]]): overrides frames
168
+ frames (Sequence[FinishedFrame]): overrides frames
169
169
  with more specific frame type.
170
170
  """
171
171
 
172
- frames: Sequence[FinishedFrame[Path]]
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.track.model.frame import (
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, Generic[S]):
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[Frame[S]], id_generator: ID_GENERATOR
33
- ) -> Iterator[TrackedFrame[S]]:
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[Frame[S]]): (lazy) stream of Frames
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[S]]: (lazy) stream of TrackedFrames with
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: Frame[S],
46
+ frame: DetectedFrame,
53
47
  id_generator: ID_GENERATOR,
54
- ) -> TrackedFrame[S]:
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 (Frame[S]): the Frame (with source S) to be tracked.
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[S]: TrackedFrame with TrackedDetections
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[S], FinishedFrame[S]]):
265
+ class UnfinishedFramesBuffer(UnfinishedTracksBuffer[TrackedFrame, FinishedFrame]):
272
266
  """UnfinishedTracksBuffer implementation for Frames as Detection container."""
273
267
 
274
- def __init__(self, tracker: Tracker[S], keep_discarded: bool = False):
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[Frame[S]], id_generator: ID_GENERATOR
280
- ) -> Iterator[FinishedFrame[S]]:
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[S]) -> dict[TrackId, int]:
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[S]) -> set[TrackId]:
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[S]) -> set[TrackId]:
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[S]) -> set[TrackId]:
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[S]) -> set[TrackId]:
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[S]) -> FrameNo:
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[S],
298
+ container: TrackedFrame,
305
299
  is_last: IsLastFrame,
306
300
  discarded_tracks: set[TrackId],
307
301
  keep_discarded: bool,
308
- ) -> FinishedFrame[S]:
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[Frame[Path]]:
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 = 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[Path] = 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[Path]):
28
+ class ChunkBasedTracker(Tracker):
34
29
 
35
- def __init__(self, tracker: Tracker[Path], chunkParser: ChunkParser) -> None:
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: Frame[Path],
37
+ frames: DetectedFrame,
43
38
  id_generator: ID_GENERATOR,
44
- ) -> TrackedFrame[Path]:
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[Path],
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.track.model.detection import Detection, TrackedDetection
4
- from OTVision.track.model.frame import Frame, TrackedFrame
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__(self, id: TrackId, frame: "Frame", detection: "Detection") -> None:
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: Frame, detection: Detection) -> None:
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[S]):
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: Frame[S], id_generator: ID_GENERATOR
173
- ) -> TrackedFrame[S]:
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,4 +1,4 @@
1
- __version__ = "v0.6.2"
1
+ __version__ = "v0.6.3"
2
2
 
3
3
 
4
4
  def otdet_version() -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: OTVision
3
- Version: 0.6.2
3
+ Version: 0.6.3
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: opencv-python==4.9.0.80
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.74
35
+ Requires-Dist: ultralytics==8.3.94
35
36
  Description-Content-Type: text/markdown
36
37
 
37
38
  # OTVision
@@ -1,7 +1,7 @@
1
- OTVision/__init__.py,sha256=b2goS-TYrN9y8WHmjG22z1oVwCrjV0WtGJE32NJfvJM,1042
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=fADL6JlfA7K_Y8SkaM8PKO79Wscnqe81tRANXC3Fy2U,175
4
+ OTVision/version.py,sha256=LsZveHynUdA099dDFIxJcaPj9GVc31eUilqAarmeesE,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
@@ -13,10 +13,10 @@ OTVision/application/get_config.py,sha256=fRJZUWJvlTed6qriw82-B-05CAqfBma6NQ23Wr
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=dmPbZGIXE49TbBZP-KAZWPgiMEeyZKdiaD2VlLM1SxI,1380
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=x7FPckcqVVov912ysBiUVvn6eVn3-rqd2rjzXrZBNrY,932
19
- OTVision/application/detect/detected_frame_producer.py,sha256=hUxmA2wx-OXRAjGZieDfsvRQUaJN7IT42R1Po4iUPhA,943
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=fOhbspd4TuCJMDR2Vvbc4hOJqyfHTo2f6x7l_wf-IiA,6971
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=SlnwWIqvJsSp-qt9u5Vk094JCnhfpr5luA6A2h5ZRVc,1336
32
- OTVision/detect/otdet.py,sha256=VsprZPBH2hUna9wYQBAQVM9rM4X9L7cD5aNS9N5RFr0,4485
31
+ OTVision/detect/detected_frame_buffer.py,sha256=fLI87CuWucnNuRmOuQQWZBa_7XMGpJOWE9gvack8ZfM,1332
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
36
  OTVision/detect/video_input_source.py,sha256=AoFn4OJvRYSZ63qEhZ_gk0ptPFBTZc37uaptFyPoZNc,8557
37
- OTVision/detect/yolo.py,sha256=h4RTUP5RYTKe4QNy19D_8A6MeNv3lVTDRhShTnOX_as,9845
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=hWrK3Hh62Opm8t4uWASMw1MTSp1xtZYcE9cQiAfRzGs,859
44
- OTVision/domain/detection.py,sha256=FGI1R8cgTfgL3WOnvsFNMIV2aU7Zu2W4q75T-86yxyg,343
45
- OTVision/domain/frame.py,sha256=SKJlo_Gd9kRjD7JJR6GetBqiiJ7b1_Vn4KH4V6Q1iVA,751
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=2mMAr6_V9rCzWS0MLbOLtUcj44oGY1fsBEfzZgU4Eww,5962
46
46
  OTVision/domain/input_source_detect.py,sha256=9DzkTg5dh7_KmxE9oxdmxrcTYhvZY8hHLZwhrh7Gz2o,1245
47
- OTVision/domain/object_detection.py,sha256=gJ5Jd5uWGSjdIue-XKddb9krFynLZ59_XctMY4HE1ww,1288
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=BfErHX9_55unbNqVFksDku8uAjKjH5hTfdfmhXFIC3A,5060
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=VqI8K_0DbeKgOX1d1D25b0jcn9zIvgVDlzLrfEpUEqM,10992
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=Wuz0pcgaf7P1a3EOipzDTAlCIYULC_f9DbncfKdnc50,6761
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=0fj5KtSqPtlzvttyQ-tizPF9w6gwVwHSpupr5ONj8cU,3092
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=kb8CmzwyBipDaPrXdLg2g2rTZGalTC6uAvGbGQwmdVI,6553
73
- OTVision/track/tracker/tracker_plugin_iou.py,sha256=aMZoKptwG9_TPmlohnT8VVNlbmoVODPf1GLxaINi-ZE,7235
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.2.dist-info/METADATA,sha256=8-TR4TiioaiSZsl6UjTD1mDHflVsjpg7gAvYjHCT9Oc,2282
87
- otvision-0.6.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
88
- otvision-0.6.2.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
89
- otvision-0.6.2.dist-info/RECORD,,
84
+ otvision-0.6.3.dist-info/METADATA,sha256=FNw2TfxIaYGZoPrjXw5XA0DHdnBZYAR2xwd_qjMzn5o,2361
85
+ otvision-0.6.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
86
+ otvision-0.6.3.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
87
+ otvision-0.6.3.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
- }
@@ -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