sciveo 0.1.31__tar.gz → 0.1.33__tar.gz
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.
- {sciveo-0.1.31 → sciveo-0.1.33}/PKG-INFO +1 -1
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/pipeline.py +2 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/base.py +6 -0
- sciveo-0.1.33/sciveo/media/pipelines/processors/image/object_detection.py +186 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/ml/evaluation/object_detection.py +58 -12
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/tools/logger.py +6 -2
- sciveo-0.1.33/sciveo/version.py +2 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo.egg-info/PKG-INFO +1 -1
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo.egg-info/SOURCES.txt +1 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo.egg-info/requires.txt +2 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/setup.py +1 -0
- sciveo-0.1.31/sciveo/version.py +0 -2
- {sciveo-0.1.31 → sciveo-0.1.33}/README.md +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/api/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/api/base.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/api/upload.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/cli.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/common/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/common/configuration.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/common/model.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/common/optimizers.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/common/sampling.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/content/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/content/dataset.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/content/experiment.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/content/project.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/content/runner.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/ml/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/ml/base.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/ml/encoders/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/ml/encoders/base.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/ml/encoders/normalizer.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/ml/nlp/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/ml/nlp/search.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/ml/time_series/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/ml/time_series/dataset.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/ml/time_series/predictor.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/ml/time_series/trainer.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/ml/time_series/window_generator.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/base.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/job_daemon.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/layouts/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/layouts/base.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/postprocessors/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/postprocessors/base.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/postprocessors/default.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/audio/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/audio/audio.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/audio/audio_extractor_process.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/aws.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/file/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/file/archive.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/image/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/image/album.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/image/album_in_image.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/image/depth_esimation.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/image/embeddings.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/image/filters.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/image/generators.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/image/histogram.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/image/mask.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/image/resize.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/image/segmentation.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/image/watermark.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/media_info.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/nlp/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/nlp/address.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/qr.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/sci/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/sci/base.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/sci/dataset.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/sci/time_series/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/sci/time_series/predictor.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/sci/time_series/trainer.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/tpu_base.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/video/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/video/generators.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/video/motion_detection.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/video/resize.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/video/video_album.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/video/video_frames.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/video/video_resample.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/queues.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/server.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/web/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/web/server.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/ml/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/ml/evaluation/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/ml/images/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/ml/images/object_detection.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/ml/images/tools.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/ml/images/transforms.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/ml/nlp/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/monitoring/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/monitoring/monitor.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/monitoring/start.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/network/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/network/camera.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/network/sniffer.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/network/tools.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/tools/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/tools/array.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/tools/aws/__init__.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/tools/aws/priority_queue.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/tools/aws/s3.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/tools/common.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/tools/compress.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/tools/configuration.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/tools/crypto.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/tools/daemon.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/tools/formating.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/tools/hardware.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/tools/http.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/tools/os.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/tools/random.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/tools/remote.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/tools/simple_counter.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/tools/synchronized.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo/tools/timers.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo.egg-info/dependency_links.txt +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo.egg-info/entry_points.txt +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/sciveo.egg-info/top_level.txt +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/setup.cfg +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/test/test_compress.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/test/test_configuration.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/test/test_crypto.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/test/test_monitoring.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/test/test_runner.py +0 -0
- {sciveo-0.1.31 → sciveo-0.1.33}/test/test_sampling.py +0 -0
|
@@ -31,6 +31,7 @@ from sciveo.media.pipelines.processors.image.depth_esimation import *
|
|
|
31
31
|
from sciveo.media.pipelines.processors.image.mask import *
|
|
32
32
|
from sciveo.media.pipelines.processors.image.segmentation import *
|
|
33
33
|
from sciveo.media.pipelines.processors.image.watermark import *
|
|
34
|
+
from sciveo.media.pipelines.processors.image.object_detection import ImageObjectDetectionProcessor
|
|
34
35
|
|
|
35
36
|
from sciveo.media.pipelines.processors.file.archive import *
|
|
36
37
|
|
|
@@ -84,6 +85,7 @@ class MediaPipeline:
|
|
|
84
85
|
"image-segmentation": ImageSegmentation,
|
|
85
86
|
"image-depth-estimation": ImageDepthEstimation,
|
|
86
87
|
"image-embedding": ImageEmbedding,
|
|
88
|
+
"image-object-detection": ImageObjectDetectionProcessor,
|
|
87
89
|
|
|
88
90
|
"sci-timeseries-predictor": TimeSeriesPredictorProcessor,
|
|
89
91
|
"sci-timeseries-trainer": TimeSeriesTrainerProcessor,
|
|
@@ -27,6 +27,9 @@ class BaseProcessor(BaseContentProcessor):
|
|
|
27
27
|
def post_process(self):
|
|
28
28
|
pass
|
|
29
29
|
|
|
30
|
+
def init_run(self):
|
|
31
|
+
pass
|
|
32
|
+
|
|
30
33
|
def run(self, job, input):
|
|
31
34
|
self.job = job
|
|
32
35
|
self.job_id = job["id"]
|
|
@@ -35,6 +38,9 @@ class BaseProcessor(BaseContentProcessor):
|
|
|
35
38
|
return []
|
|
36
39
|
progress_per_media = self.max_progress / max(1, len(input))
|
|
37
40
|
debug("run", job["id"], progress_per_media, "input", input)
|
|
41
|
+
|
|
42
|
+
self.init_run()
|
|
43
|
+
|
|
38
44
|
for i, media in enumerate(input):
|
|
39
45
|
if self.is_processor_run(job, media):
|
|
40
46
|
try:
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Pavlin Georgiev, Softel Labs
|
|
3
|
+
#
|
|
4
|
+
# This is a proprietary file and may not be copied,
|
|
5
|
+
# distributed, or modified without express permission
|
|
6
|
+
# from the owner. For licensing inquiries, please
|
|
7
|
+
# contact pavlin@softel.bg.
|
|
8
|
+
#
|
|
9
|
+
# 2024
|
|
10
|
+
#
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
import cv2
|
|
14
|
+
|
|
15
|
+
from sciveo.tools.logger import *
|
|
16
|
+
from sciveo.tools.simple_counter import Timer
|
|
17
|
+
from sciveo.tools.common import *
|
|
18
|
+
from sciveo.media.pipelines.processors.base import *
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ObjectDetectorBase:
|
|
22
|
+
def __init__(self, model_path, device='cpu', colors=None):
|
|
23
|
+
self.model_path = model_path
|
|
24
|
+
self.device = device
|
|
25
|
+
if colors is None:
|
|
26
|
+
self.colors = [
|
|
27
|
+
(60, 180, 75), # Green
|
|
28
|
+
(255, 255, 255), # White
|
|
29
|
+
(245, 130, 48), # Orange
|
|
30
|
+
(255, 225, 25), # Yellow
|
|
31
|
+
(0, 130, 200), # Blue
|
|
32
|
+
(230, 25, 75), # Red
|
|
33
|
+
(145, 30, 180), # Purple
|
|
34
|
+
(70, 240, 240), # Cyan
|
|
35
|
+
(240, 50, 230), # Magenta
|
|
36
|
+
(210, 245, 60), # Lime
|
|
37
|
+
(250, 190, 212), # Pink
|
|
38
|
+
(0, 128, 128), # Teal
|
|
39
|
+
(220, 190, 255), # Lavender
|
|
40
|
+
(170, 110, 40), # Brown
|
|
41
|
+
(128, 0, 0), # Maroon
|
|
42
|
+
(0, 0, 128), # Navy
|
|
43
|
+
(128, 128, 0), # Olive
|
|
44
|
+
(255, 215, 180), # Peach
|
|
45
|
+
(255, 250, 200), # Ivory
|
|
46
|
+
(170, 255, 195), # Mint
|
|
47
|
+
]
|
|
48
|
+
else:
|
|
49
|
+
self.colors = colors
|
|
50
|
+
|
|
51
|
+
def resize(self, image, h):
|
|
52
|
+
ratio = max(image.shape[0], image.shape[1]) / h
|
|
53
|
+
h, w = int(image.shape[0] / ratio), int(image.shape[1] / ratio)
|
|
54
|
+
return cv2.resize(image, (w, h))
|
|
55
|
+
|
|
56
|
+
def load(self, image_path):
|
|
57
|
+
image = cv2.imread(image_path)
|
|
58
|
+
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
|
59
|
+
return image
|
|
60
|
+
|
|
61
|
+
def read_images(self, images_paths):
|
|
62
|
+
X = []
|
|
63
|
+
for image_path in images_paths:
|
|
64
|
+
image = self.load(image_path)
|
|
65
|
+
image = self.resize(image)
|
|
66
|
+
X.append(image)
|
|
67
|
+
return X
|
|
68
|
+
|
|
69
|
+
def draw_label_inverted(self, frame, label_text, x_min, y_min, color, font=cv2.FONT_HERSHEY_SIMPLEX, font_scale=0.3, font_thickness=1):
|
|
70
|
+
text_size = cv2.getTextSize(label_text, font, font_scale, font_thickness)[0]
|
|
71
|
+
y_offset = text_size[1] + 4
|
|
72
|
+
x_offset = text_size[0] + 4
|
|
73
|
+
if y_min - y_offset >= 0:
|
|
74
|
+
text_background_top_left = (x_min, y_min - y_offset)
|
|
75
|
+
text_background_bottom_right = (x_min + x_offset, y_min)
|
|
76
|
+
else:
|
|
77
|
+
text_background_top_left = (x_min, y_min)
|
|
78
|
+
text_background_bottom_right = (x_min + x_offset, y_min + y_offset)
|
|
79
|
+
|
|
80
|
+
cv2.rectangle(frame, text_background_top_left, text_background_bottom_right, color, cv2.FILLED)
|
|
81
|
+
cv2.putText(frame, label_text, (x_min + 2, text_background_bottom_right[1] - 2), font, font_scale, (0,0,0), font_thickness)
|
|
82
|
+
|
|
83
|
+
def draw_object_rectangle_xyxy(self, frame, box, label, color, alpha=0.2, filled=True):
|
|
84
|
+
(x1, y1, x2, y2) = box
|
|
85
|
+
|
|
86
|
+
if filled:
|
|
87
|
+
rectangle = np.zeros((y2 - y1, x2 - x1, 3), dtype=np.uint8)
|
|
88
|
+
rectangle[:] = color
|
|
89
|
+
frame[y1:y2, x1:x2] = cv2.addWeighted(rectangle, alpha, frame[y1:y2, x1:x2], 1 - alpha, 0)
|
|
90
|
+
|
|
91
|
+
cv2.rectangle(frame, (x1, y1), (x2, y2), color, thickness=1)
|
|
92
|
+
|
|
93
|
+
self.draw_label_inverted(frame, label, x1, y1, color=color)
|
|
94
|
+
|
|
95
|
+
def draw_object_rectangle_xywh(frame, box, label, color, alpha=0.2, filled=True):
|
|
96
|
+
(x, y, w, h) = box
|
|
97
|
+
(x1, y1, x2, y2) = x, y, x + w, y + h
|
|
98
|
+
self.draw_object_rectangle_xyxy(frame, (x1, y1, x2, y2), label, color, alpha=alpha, filled=filled)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class ObjectDetectorYOLO(ObjectDetectorBase):
|
|
102
|
+
def __init__(self, model_path="yolo11m.pt", device='cpu', colors=None):
|
|
103
|
+
super().__init__(model_path, device=device, colors=colors)
|
|
104
|
+
from ultralytics import YOLO
|
|
105
|
+
self.model = YOLO(self.model_path)
|
|
106
|
+
|
|
107
|
+
def predict_one(self, x, confidence_threshold=0.5):
|
|
108
|
+
return self.model.predict(x, device=self.device, conf=confidence_threshold, verbose=False)
|
|
109
|
+
|
|
110
|
+
def predict(self, X, max_n=64, confidence_threshold=0.5):
|
|
111
|
+
predictions = []
|
|
112
|
+
|
|
113
|
+
num_batches = math.ceil(len(X) / max_n)
|
|
114
|
+
|
|
115
|
+
for batch_idx in range(num_batches):
|
|
116
|
+
timer = Timer()
|
|
117
|
+
start_idx = batch_idx * max_n
|
|
118
|
+
end_idx = min((batch_idx + 1) * max_n, len(X))
|
|
119
|
+
|
|
120
|
+
batch_images = X[start_idx:end_idx]
|
|
121
|
+
batch_predictions = self.model.predict(batch_images, device=self.device, conf=confidence_threshold, verbose=False)
|
|
122
|
+
predictions.extend(batch_predictions)
|
|
123
|
+
|
|
124
|
+
elapsed = timer.stop()
|
|
125
|
+
FPS = len(batch_images) / elapsed
|
|
126
|
+
debug(f"batch {batch_idx} / {num_batches}", "elapsed", elapsed, "FPS", FPS, "len", len(batch_images))
|
|
127
|
+
|
|
128
|
+
return predictions
|
|
129
|
+
|
|
130
|
+
def resize(self, image):
|
|
131
|
+
return super().resize(image, 640)
|
|
132
|
+
|
|
133
|
+
def draw(self, image, detections, colors=None):
|
|
134
|
+
if colors is None:
|
|
135
|
+
colors = self.colors
|
|
136
|
+
height, width = image.shape[:2]
|
|
137
|
+
boxes = detections.boxes
|
|
138
|
+
class_names = detections.names
|
|
139
|
+
for i, box in enumerate(boxes):
|
|
140
|
+
bbox = (np.array(box.xyxyn[0]) * np.array([width, height, width, height])).astype(int).tolist()
|
|
141
|
+
|
|
142
|
+
confidence = box.conf[0].item()
|
|
143
|
+
class_id = int(box.cls[0].item())
|
|
144
|
+
label = class_names[class_id]
|
|
145
|
+
label_text = f"{label} {int(confidence * 100)}%"
|
|
146
|
+
|
|
147
|
+
color = colors[i % len(colors)]
|
|
148
|
+
self.draw_object_rectangle_xyxy(image, bbox, label_text, color)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class ImageObjectDetectionProcessor(BaseProcessor):
|
|
152
|
+
def __init__(self, processor_config, max_progress) -> None:
|
|
153
|
+
super().__init__(processor_config, max_progress)
|
|
154
|
+
self.default.update({"JPEG_QUALITY": 80, "min_confidence": 0.5, "model_type": 0, "height": 720})
|
|
155
|
+
|
|
156
|
+
def init_run(self):
|
|
157
|
+
TPU = os.environ.get("MEDIA_PROCESSING_BACKEND", "cpu")
|
|
158
|
+
self.predictor = ObjectDetectorYOLO(model_path=["yolo11x.pt", "yolo11l.pt", "yolo11m.pt", "yolo11s.pt"][self["model_type"]], device=TPU)
|
|
159
|
+
|
|
160
|
+
def process(self, media):
|
|
161
|
+
try:
|
|
162
|
+
self.media = media
|
|
163
|
+
self.local_path = media["local_path"]
|
|
164
|
+
|
|
165
|
+
tag = "object-detections"
|
|
166
|
+
image = self.predictor.load(self.local_path)
|
|
167
|
+
image_resized = self.predictor.resize(image)
|
|
168
|
+
|
|
169
|
+
detections = self.predictor.predict_one([image_resized], confidence_threshold=self["min_confidence"])
|
|
170
|
+
|
|
171
|
+
# image_resized = self.predictor.super().resize(image, h=self["height"])
|
|
172
|
+
image_resized = cv2.cvtColor(image_resized, cv2.COLOR_RGB2BGR)
|
|
173
|
+
self.predictor.draw(image_resized, detections[0])
|
|
174
|
+
result_image_local_path = self.add_suffix_to_filename(self.local_path, tag)
|
|
175
|
+
cv2.imwrite(result_image_local_path, image_resized, [cv2.IMWRITE_JPEG_QUALITY, self["JPEG_QUALITY"]])
|
|
176
|
+
|
|
177
|
+
self.next_content(self.media, tag, result_image_local_path, w=image_resized.shape[1], h=image_resized.shape[0])
|
|
178
|
+
except Exception as e:
|
|
179
|
+
exception(e, self.media)
|
|
180
|
+
return self.media
|
|
181
|
+
|
|
182
|
+
def content_type(self):
|
|
183
|
+
return "image"
|
|
184
|
+
|
|
185
|
+
def name(self):
|
|
186
|
+
return "image-object-detection"
|
|
@@ -21,6 +21,10 @@ from sciveo.ml.images.object_detection import *
|
|
|
21
21
|
|
|
22
22
|
Object Detection Evaluation
|
|
23
23
|
|
|
24
|
+
Using AP and FP for object detector evaluation.
|
|
25
|
+
Need to define max allowed false positives (for example 0.03).
|
|
26
|
+
Usually max AP has relatively high FP rate, so maxing FP is on lower AP.
|
|
27
|
+
|
|
24
28
|
"""
|
|
25
29
|
class EvalObjectDetection:
|
|
26
30
|
def __init__(self, predictions, labels, class_names):
|
|
@@ -79,7 +83,6 @@ class EvalObjectDetection:
|
|
|
79
83
|
|
|
80
84
|
return inter_area / union_area
|
|
81
85
|
|
|
82
|
-
# Compute the Average Precision (AP)
|
|
83
86
|
def compute_ap(self, class_name, confidence_threshold=0.0):
|
|
84
87
|
"""
|
|
85
88
|
Calculate the Average Precision based on IoU and confidence scores.
|
|
@@ -118,40 +121,83 @@ class EvalObjectDetection:
|
|
|
118
121
|
true_positives.append(0)
|
|
119
122
|
false_positives.append(1)
|
|
120
123
|
|
|
121
|
-
# Convert to cumulative sums
|
|
122
124
|
tp_cumsum = np.cumsum(true_positives)
|
|
123
125
|
fp_cumsum = np.cumsum(false_positives)
|
|
124
126
|
|
|
125
|
-
# Compute recall and precision
|
|
126
127
|
recall = tp_cumsum / (count_labels + 1e-20)
|
|
127
128
|
precision = tp_cumsum / (tp_cumsum + fp_cumsum + 1e-20)
|
|
128
129
|
|
|
130
|
+
FP = np.sum(false_positives) / (count_labels + 1e-20)
|
|
131
|
+
|
|
129
132
|
# Compute AP using the trapezoidal rule (integrating precision over recall)
|
|
130
133
|
ap = np.trapz(precision, recall)
|
|
131
|
-
return ap
|
|
134
|
+
return ap, FP
|
|
132
135
|
|
|
133
|
-
|
|
136
|
+
# TODO: Use simple gradient-based threshold optimisation instead of current grid search.
|
|
137
|
+
def calc_thresholds(self, class_name):
|
|
134
138
|
list_ap = []
|
|
139
|
+
list_FP = []
|
|
135
140
|
list_thresholds = np.linspace(0.0, 1.0, 101).tolist()
|
|
136
141
|
for i, threshold in enumerate(list_thresholds):
|
|
137
|
-
ap = self.compute_ap(class_name, threshold)
|
|
142
|
+
ap, FP = self.compute_ap(class_name, threshold)
|
|
138
143
|
list_ap.append(ap)
|
|
144
|
+
list_FP.append(FP)
|
|
139
145
|
if i % 10 == 0:
|
|
140
|
-
debug(class_name, "threshold", threshold, "ap", ap)
|
|
146
|
+
debug(class_name, "threshold", threshold, "ap", ap, "FP", FP)
|
|
147
|
+
return list_thresholds, list_ap, list_FP
|
|
148
|
+
|
|
149
|
+
def threshold(self, class_name, list_thresholds, list_ap, list_FP, max_fp=0.05):
|
|
141
150
|
idx = list_ap.index(max(list_ap))
|
|
142
|
-
|
|
143
|
-
|
|
151
|
+
if list_FP[idx] <= max_fp:
|
|
152
|
+
return list_thresholds[idx]
|
|
153
|
+
|
|
154
|
+
for i in range(len(list_thresholds)):
|
|
155
|
+
if list_FP[i] <= max_fp:
|
|
156
|
+
debug("Threshold", class_name, list_thresholds[i], "AP", list_ap[i], "max_fp", max_fp, "FP", list_FP[i])
|
|
157
|
+
idx = i
|
|
158
|
+
break
|
|
144
159
|
|
|
145
|
-
|
|
160
|
+
return list_thresholds[idx]
|
|
161
|
+
|
|
162
|
+
def thresholds(self):
|
|
163
|
+
"""
|
|
164
|
+
Calculate best confidence thresholds for every class.
|
|
165
|
+
Predictions should be non-thresholded.
|
|
166
|
+
Thresholds precission is 1/100.
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
result = {}
|
|
170
|
+
list_thresholds = {}
|
|
171
|
+
list_ap = {}
|
|
172
|
+
list_FP = {}
|
|
173
|
+
|
|
174
|
+
for class_name in self.class_names:
|
|
175
|
+
list_thresholds[class_name], list_ap[class_name], list_FP[class_name] = self.calc_thresholds(class_name)
|
|
176
|
+
idx = list_ap[class_name].index(max(list_ap[class_name]))
|
|
177
|
+
debug("Threshold", class_name, list_thresholds[class_name][idx], "max AP", list_ap[class_name][idx])
|
|
178
|
+
|
|
179
|
+
for max_fp in [0.01, 0.03, 0.05, 0.1]:
|
|
180
|
+
result[max_fp] = {"default": 0.5}
|
|
181
|
+
for class_name in self.class_names:
|
|
182
|
+
class_threshold = self.threshold(class_name, list_thresholds[class_name], list_ap[class_name], list_FP[class_name], max_fp)
|
|
183
|
+
result[max_fp][class_name] = class_threshold
|
|
184
|
+
|
|
185
|
+
return result
|
|
186
|
+
|
|
187
|
+
def evaluate(self, confidence_thresholds={"default": 0.0}):
|
|
146
188
|
"""
|
|
147
189
|
Calculate metrics like mAP based on IoU.
|
|
148
190
|
"""
|
|
191
|
+
|
|
149
192
|
aps = []
|
|
150
193
|
class_ap = {}
|
|
194
|
+
class_FP = {}
|
|
195
|
+
|
|
151
196
|
for class_name in self.class_names:
|
|
152
|
-
ap = self.compute_ap(class_name,
|
|
197
|
+
ap, FP = self.compute_ap(class_name, confidence_thresholds.get(class_name, confidence_thresholds["default"]))
|
|
153
198
|
aps.append(ap)
|
|
154
199
|
class_ap[class_name] = ap
|
|
200
|
+
class_FP[class_name] = FP
|
|
155
201
|
|
|
156
202
|
mAP = np.mean(aps)
|
|
157
|
-
return {'mAP': mAP, 'AP per class': class_ap}
|
|
203
|
+
return {'mAP': mAP, 'AP per class': class_ap, "FP per class": class_FP}
|
|
@@ -20,7 +20,7 @@ from sciveo.tools.configuration import GlobalConfiguration
|
|
|
20
20
|
SCIVEO_LOGGER_NAME = "sciveo-log"
|
|
21
21
|
|
|
22
22
|
_sciveo_global_config = GlobalConfiguration.get()
|
|
23
|
-
_sciveo_log_min_level = _sciveo_global_config["
|
|
23
|
+
_sciveo_log_min_level = _sciveo_global_config["SCI_LOG_LEVEL"]
|
|
24
24
|
_sciveo_log_lock = Lock()
|
|
25
25
|
|
|
26
26
|
def _sciveo_get_logger(name):
|
|
@@ -28,7 +28,11 @@ def _sciveo_get_logger(name):
|
|
|
28
28
|
if not logger.hasHandlers():
|
|
29
29
|
with _sciveo_log_lock:
|
|
30
30
|
if not logger.hasHandlers():
|
|
31
|
-
|
|
31
|
+
log_min_level = logging.getLevelName(_sciveo_log_min_level)
|
|
32
|
+
if (isinstance(log_min_level, str) and log_min_level.startswith("Level")) or isinstance(log_min_level, int):
|
|
33
|
+
log_min_level = "DEBUG"
|
|
34
|
+
logger.setLevel(log_min_level)
|
|
35
|
+
|
|
32
36
|
formatter = logging.Formatter('%(asctime)s [%(thread)d] [%(levelname)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
|
|
33
37
|
ch = logging.StreamHandler()
|
|
34
38
|
ch.setFormatter(formatter)
|
|
@@ -66,6 +66,7 @@ sciveo/media/pipelines/processors/image/filters.py
|
|
|
66
66
|
sciveo/media/pipelines/processors/image/generators.py
|
|
67
67
|
sciveo/media/pipelines/processors/image/histogram.py
|
|
68
68
|
sciveo/media/pipelines/processors/image/mask.py
|
|
69
|
+
sciveo/media/pipelines/processors/image/object_detection.py
|
|
69
70
|
sciveo/media/pipelines/processors/image/resize.py
|
|
70
71
|
sciveo/media/pipelines/processors/image/segmentation.py
|
|
71
72
|
sciveo/media/pipelines/processors/image/watermark.py
|
|
@@ -56,6 +56,7 @@ diffusers>=0.0.0
|
|
|
56
56
|
transformers>=0.0.0
|
|
57
57
|
accelerate>=0.0.0
|
|
58
58
|
annoy>=0.0.0
|
|
59
|
+
ultralytics
|
|
59
60
|
|
|
60
61
|
[media-ml]
|
|
61
62
|
tensorflow>=0.0.0
|
|
@@ -66,6 +67,7 @@ diffusers>=0.0.0
|
|
|
66
67
|
transformers>=0.0.0
|
|
67
68
|
accelerate>=0.0.0
|
|
68
69
|
annoy>=0.0.0
|
|
70
|
+
ultralytics
|
|
69
71
|
|
|
70
72
|
[media-server]
|
|
71
73
|
fastapi
|
sciveo-0.1.31/sciveo/version.py
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/audio/audio_extractor_process.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/sci/time_series/__init__.py
RENAMED
|
File without changes
|
{sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/sci/time_series/predictor.py
RENAMED
|
File without changes
|
{sciveo-0.1.31 → sciveo-0.1.33}/sciveo/media/pipelines/processors/sci/time_series/trainer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|