@camera.ui/camera-ui-coreml 0.0.22 → 0.0.23
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.
- package/bundle.zip +0 -0
- package/package.json +15 -13
- package/CHANGELOG.md +0 -8
- package/CONTRIBUTING.md +0 -1
- package/LICENSE.md +0 -22
- package/README.md +0 -1
- package/config.schema.json +0 -99
- package/requirements.txt +0 -5
- package/src/camera_detector.py +0 -272
- package/src/detector_defaults.py +0 -86
- package/src/main.py +0 -166
- package/src/object_detector.py +0 -228
- package/src/plugin_typings.py +0 -10
- package/src/utils.py +0 -44
package/bundle.zip
ADDED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"displayName": "CoreML",
|
|
3
3
|
"name": "@camera.ui/camera-ui-coreml",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.23",
|
|
5
5
|
"description": "camera.ui coreml plugin",
|
|
6
6
|
"author": "seydx (https://github.com/seydx/camera.ui)",
|
|
7
|
-
"main": "./
|
|
8
|
-
"type": "
|
|
7
|
+
"main": "./dist/main.py",
|
|
8
|
+
"type": "commonjs",
|
|
9
9
|
"scripts": {
|
|
10
|
+
"bundle": "npm run format && npm run lint && cui bundle",
|
|
10
11
|
"format": "ruff format",
|
|
11
12
|
"install-updates": "npm i --save",
|
|
12
|
-
"lint": "ruff check
|
|
13
|
-
"
|
|
14
|
-
"prepublishOnly": "
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
13
|
+
"lint": "ruff check",
|
|
14
|
+
"lint:fix": "ruff check --fix",
|
|
15
|
+
"prepublishOnly": "node -e \"if(!process.env.SAFE_PUBLISH){console.error('Error: Please use @camera.ui/cli to publish the plugin:\\n npm run publish:alpha\\n npm run publish:beta\\n npm run publish:latest\\n');process.exit(1)}\"",
|
|
16
|
+
"publish:alpha": "cui publish --alpha",
|
|
17
|
+
"publish:beta": "cui publish --beta",
|
|
18
|
+
"publish:latest": "cui publish --latest",
|
|
19
|
+
"update": "updates --update ./"
|
|
19
20
|
},
|
|
20
21
|
"bugs": {
|
|
21
22
|
"url": "https://github.com/seydx/camera.ui/issues"
|
|
22
23
|
},
|
|
23
24
|
"engines": {
|
|
24
25
|
"camera.ui": ">=0.0.34-alpha.2",
|
|
25
|
-
"node": ">=20.
|
|
26
|
+
"node": ">=20.18.0"
|
|
26
27
|
},
|
|
27
28
|
"homepage": "https://github.com/seydx/camera.ui#readme",
|
|
28
29
|
"keywords": [
|
|
@@ -40,6 +41,7 @@
|
|
|
40
41
|
"camera.ui": {
|
|
41
42
|
"pythonVersion": "3.11",
|
|
42
43
|
"extension": "objectDetection",
|
|
43
|
-
"dependencies": []
|
|
44
|
+
"dependencies": [],
|
|
45
|
+
"bundled": true
|
|
44
46
|
}
|
|
45
|
-
}
|
|
47
|
+
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
All notable changes to this project will be documented in this file.
|
|
2
|
-
|
|
3
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
4
|
-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
|
-
|
|
6
|
-
## [X.X.X] - ???
|
|
7
|
-
|
|
8
|
-
- Initial Release
|
package/CONTRIBUTING.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
# Contributing
|
package/LICENSE.md
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2023-2024 seydx <dev@seydx.com>
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
-
a copy of this software and associated documentation files (the
|
|
7
|
-
"Software"), to deal in the Software without restriction, including
|
|
8
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
-
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
-
the following conditions:
|
|
12
|
-
|
|
13
|
-
The above copyright notice and this permission notice shall be
|
|
14
|
-
included in all copies or substantial portions of the Software.
|
|
15
|
-
|
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
# @camera.ui/camera-ui-coreml
|
package/config.schema.json
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"schema": {
|
|
3
|
-
"items": {
|
|
4
|
-
"type": "object",
|
|
5
|
-
"title": "Remove Models",
|
|
6
|
-
"opened": true,
|
|
7
|
-
"properties": {
|
|
8
|
-
"model": {
|
|
9
|
-
"type": "string",
|
|
10
|
-
"title": "Model",
|
|
11
|
-
"description": "Model to use for object detection",
|
|
12
|
-
"required": true,
|
|
13
|
-
"store": false,
|
|
14
|
-
"defaultValue": "yolov9m_320 - FP16",
|
|
15
|
-
"enum": [
|
|
16
|
-
"yolo3-tinyu - INT8",
|
|
17
|
-
"yolo3-tinyu_320 - INT8",
|
|
18
|
-
"yolo3-tinyu - FP16",
|
|
19
|
-
"yolo3-tinyu_320 - FP16",
|
|
20
|
-
"yolov5nu - INT8",
|
|
21
|
-
"yolov5nu_320 - INT8",
|
|
22
|
-
"yolov5mu - INT8",
|
|
23
|
-
"yolov5mu_320 - INT8",
|
|
24
|
-
"yolov5nu - FP16",
|
|
25
|
-
"yolov5nu_320 - FP16",
|
|
26
|
-
"yolov5mu - FP16",
|
|
27
|
-
"yolov5mu_320 - FP16",
|
|
28
|
-
"yolov8n - INT8",
|
|
29
|
-
"yolov8n_320 - INT8",
|
|
30
|
-
"yolov8s - INT8",
|
|
31
|
-
"yolov8s_320 - INT8",
|
|
32
|
-
"yolov8m - INT8",
|
|
33
|
-
"yolov8m_320 - INT8",
|
|
34
|
-
"yolov8n - FP16",
|
|
35
|
-
"yolov8n_320 - FP16",
|
|
36
|
-
"yolov8s - FP16",
|
|
37
|
-
"yolov8s_320 - FP16",
|
|
38
|
-
"yolov8m - FP16",
|
|
39
|
-
"yolov8m_320 - FP16",
|
|
40
|
-
"yolov9t - INT8",
|
|
41
|
-
"yolov9t_320 - INT8",
|
|
42
|
-
"yolov9s - INT8",
|
|
43
|
-
"yolov9s_320 - INT8",
|
|
44
|
-
"yolov9m - INT8",
|
|
45
|
-
"yolov9m_320 - INT8",
|
|
46
|
-
"yolov9c - INT8",
|
|
47
|
-
"yolov9c_320 - INT8",
|
|
48
|
-
"yolov9t - FP16",
|
|
49
|
-
"yolov9t_320 - FP16",
|
|
50
|
-
"yolov9s - FP16",
|
|
51
|
-
"yolov9s_320 - FP16",
|
|
52
|
-
"yolov9m - FP16",
|
|
53
|
-
"yolov9m_320 - FP16",
|
|
54
|
-
"yolov9c - FP16",
|
|
55
|
-
"yolov9c_320 - FP16",
|
|
56
|
-
"yolov10n - INT8",
|
|
57
|
-
"yolov10n_320 - INT8",
|
|
58
|
-
"yolov10s - INT8",
|
|
59
|
-
"yolov10s_320 - INT8",
|
|
60
|
-
"yolov10m - INT8",
|
|
61
|
-
"yolov10m_320 - INT8",
|
|
62
|
-
"yolov10l - INT8",
|
|
63
|
-
"yolov10l_320 - INT8",
|
|
64
|
-
"yolov10n - FP16",
|
|
65
|
-
"yolov10n_320 - FP16",
|
|
66
|
-
"yolov10s - FP16",
|
|
67
|
-
"yolov10s_320 - FP16",
|
|
68
|
-
"yolov10m - FP16",
|
|
69
|
-
"yolov10m_320 - FP16",
|
|
70
|
-
"yolov10l - FP16",
|
|
71
|
-
"yolov10l_320 - FP16",
|
|
72
|
-
"yolov11n - INT8",
|
|
73
|
-
"yolov11n_320 - INT8",
|
|
74
|
-
"yolov11s - INT8",
|
|
75
|
-
"yolov11s_320 - INT8",
|
|
76
|
-
"yolov11m - INT8",
|
|
77
|
-
"yolov11m_320 - INT8",
|
|
78
|
-
"yolov11l - INT8",
|
|
79
|
-
"yolov11l_320 - INT8",
|
|
80
|
-
"yolov11n - FP16",
|
|
81
|
-
"yolov11n_320 - FP16",
|
|
82
|
-
"yolov11s - FP16",
|
|
83
|
-
"yolov11s_320 - FP16",
|
|
84
|
-
"yolov11m - FP16",
|
|
85
|
-
"yolov11m_320 - FP16",
|
|
86
|
-
"yolov11l - FP16",
|
|
87
|
-
"yolov11l_320 - FP16"
|
|
88
|
-
]
|
|
89
|
-
}
|
|
90
|
-
},
|
|
91
|
-
"buttons": [
|
|
92
|
-
{
|
|
93
|
-
"label": "Remove",
|
|
94
|
-
"onSubmit": "onRemove"
|
|
95
|
-
}
|
|
96
|
-
]
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
package/requirements.txt
DELETED
package/src/camera_detector.py
DELETED
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
# camera_detector.py
|
|
2
|
-
import asyncio
|
|
3
|
-
from typing import Optional, cast
|
|
4
|
-
from uuid import uuid4
|
|
5
|
-
|
|
6
|
-
from camera_ui_python_types import (
|
|
7
|
-
CameraDevice,
|
|
8
|
-
CameraPropertyObservableObject,
|
|
9
|
-
CameraStorage,
|
|
10
|
-
Detection,
|
|
11
|
-
LoggerService,
|
|
12
|
-
ObjectClass,
|
|
13
|
-
PluginAPI,
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
from detector_defaults import available_models, default_model
|
|
17
|
-
from object_detector import ObjectDetector
|
|
18
|
-
from plugin_typings import CameraStorageValues
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class CameraDetector:
|
|
22
|
-
def __init__(
|
|
23
|
-
self,
|
|
24
|
-
camera: CameraDevice,
|
|
25
|
-
api: PluginAPI,
|
|
26
|
-
logger: LoggerService,
|
|
27
|
-
) -> None:
|
|
28
|
-
super().__init__()
|
|
29
|
-
|
|
30
|
-
self.camera = camera
|
|
31
|
-
self.api = api
|
|
32
|
-
self.camera_logger = camera.logger
|
|
33
|
-
|
|
34
|
-
self.object_detector = ObjectDetector(self.api, self.camera_logger)
|
|
35
|
-
|
|
36
|
-
self.started = False
|
|
37
|
-
self.closed = False
|
|
38
|
-
self.restarting = False
|
|
39
|
-
|
|
40
|
-
self.initialized = False
|
|
41
|
-
|
|
42
|
-
self.current_generator_type: Optional[bool] = None # True for extended, False for normal
|
|
43
|
-
self.detection_task: Optional[asyncio.Task[None]] = None
|
|
44
|
-
|
|
45
|
-
# Keep track of camera properties
|
|
46
|
-
self.connected = False
|
|
47
|
-
self.has_motion_detector = False
|
|
48
|
-
self.extended_motion_detector = False
|
|
49
|
-
|
|
50
|
-
# Debounce task
|
|
51
|
-
self.debounce_task: Optional[asyncio.Task[None]] = None
|
|
52
|
-
|
|
53
|
-
self.camera_storage: CameraStorage[CameraStorageValues] = self.__create_camera_storage()
|
|
54
|
-
|
|
55
|
-
async def initialize(self) -> None:
|
|
56
|
-
if self.initialized:
|
|
57
|
-
return
|
|
58
|
-
|
|
59
|
-
self.initialized = True
|
|
60
|
-
|
|
61
|
-
# Initialize object detector
|
|
62
|
-
try:
|
|
63
|
-
model_name = self.camera_storage.values["model"]
|
|
64
|
-
self.camera_logger.log(f"Initialising Object Detection with model: {model_name}")
|
|
65
|
-
await self.object_detector.initialize(model_name)
|
|
66
|
-
except Exception as error:
|
|
67
|
-
self.camera_logger.error("Error initializing Object Detection", error)
|
|
68
|
-
return
|
|
69
|
-
|
|
70
|
-
if self.closed:
|
|
71
|
-
return
|
|
72
|
-
|
|
73
|
-
# Initialize property values
|
|
74
|
-
self.connected = self.camera.connected
|
|
75
|
-
self.has_motion_detector = self.camera.has_motion_detector
|
|
76
|
-
self.extended_motion_detector = self.camera.extended_motion_detector
|
|
77
|
-
|
|
78
|
-
# Subscribe to changes
|
|
79
|
-
self.camera.on_connected.subscribe(self.on_camera_connected)
|
|
80
|
-
self.camera.on_property_change("hasMotionDetector").subscribe(self.on_has_motion_detector_changed)
|
|
81
|
-
self.camera.on_property_change("extendedMotionDetector").subscribe(
|
|
82
|
-
self.on_extended_motion_detector_changed
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
# Start detection if initial conditions are met
|
|
86
|
-
await self.check_and_start_or_stop_detection()
|
|
87
|
-
|
|
88
|
-
def schedule_check(self):
|
|
89
|
-
if self.debounce_task and not self.debounce_task.done():
|
|
90
|
-
return # Debounce task already scheduled
|
|
91
|
-
|
|
92
|
-
self.debounce_task = asyncio.create_task(self.debounce_check())
|
|
93
|
-
|
|
94
|
-
async def debounce_check(self):
|
|
95
|
-
await asyncio.sleep(0.1) # Debounce delay
|
|
96
|
-
await self.check_and_start_or_stop_detection()
|
|
97
|
-
|
|
98
|
-
def on_camera_connected(self, connected: bool) -> None:
|
|
99
|
-
self.connected = connected
|
|
100
|
-
self.schedule_check()
|
|
101
|
-
|
|
102
|
-
def on_has_motion_detector_changed(self, state: CameraPropertyObservableObject) -> None:
|
|
103
|
-
self.has_motion_detector: bool = state["new_state"]
|
|
104
|
-
self.schedule_check()
|
|
105
|
-
|
|
106
|
-
def on_extended_motion_detector_changed(self, state: CameraPropertyObservableObject) -> None:
|
|
107
|
-
self.extended_motion_detector: bool = state["new_state"]
|
|
108
|
-
self.schedule_check()
|
|
109
|
-
|
|
110
|
-
async def check_and_start_or_stop_detection(self):
|
|
111
|
-
if self.connected and self.has_motion_detector:
|
|
112
|
-
if not self.started:
|
|
113
|
-
await self.start()
|
|
114
|
-
else:
|
|
115
|
-
# Restart detection if the generator type has changed
|
|
116
|
-
if self.current_generator_type != self.extended_motion_detector:
|
|
117
|
-
self.close()
|
|
118
|
-
await self.start()
|
|
119
|
-
else:
|
|
120
|
-
if self.started:
|
|
121
|
-
self.close()
|
|
122
|
-
|
|
123
|
-
async def start(self):
|
|
124
|
-
if not self.started:
|
|
125
|
-
self.started = True
|
|
126
|
-
self.closed = False
|
|
127
|
-
|
|
128
|
-
if self.extended_motion_detector:
|
|
129
|
-
self.camera_logger.log("Starting Object Detection with Motion Frames")
|
|
130
|
-
else:
|
|
131
|
-
self.camera_logger.log("Starting Object Detection")
|
|
132
|
-
|
|
133
|
-
self.detection_task = asyncio.create_task(self.__start_detection())
|
|
134
|
-
|
|
135
|
-
def close(self):
|
|
136
|
-
if not self.closed:
|
|
137
|
-
self.started = False
|
|
138
|
-
self.closed = True
|
|
139
|
-
|
|
140
|
-
self.camera_logger.log("Stopping Object Detection")
|
|
141
|
-
|
|
142
|
-
if self.detection_task:
|
|
143
|
-
self.detection_task.cancel()
|
|
144
|
-
self.detection_task = None
|
|
145
|
-
|
|
146
|
-
if self.debounce_task:
|
|
147
|
-
self.debounce_task.cancel()
|
|
148
|
-
self.debounce_task = None
|
|
149
|
-
|
|
150
|
-
self.object_detector.close()
|
|
151
|
-
|
|
152
|
-
async def __start_detection(self) -> None:
|
|
153
|
-
try:
|
|
154
|
-
await self.__detect()
|
|
155
|
-
except asyncio.CancelledError:
|
|
156
|
-
pass
|
|
157
|
-
except Exception as error:
|
|
158
|
-
self.camera_logger.error("Error generating frames", error)
|
|
159
|
-
await self.__restart()
|
|
160
|
-
|
|
161
|
-
async def __restart(self) -> None:
|
|
162
|
-
if self.restarting or self.closed:
|
|
163
|
-
return
|
|
164
|
-
|
|
165
|
-
self.restarting = True
|
|
166
|
-
|
|
167
|
-
self.camera_logger.log("Restarting Object Detection in 2s...")
|
|
168
|
-
await asyncio.sleep(2)
|
|
169
|
-
|
|
170
|
-
self.restarting = False
|
|
171
|
-
|
|
172
|
-
if self.connected and not self.closed and self.has_motion_detector:
|
|
173
|
-
await self.__start_detection()
|
|
174
|
-
|
|
175
|
-
async def __detect(self):
|
|
176
|
-
# Choose the generator based on extendedMotionDetector
|
|
177
|
-
if self.extended_motion_detector:
|
|
178
|
-
generator = self.camera.get_motion_frames()
|
|
179
|
-
else:
|
|
180
|
-
generator = self.camera.get_frames()
|
|
181
|
-
|
|
182
|
-
self.current_generator_type = self.extended_motion_detector
|
|
183
|
-
|
|
184
|
-
async for frame in generator:
|
|
185
|
-
if self.closed:
|
|
186
|
-
break
|
|
187
|
-
|
|
188
|
-
# Check if properties have changed
|
|
189
|
-
if (
|
|
190
|
-
self.connected != self.camera.connected
|
|
191
|
-
or self.has_motion_detector != self.camera.has_motion_detector
|
|
192
|
-
or self.extended_motion_detector != self.camera.extended_motion_detector
|
|
193
|
-
):
|
|
194
|
-
await self.__restart()
|
|
195
|
-
break
|
|
196
|
-
|
|
197
|
-
frame_image = await frame.to_image(
|
|
198
|
-
{
|
|
199
|
-
"format": {"to": "rgb"},
|
|
200
|
-
"resize": {
|
|
201
|
-
"width": self.object_detector.input_width,
|
|
202
|
-
"height": self.object_detector.input_height,
|
|
203
|
-
},
|
|
204
|
-
}
|
|
205
|
-
)
|
|
206
|
-
|
|
207
|
-
detections: list[Detection] = []
|
|
208
|
-
|
|
209
|
-
class_ids, scores, boxes = await self.object_detector.detect(frame_image["image"])
|
|
210
|
-
|
|
211
|
-
for class_id, score, box in zip(class_ids, scores, boxes):
|
|
212
|
-
x1, y1, x2, y2 = box
|
|
213
|
-
|
|
214
|
-
detection: Detection = {
|
|
215
|
-
"id": str(uuid4()),
|
|
216
|
-
"label": cast(ObjectClass, self.object_detector.labels[class_id]),
|
|
217
|
-
"confidence": score,
|
|
218
|
-
"boundingBox": (float(x1), float(y1), float(x2), float(y2)),
|
|
219
|
-
"inputWidth": self.object_detector.input_width,
|
|
220
|
-
"inputHeight": self.object_detector.input_height,
|
|
221
|
-
"origWidth": frame.metadata["origWidth"],
|
|
222
|
-
"origHeight": frame.metadata["origHeight"],
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
detections.append(detection)
|
|
226
|
-
|
|
227
|
-
await self.camera.update_state("object", {"detections": detections})
|
|
228
|
-
|
|
229
|
-
async def __default_settings(self) -> None:
|
|
230
|
-
if self.camera_storage.values["model"] != default_model:
|
|
231
|
-
await self.camera_storage.setValue("model", default_model)
|
|
232
|
-
|
|
233
|
-
async def __change_model(self, new_model: str, old_model: str) -> None:
|
|
234
|
-
if self.object_detector.downloading:
|
|
235
|
-
self.camera_logger.log("Model is currently downloading, please wait")
|
|
236
|
-
await self.camera_storage.setValue("model", old_model)
|
|
237
|
-
return
|
|
238
|
-
|
|
239
|
-
self.camera_logger.log(f"Model changed from {old_model} to {new_model}")
|
|
240
|
-
|
|
241
|
-
await self.object_detector.reset()
|
|
242
|
-
await self.object_detector.initialize(new_model)
|
|
243
|
-
|
|
244
|
-
def __create_camera_storage(self) -> CameraStorage[CameraStorageValues]:
|
|
245
|
-
camera_storage: CameraStorage[CameraStorageValues] = (
|
|
246
|
-
self.api.storage_controller.create_camera_storage(
|
|
247
|
-
self,
|
|
248
|
-
self.camera.id,
|
|
249
|
-
{
|
|
250
|
-
"model": {
|
|
251
|
-
"type": "string",
|
|
252
|
-
"key": "model",
|
|
253
|
-
"title": "Model",
|
|
254
|
-
"description": "Model for object detection",
|
|
255
|
-
"store": True,
|
|
256
|
-
"defaultValue": default_model,
|
|
257
|
-
"enum": available_models,
|
|
258
|
-
"required": True,
|
|
259
|
-
"onSet": lambda new, old: self.__change_model(new, old),
|
|
260
|
-
},
|
|
261
|
-
"reset": {
|
|
262
|
-
"type": "button",
|
|
263
|
-
"title": "Default Settings",
|
|
264
|
-
"key": "default",
|
|
265
|
-
"description": "Reset settings to default",
|
|
266
|
-
"onSet": lambda new, old: self.__default_settings(),
|
|
267
|
-
},
|
|
268
|
-
},
|
|
269
|
-
)
|
|
270
|
-
)
|
|
271
|
-
|
|
272
|
-
return camera_storage
|
package/src/detector_defaults.py
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
available_models = [
|
|
2
|
-
# YOLOv3
|
|
3
|
-
"yolo3-tinyu - INT8",
|
|
4
|
-
"yolo3-tinyu_320 - INT8",
|
|
5
|
-
"yolo3-tinyu - FP16",
|
|
6
|
-
"yolo3-tinyu_320 - FP16",
|
|
7
|
-
# YOLOv5
|
|
8
|
-
"yolov5nu - INT8",
|
|
9
|
-
"yolov5nu_320 - INT8",
|
|
10
|
-
"yolov5mu - INT8",
|
|
11
|
-
"yolov5mu_320 - INT8",
|
|
12
|
-
"yolov5nu - FP16",
|
|
13
|
-
"yolov5nu_320 - FP16",
|
|
14
|
-
"yolov5mu - FP16",
|
|
15
|
-
"yolov5mu_320 - FP16",
|
|
16
|
-
# YOLOv8
|
|
17
|
-
"yolov8n - INT8",
|
|
18
|
-
"yolov8n_320 - INT8",
|
|
19
|
-
"yolov8s - INT8",
|
|
20
|
-
"yolov8s_320 - INT8",
|
|
21
|
-
"yolov8m - INT8",
|
|
22
|
-
"yolov8m_320 - INT8",
|
|
23
|
-
"yolov8n - FP16",
|
|
24
|
-
"yolov8n_320 - FP16",
|
|
25
|
-
"yolov8s - FP16",
|
|
26
|
-
"yolov8s_320 - FP16",
|
|
27
|
-
"yolov8m - FP16",
|
|
28
|
-
"yolov8m_320 - FP16",
|
|
29
|
-
# YOLOv9
|
|
30
|
-
"yolov9t - INT8",
|
|
31
|
-
"yolov9t_320 - INT8",
|
|
32
|
-
"yolov9s - INT8",
|
|
33
|
-
"yolov9s_320 - INT8",
|
|
34
|
-
"yolov9m - INT8",
|
|
35
|
-
"yolov9m_320 - INT8",
|
|
36
|
-
"yolov9c - INT8",
|
|
37
|
-
"yolov9c_320 - INT8",
|
|
38
|
-
"yolov9t - FP16",
|
|
39
|
-
"yolov9t_320 - FP16",
|
|
40
|
-
"yolov9s - FP16",
|
|
41
|
-
"yolov9s_320 - FP16",
|
|
42
|
-
"yolov9m - FP16",
|
|
43
|
-
"yolov9m_320 - FP16",
|
|
44
|
-
"yolov9c - FP16",
|
|
45
|
-
"yolov9c_320 - FP16",
|
|
46
|
-
# YOLOv10
|
|
47
|
-
"yolov10n - INT8",
|
|
48
|
-
"yolov10n_320 - INT8",
|
|
49
|
-
"yolov10s - INT8",
|
|
50
|
-
"yolov10s_320 - INT8",
|
|
51
|
-
"yolov10m - INT8",
|
|
52
|
-
"yolov10m_320 - INT8",
|
|
53
|
-
"yolov10l - INT8",
|
|
54
|
-
"yolov10l_320 - INT8",
|
|
55
|
-
"yolov10n - FP16",
|
|
56
|
-
"yolov10n_320 - FP16",
|
|
57
|
-
"yolov10s - FP16",
|
|
58
|
-
"yolov10s_320 - FP16",
|
|
59
|
-
"yolov10m - FP16",
|
|
60
|
-
"yolov10m_320 - FP16",
|
|
61
|
-
"yolov10l - FP16",
|
|
62
|
-
"yolov10l_320 - FP16",
|
|
63
|
-
# YOLOv11
|
|
64
|
-
"yolov11n - INT8",
|
|
65
|
-
"yolov11n_320 - INT8",
|
|
66
|
-
"yolov11s - INT8",
|
|
67
|
-
"yolov11s_320 - INT8",
|
|
68
|
-
"yolov11m - INT8",
|
|
69
|
-
"yolov11m_320 - INT8",
|
|
70
|
-
"yolov11l - INT8",
|
|
71
|
-
"yolov11l_320 - INT8",
|
|
72
|
-
"yolov11n - FP16",
|
|
73
|
-
"yolov11n_320 - FP16",
|
|
74
|
-
"yolov11s - FP16",
|
|
75
|
-
"yolov11s_320 - FP16",
|
|
76
|
-
"yolov11m - FP16",
|
|
77
|
-
"yolov11m_320 - FP16",
|
|
78
|
-
"yolov11l - FP16",
|
|
79
|
-
"yolov11l_320 - FP16",
|
|
80
|
-
]
|
|
81
|
-
|
|
82
|
-
available_precision = ["INT8", "FP16"]
|
|
83
|
-
|
|
84
|
-
default_model = "yolov9m_320 - FP16"
|
|
85
|
-
|
|
86
|
-
model_version = "v1"
|
package/src/main.py
DELETED
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
# main.py
|
|
2
|
-
import asyncio
|
|
3
|
-
import os
|
|
4
|
-
from typing import Any, Union
|
|
5
|
-
|
|
6
|
-
from camera_ui_python_types import (
|
|
7
|
-
CameraDevice,
|
|
8
|
-
CameraExtension,
|
|
9
|
-
FormSubmitResponse,
|
|
10
|
-
ImageMetadata,
|
|
11
|
-
LoggerService,
|
|
12
|
-
ObjectDetectionPlugin,
|
|
13
|
-
ObjectDetectionPluginResponse,
|
|
14
|
-
PluginAPI,
|
|
15
|
-
RootSchema,
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
from camera_detector import CameraDetector
|
|
19
|
-
from detector_defaults import available_models, default_model, model_version
|
|
20
|
-
from object_detector import ObjectDetector
|
|
21
|
-
from plugin_typings import FormPayload
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class CoreML(ObjectDetectionPlugin):
|
|
25
|
-
def __init__(self, logger: LoggerService, api: PluginAPI):
|
|
26
|
-
self.api = api
|
|
27
|
-
self.logger = logger
|
|
28
|
-
self.camera_devices: dict[str, CameraDevice] = {}
|
|
29
|
-
self.detectors: dict[str, CameraDetector] = {}
|
|
30
|
-
|
|
31
|
-
self.api.on("finishLaunching", self.start)
|
|
32
|
-
self.api.on("shutdown", self.stop)
|
|
33
|
-
|
|
34
|
-
self.api.device_manager.on("cameraSelected", self.camera_selected)
|
|
35
|
-
self.api.device_manager.on("cameraDeselected", self.camera_deselected)
|
|
36
|
-
|
|
37
|
-
self.object_detectors: dict[str, ObjectDetector] = {}
|
|
38
|
-
|
|
39
|
-
async def camera_selected(self, camera: CameraDevice, extension: CameraExtension) -> None:
|
|
40
|
-
self.logger.log(f"Camera added: {camera.name}")
|
|
41
|
-
self.camera_devices[camera.id] = camera
|
|
42
|
-
await self.create_detector(camera)
|
|
43
|
-
|
|
44
|
-
async def camera_deselected(self, id: str, extension: CameraExtension) -> None:
|
|
45
|
-
camera = self.camera_devices.get(id)
|
|
46
|
-
|
|
47
|
-
if camera:
|
|
48
|
-
self.logger.log(f"Camera removed: {camera.name}")
|
|
49
|
-
self.remove_detector(camera)
|
|
50
|
-
del self.camera_devices[id]
|
|
51
|
-
|
|
52
|
-
async def start(self) -> None:
|
|
53
|
-
self.logger.log("Plugin started")
|
|
54
|
-
await asyncio.gather(*[self.create_detector(camera) for camera in self.camera_devices.values()])
|
|
55
|
-
|
|
56
|
-
async def stop(self) -> None:
|
|
57
|
-
self.logger.log("Plugin stopped")
|
|
58
|
-
|
|
59
|
-
detectors_copy = list(self.detectors.values())
|
|
60
|
-
|
|
61
|
-
for detector in detectors_copy:
|
|
62
|
-
detector.close()
|
|
63
|
-
del self.detectors[detector.camera.id]
|
|
64
|
-
|
|
65
|
-
async def onFormSubmit(self, action_id: str, payload: FormPayload) -> Union[FormSubmitResponse, None]:
|
|
66
|
-
if action_id == "onRemove":
|
|
67
|
-
response: FormSubmitResponse = {
|
|
68
|
-
"schema": {
|
|
69
|
-
"config": {
|
|
70
|
-
"type": "object",
|
|
71
|
-
"title": "Confirm",
|
|
72
|
-
"required": True,
|
|
73
|
-
"opened": True,
|
|
74
|
-
"properties": {
|
|
75
|
-
"confirm": {
|
|
76
|
-
"type": "string",
|
|
77
|
-
"description": "Please type 'REMOVE' to confirm",
|
|
78
|
-
"required": True,
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
"buttons": [
|
|
82
|
-
{
|
|
83
|
-
"label": "Remove",
|
|
84
|
-
"onSubmit": "onConfirm",
|
|
85
|
-
},
|
|
86
|
-
],
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return response
|
|
92
|
-
elif action_id == "onConfirm":
|
|
93
|
-
if payload.get("confirm") == "REMOVE":
|
|
94
|
-
model, precision = payload["model"].split(" - ")
|
|
95
|
-
filename = f"coreml_{model}/{precision}"
|
|
96
|
-
|
|
97
|
-
model_storage = os.path.join(f"{self.api.storage_path}/models/{model_version}")
|
|
98
|
-
dir = os.path.join(model_storage, filename)
|
|
99
|
-
|
|
100
|
-
if os.path.exists(dir):
|
|
101
|
-
self.logger.attention(f"Removing model: {model} - {precision}")
|
|
102
|
-
os.rmdir(dir)
|
|
103
|
-
else:
|
|
104
|
-
self.logger.debug(f"Model not found: {model} - {precision}, skipping removal")
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
"toast": {
|
|
108
|
-
"message": "All models removed!",
|
|
109
|
-
"type": "success",
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
def configureCameras(self, cameras: list[CameraDevice]) -> None:
|
|
114
|
-
for camera in cameras:
|
|
115
|
-
self.camera_devices[camera.id] = camera
|
|
116
|
-
|
|
117
|
-
async def create_detector(self, camera: CameraDevice) -> None:
|
|
118
|
-
detector = CameraDetector(camera, self.api, self.logger)
|
|
119
|
-
self.detectors[camera.id] = detector
|
|
120
|
-
await detector.initialize()
|
|
121
|
-
|
|
122
|
-
def remove_detector(self, camera: CameraDevice) -> None:
|
|
123
|
-
detector = self.detectors[camera.id]
|
|
124
|
-
|
|
125
|
-
if detector:
|
|
126
|
-
detector.close()
|
|
127
|
-
del self.detectors[camera.id]
|
|
128
|
-
|
|
129
|
-
def interfaceSchema(self) -> RootSchema | None:
|
|
130
|
-
return {
|
|
131
|
-
"schema": {
|
|
132
|
-
"models": {
|
|
133
|
-
"type": "string",
|
|
134
|
-
"title": "Model",
|
|
135
|
-
"description": "Model for object detection",
|
|
136
|
-
"required": True,
|
|
137
|
-
"defaultValue": default_model,
|
|
138
|
-
"enum": available_models,
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
async def detectObjects(
|
|
144
|
-
self, image_path: str, metadata: ImageMetadata, config: dict[str, Any]
|
|
145
|
-
) -> ObjectDetectionPluginResponse:
|
|
146
|
-
model_name: str = config.get("models", default_model)
|
|
147
|
-
|
|
148
|
-
object_detector = self.object_detectors.get(model_name)
|
|
149
|
-
|
|
150
|
-
if not object_detector:
|
|
151
|
-
object_detector = ObjectDetector(self.api, self.logger)
|
|
152
|
-
self.object_detectors[model_name] = object_detector
|
|
153
|
-
|
|
154
|
-
await object_detector.initialize(model_name)
|
|
155
|
-
|
|
156
|
-
detections = await object_detector.detect_single(image_path, metadata)
|
|
157
|
-
|
|
158
|
-
print("Detections: ", len(detections))
|
|
159
|
-
|
|
160
|
-
return {
|
|
161
|
-
"detections": detections,
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
def __main__():
|
|
166
|
-
return CoreML
|
package/src/object_detector.py
DELETED
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
# object_detector.py
|
|
2
|
-
import asyncio
|
|
3
|
-
import os
|
|
4
|
-
from asyncio import Lock
|
|
5
|
-
from concurrent.futures import ThreadPoolExecutor
|
|
6
|
-
from typing import Any, Optional, cast
|
|
7
|
-
from uuid import uuid4
|
|
8
|
-
|
|
9
|
-
import aiohttp
|
|
10
|
-
import coremltools as ct
|
|
11
|
-
import numpy as np
|
|
12
|
-
from camera_ui_python_types import (
|
|
13
|
-
CameraDevice,
|
|
14
|
-
Detection,
|
|
15
|
-
ImageMetadata,
|
|
16
|
-
LoggerService,
|
|
17
|
-
ObjectClass,
|
|
18
|
-
PluginAPI,
|
|
19
|
-
)
|
|
20
|
-
from PIL import Image
|
|
21
|
-
|
|
22
|
-
from detector_defaults import available_models, available_precision, default_model, model_version
|
|
23
|
-
from utils import parse_labels
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class ObjectDetector:
|
|
27
|
-
def __init__(
|
|
28
|
-
self,
|
|
29
|
-
api: PluginAPI,
|
|
30
|
-
logger: LoggerService,
|
|
31
|
-
camera: Optional[CameraDevice] = None,
|
|
32
|
-
):
|
|
33
|
-
self.api = api
|
|
34
|
-
self.logger = logger
|
|
35
|
-
|
|
36
|
-
self.model_path = os.path.join(f"{self.api.storage_path}/models/{model_version}")
|
|
37
|
-
|
|
38
|
-
self.initialized = False
|
|
39
|
-
self.minThreshold = 0.2
|
|
40
|
-
self.executor = ThreadPoolExecutor(max_workers=8)
|
|
41
|
-
|
|
42
|
-
self.selected_model = None
|
|
43
|
-
self.selected_precision = None
|
|
44
|
-
|
|
45
|
-
self.closed = False
|
|
46
|
-
self.__downloading = False
|
|
47
|
-
|
|
48
|
-
self.lock = Lock()
|
|
49
|
-
|
|
50
|
-
@property
|
|
51
|
-
def downloading(self) -> bool:
|
|
52
|
-
return self.__downloading
|
|
53
|
-
|
|
54
|
-
@property
|
|
55
|
-
def input_width(self) -> int:
|
|
56
|
-
return self.input_size[0]
|
|
57
|
-
|
|
58
|
-
@property
|
|
59
|
-
def input_height(self) -> int:
|
|
60
|
-
return self.input_size[1]
|
|
61
|
-
|
|
62
|
-
async def initialize(self, model_name: str) -> None:
|
|
63
|
-
async with self.lock:
|
|
64
|
-
if self.__downloading:
|
|
65
|
-
raise Exception("Already downloading a model")
|
|
66
|
-
|
|
67
|
-
if not self.initialized:
|
|
68
|
-
self.initialized = True
|
|
69
|
-
self.__downloading = True
|
|
70
|
-
|
|
71
|
-
self.selected_model, self.selected_precision = model_name.split(" - ")
|
|
72
|
-
|
|
73
|
-
if self.selected_model not in available_models:
|
|
74
|
-
self.selected_model = default_model.split(" - ")[0]
|
|
75
|
-
|
|
76
|
-
if self.selected_precision not in available_precision:
|
|
77
|
-
self.selected_precision = default_model.split(" - ")[1]
|
|
78
|
-
|
|
79
|
-
yolo_model_name = self.selected_model.split("_")[0]
|
|
80
|
-
ml_package = (
|
|
81
|
-
f"coreml_{self.selected_model}/{self.selected_precision}/{yolo_model_name}.mlpackage"
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
files = [
|
|
85
|
-
f"{ml_package}/Data/com.apple.CoreML/weights/weight.bin",
|
|
86
|
-
f"{ml_package}/Data/com.apple.CoreML/model.mlmodel",
|
|
87
|
-
f"{ml_package}/Manifest.json",
|
|
88
|
-
]
|
|
89
|
-
|
|
90
|
-
for f in files:
|
|
91
|
-
url = f"https://raw.githubusercontent.com/seydx/models/main/models/coreml/{f}"
|
|
92
|
-
await self.__downloadFile(url, f)
|
|
93
|
-
if self.closed:
|
|
94
|
-
return
|
|
95
|
-
|
|
96
|
-
# Proceed with model loading and initialization
|
|
97
|
-
loop = asyncio.get_event_loop()
|
|
98
|
-
self.model = await loop.run_in_executor(
|
|
99
|
-
None, ct.models.MLModel, os.path.join(self.model_path, ml_package)
|
|
100
|
-
)
|
|
101
|
-
self.input_name: str = self.model.get_spec().description.input[0].name # type: ignore
|
|
102
|
-
|
|
103
|
-
modelspec: Any = self.model.get_spec()
|
|
104
|
-
inputdesc = modelspec.description.input[0] # type: ignore
|
|
105
|
-
inputheight: Any = inputdesc.type.imageType.height
|
|
106
|
-
inputwidth: Any = inputdesc.type.imageType.width
|
|
107
|
-
|
|
108
|
-
self.input_size: tuple[int, int] = (inputwidth, inputheight)
|
|
109
|
-
self.labels = parse_labels(modelspec.description.metadata.userDefined) # type: ignore
|
|
110
|
-
|
|
111
|
-
self.__downloading = False
|
|
112
|
-
|
|
113
|
-
async def reset(self) -> None:
|
|
114
|
-
async with self.lock:
|
|
115
|
-
self.initialized = False
|
|
116
|
-
if self.executor:
|
|
117
|
-
self.executor.shutdown(wait=False)
|
|
118
|
-
self.executor = ThreadPoolExecutor(max_workers=8)
|
|
119
|
-
|
|
120
|
-
def close(self) -> None:
|
|
121
|
-
self.closed = True
|
|
122
|
-
asyncio.create_task(self.reset())
|
|
123
|
-
if self.executor:
|
|
124
|
-
self.executor.shutdown(wait=False)
|
|
125
|
-
self.executor = None
|
|
126
|
-
|
|
127
|
-
def cleanup_model(self, model_name: str) -> None:
|
|
128
|
-
model, precision = model_name.split(" - ")
|
|
129
|
-
filename = f"coreml_{model}/{precision}"
|
|
130
|
-
dir = os.path.join(self.model_path, filename)
|
|
131
|
-
|
|
132
|
-
if os.path.exists(dir):
|
|
133
|
-
os.rmdir(dir)
|
|
134
|
-
|
|
135
|
-
async def detect(self, image: Image.Image) -> tuple[list[int], list[float], np.ndarray[Any, Any]]:
|
|
136
|
-
if not self.initialized:
|
|
137
|
-
return [], [], np.array([])
|
|
138
|
-
|
|
139
|
-
out_dict = await asyncio.get_event_loop().run_in_executor(self.executor, self.__model_predict, image)
|
|
140
|
-
|
|
141
|
-
coordinatesList = out_dict["coordinates"]
|
|
142
|
-
confidenceList = out_dict["confidence"]
|
|
143
|
-
|
|
144
|
-
indices = np.argmax(confidenceList, axis=1)
|
|
145
|
-
maxConfidences = confidenceList[np.arange(len(confidenceList)), indices]
|
|
146
|
-
valid = maxConfidences >= self.minThreshold
|
|
147
|
-
|
|
148
|
-
if not np.any(valid):
|
|
149
|
-
return [], [], np.array([])
|
|
150
|
-
|
|
151
|
-
coordinates = coordinatesList[valid]
|
|
152
|
-
class_ids = indices[valid]
|
|
153
|
-
scores = maxConfidences[valid]
|
|
154
|
-
|
|
155
|
-
x_center = self.__torelative(coordinates[:, 0], self.input_width)
|
|
156
|
-
y_center = self.__torelative(coordinates[:, 1], self.input_height)
|
|
157
|
-
width = self.__torelative(coordinates[:, 2], self.input_width)
|
|
158
|
-
height = self.__torelative(coordinates[:, 3], self.input_height)
|
|
159
|
-
|
|
160
|
-
xmin = np.maximum(0, x_center - width / 2)
|
|
161
|
-
ymin = np.maximum(0, y_center - height / 2)
|
|
162
|
-
xmax = np.minimum(self.input_width, x_center + width / 2)
|
|
163
|
-
ymax = np.minimum(self.input_height, y_center + height / 2)
|
|
164
|
-
|
|
165
|
-
boxes = np.vstack([xmin, ymin, xmax, ymax]).T
|
|
166
|
-
|
|
167
|
-
return class_ids.tolist(), scores.tolist(), boxes
|
|
168
|
-
|
|
169
|
-
async def detect_single(self, image_path: str, metadata: ImageMetadata) -> list[Detection]:
|
|
170
|
-
image = Image.open(image_path)
|
|
171
|
-
image = image.convert("RGB").resize((self.input_width, self.input_height))
|
|
172
|
-
|
|
173
|
-
detections: list[Detection] = []
|
|
174
|
-
|
|
175
|
-
class_ids, scores, boxes = await self.detect(image)
|
|
176
|
-
|
|
177
|
-
for class_id, score, box in zip(class_ids, scores, boxes):
|
|
178
|
-
x1, y1, x2, y2 = box
|
|
179
|
-
|
|
180
|
-
detection: Detection = {
|
|
181
|
-
"id": str(uuid4()),
|
|
182
|
-
"label": cast(ObjectClass, self.labels[class_id]),
|
|
183
|
-
"confidence": score,
|
|
184
|
-
"boundingBox": (float(x1), float(y1), float(x2), float(y2)),
|
|
185
|
-
"inputWidth": self.input_width,
|
|
186
|
-
"inputHeight": self.input_height,
|
|
187
|
-
"origWidth": metadata["width"],
|
|
188
|
-
"origHeight": metadata["height"],
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
detections.append(detection)
|
|
192
|
-
|
|
193
|
-
image.close()
|
|
194
|
-
|
|
195
|
-
return detections
|
|
196
|
-
|
|
197
|
-
def __torelative(self, values: int, size: int) -> int:
|
|
198
|
-
return values * size
|
|
199
|
-
|
|
200
|
-
def __model_predict(self, image: Image.Image) -> Any:
|
|
201
|
-
return self.model.predict({self.input_name: image}) # type: ignore
|
|
202
|
-
|
|
203
|
-
async def __downloadFile(self, url: str, filename: str) -> None:
|
|
204
|
-
fullpath = os.path.join(self.model_path, filename)
|
|
205
|
-
|
|
206
|
-
if os.path.isfile(fullpath):
|
|
207
|
-
return
|
|
208
|
-
|
|
209
|
-
tmp = fullpath + ".tmp"
|
|
210
|
-
|
|
211
|
-
os.makedirs(os.path.dirname(fullpath), exist_ok=True)
|
|
212
|
-
|
|
213
|
-
self.logger.debug("Downloading", url)
|
|
214
|
-
|
|
215
|
-
async with aiohttp.ClientSession() as session, session.get(url) as response:
|
|
216
|
-
if response.status < 200 or response.status >= 300:
|
|
217
|
-
raise Exception(f"Error downloading {url}: {response.status}")
|
|
218
|
-
|
|
219
|
-
read = 0
|
|
220
|
-
|
|
221
|
-
with open(tmp, "wb") as f:
|
|
222
|
-
async for chunk in response.content.iter_chunked(1024 * 1024):
|
|
223
|
-
if chunk:
|
|
224
|
-
read += len(chunk)
|
|
225
|
-
self.logger.debug("Downloaded", read, "Bytes")
|
|
226
|
-
f.write(chunk)
|
|
227
|
-
|
|
228
|
-
os.rename(tmp, fullpath)
|
package/src/plugin_typings.py
DELETED
package/src/utils.py
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import ast
|
|
2
|
-
import re
|
|
3
|
-
from typing import Any
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def parse_label_contents(contents: str) -> dict[int, Any]:
|
|
7
|
-
lines = contents.split(",")
|
|
8
|
-
ret: dict[int, Any] = {}
|
|
9
|
-
|
|
10
|
-
for row_number, content in enumerate(lines):
|
|
11
|
-
pair = re.split(r"[:\s]+", content.strip(), maxsplit=1)
|
|
12
|
-
if len(pair) == 2 and pair[0].strip().isdigit():
|
|
13
|
-
ret[int(pair[0])] = pair[1].strip()
|
|
14
|
-
else:
|
|
15
|
-
ret[row_number] = content.strip()
|
|
16
|
-
|
|
17
|
-
return ret
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def parse_labels(userDefined: dict[str, Any]) -> list[str]:
|
|
21
|
-
yolo = userDefined.get("names") or userDefined.get("yolo.names")
|
|
22
|
-
|
|
23
|
-
if yolo:
|
|
24
|
-
j = ast.literal_eval(yolo)
|
|
25
|
-
ret = []
|
|
26
|
-
for _, v in j.items():
|
|
27
|
-
ret.append(v)
|
|
28
|
-
|
|
29
|
-
return ret
|
|
30
|
-
|
|
31
|
-
classes = userDefined.get("classes")
|
|
32
|
-
|
|
33
|
-
# print("classes", classes)
|
|
34
|
-
|
|
35
|
-
if not classes:
|
|
36
|
-
raise Exception("no classes found in model metadata")
|
|
37
|
-
|
|
38
|
-
cont = parse_label_contents(classes)
|
|
39
|
-
ret: list[str] = []
|
|
40
|
-
|
|
41
|
-
for _, v in cont.items():
|
|
42
|
-
ret.append(v)
|
|
43
|
-
|
|
44
|
-
return ret
|