matrice-analytics 0.1.2__py3-none-any.whl → 0.1.31__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.
Potentially problematic release.
This version of matrice-analytics might be problematic. Click here for more details.
- matrice_analytics/post_processing/advanced_tracker/matching.py +3 -3
- matrice_analytics/post_processing/advanced_tracker/strack.py +1 -1
- matrice_analytics/post_processing/face_reg/compare_similarity.py +5 -5
- matrice_analytics/post_processing/face_reg/embedding_manager.py +14 -7
- matrice_analytics/post_processing/face_reg/face_recognition.py +123 -34
- matrice_analytics/post_processing/face_reg/face_recognition_client.py +332 -82
- matrice_analytics/post_processing/face_reg/people_activity_logging.py +29 -22
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/__init__.py +9 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/__init__.py +4 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/cli.py +33 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/dataset_stats.py +139 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/export.py +398 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/train.py +447 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/utils.py +129 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/valid.py +93 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/validate_dataset.py +240 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/visualize_augmentation.py +176 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/visualize_predictions.py +96 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/__init__.py +3 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/process.py +246 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/types.py +60 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/utils.py +87 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/__init__.py +3 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/config.py +82 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/hub.py +141 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/plate_recognizer.py +323 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/py.typed +0 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/__init__.py +0 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/__init__.py +0 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/augmentation.py +101 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/dataset.py +97 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/__init__.py +0 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/config.py +114 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/layers.py +553 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/loss.py +55 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/metric.py +86 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/model_builders.py +95 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/model_schema.py +395 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/__init__.py +0 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/backend_utils.py +38 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/utils.py +214 -0
- matrice_analytics/post_processing/ocr/postprocessing.py +0 -1
- matrice_analytics/post_processing/post_processor.py +19 -5
- matrice_analytics/post_processing/usecases/color/clip.py +292 -132
- matrice_analytics/post_processing/usecases/color/color_mapper.py +2 -2
- matrice_analytics/post_processing/usecases/color_detection.py +429 -355
- matrice_analytics/post_processing/usecases/drone_traffic_monitoring.py +41 -386
- matrice_analytics/post_processing/usecases/flare_analysis.py +1 -56
- matrice_analytics/post_processing/usecases/license_plate_detection.py +476 -202
- matrice_analytics/post_processing/usecases/license_plate_monitoring.py +252 -11
- matrice_analytics/post_processing/usecases/people_counting.py +408 -1431
- matrice_analytics/post_processing/usecases/people_counting_bckp.py +1683 -0
- matrice_analytics/post_processing/usecases/vehicle_monitoring.py +39 -10
- matrice_analytics/post_processing/utils/__init__.py +8 -8
- {matrice_analytics-0.1.2.dist-info → matrice_analytics-0.1.31.dist-info}/METADATA +1 -1
- {matrice_analytics-0.1.2.dist-info → matrice_analytics-0.1.31.dist-info}/RECORD +59 -24
- {matrice_analytics-0.1.2.dist-info → matrice_analytics-0.1.31.dist-info}/WHEEL +0 -0
- {matrice_analytics-0.1.2.dist-info → matrice_analytics-0.1.31.dist-info}/licenses/LICENSE.txt +0 -0
- {matrice_analytics-0.1.2.dist-info → matrice_analytics-0.1.31.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utility functions module
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import pathlib
|
|
7
|
+
import pkgutil
|
|
8
|
+
import random
|
|
9
|
+
from collections.abc import Iterator
|
|
10
|
+
from importlib import import_module
|
|
11
|
+
from typing import Optional, Union
|
|
12
|
+
|
|
13
|
+
import cv2
|
|
14
|
+
import keras
|
|
15
|
+
import numpy as np
|
|
16
|
+
import numpy.typing as npt
|
|
17
|
+
|
|
18
|
+
from fast_plate_ocr.core.process import read_and_resize_plate_image
|
|
19
|
+
from fast_plate_ocr.core.types import ImageColorMode, ImageInterpolation, PaddingColor
|
|
20
|
+
from fast_plate_ocr.train.model.config import PlateOCRConfig
|
|
21
|
+
from fast_plate_ocr.train.model.loss import cce_loss, focal_cce_loss
|
|
22
|
+
from fast_plate_ocr.train.model.metric import (
|
|
23
|
+
cat_acc_metric,
|
|
24
|
+
plate_acc_metric,
|
|
25
|
+
plate_len_acc_metric,
|
|
26
|
+
top_3_k_metric,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def one_hot_plate(plate: str, alphabet: str) -> list[list[int]]:
|
|
31
|
+
return [[0 if char != letter else 1 for char in alphabet] for letter in plate]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def target_transform(
|
|
35
|
+
plate_text: str,
|
|
36
|
+
max_plate_slots: int,
|
|
37
|
+
alphabet: str,
|
|
38
|
+
pad_char: str,
|
|
39
|
+
) -> npt.NDArray[np.uint8]:
|
|
40
|
+
# Pad the plates which length is smaller than 'max_plate_slots'
|
|
41
|
+
plate_text = plate_text.ljust(max_plate_slots, pad_char)
|
|
42
|
+
# Generate numpy arrays with one-hot encoding of plates
|
|
43
|
+
encoded_plate = np.array(one_hot_plate(plate_text, alphabet=alphabet), dtype=np.uint8)
|
|
44
|
+
return encoded_plate
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _register_custom_keras():
|
|
48
|
+
base_pkg = "fast_plate_ocr.train.model"
|
|
49
|
+
for _, name, _ in pkgutil.walk_packages(
|
|
50
|
+
import_module(base_pkg).__path__, prefix=f"{base_pkg}."
|
|
51
|
+
):
|
|
52
|
+
if any(m in name for m in ("layers",)):
|
|
53
|
+
import_module(name)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def load_keras_model(
|
|
57
|
+
model_path: Union[str, pathlib.Path],
|
|
58
|
+
plate_config: PlateOCRConfig,
|
|
59
|
+
) -> keras.Model:
|
|
60
|
+
"""
|
|
61
|
+
Utility helper function to load the keras OCR model.
|
|
62
|
+
"""
|
|
63
|
+
_register_custom_keras()
|
|
64
|
+
custom_objects = {
|
|
65
|
+
"cce": cce_loss(
|
|
66
|
+
vocabulary_size=plate_config.vocabulary_size,
|
|
67
|
+
),
|
|
68
|
+
"focal_cce": focal_cce_loss(
|
|
69
|
+
vocabulary_size=plate_config.vocabulary_size,
|
|
70
|
+
),
|
|
71
|
+
"cat_acc": cat_acc_metric(
|
|
72
|
+
max_plate_slots=plate_config.max_plate_slots,
|
|
73
|
+
vocabulary_size=plate_config.vocabulary_size,
|
|
74
|
+
),
|
|
75
|
+
"plate_acc": plate_acc_metric(
|
|
76
|
+
max_plate_slots=plate_config.max_plate_slots,
|
|
77
|
+
vocabulary_size=plate_config.vocabulary_size,
|
|
78
|
+
),
|
|
79
|
+
"top_3_k": top_3_k_metric(
|
|
80
|
+
vocabulary_size=plate_config.vocabulary_size,
|
|
81
|
+
),
|
|
82
|
+
"plate_len_acc": plate_len_acc_metric(
|
|
83
|
+
max_plate_slots=plate_config.max_plate_slots,
|
|
84
|
+
vocabulary_size=plate_config.vocabulary_size,
|
|
85
|
+
pad_token_index=plate_config.pad_idx,
|
|
86
|
+
),
|
|
87
|
+
}
|
|
88
|
+
model = keras.models.load_model(model_path, custom_objects=custom_objects)
|
|
89
|
+
return model
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
IMG_EXTENSIONS: set[str] = {".jpg", ".jpeg", ".png", ".bmp", ".gif", ".tiff", ".webp"}
|
|
93
|
+
"""Valid image extensions for the scope of this script."""
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def load_images_from_folder( # noqa: PLR0913
|
|
97
|
+
img_dir: pathlib.Path,
|
|
98
|
+
width: int,
|
|
99
|
+
height: int,
|
|
100
|
+
image_color_mode: ImageColorMode = "grayscale",
|
|
101
|
+
keep_aspect_ratio: bool = False,
|
|
102
|
+
interpolation_method: ImageInterpolation = "linear",
|
|
103
|
+
padding_color: PaddingColor = (114, 114, 114),
|
|
104
|
+
shuffle: bool = False,
|
|
105
|
+
limit: Optional[int] = None,
|
|
106
|
+
) -> Iterator[npt.NDArray]:
|
|
107
|
+
"""
|
|
108
|
+
Return all images read from a directory. This uses the same read function used during training.
|
|
109
|
+
"""
|
|
110
|
+
# pylint: disable=too-many-arguments
|
|
111
|
+
image_paths = sorted(
|
|
112
|
+
str(f.resolve()) for f in img_dir.iterdir() if f.is_file() and f.suffix in IMG_EXTENSIONS
|
|
113
|
+
)
|
|
114
|
+
if limit:
|
|
115
|
+
image_paths = image_paths[:limit]
|
|
116
|
+
if shuffle:
|
|
117
|
+
random.shuffle(image_paths)
|
|
118
|
+
yield from (
|
|
119
|
+
read_and_resize_plate_image(
|
|
120
|
+
i,
|
|
121
|
+
img_height=height,
|
|
122
|
+
img_width=width,
|
|
123
|
+
image_color_mode=image_color_mode,
|
|
124
|
+
keep_aspect_ratio=keep_aspect_ratio,
|
|
125
|
+
interpolation_method=interpolation_method,
|
|
126
|
+
padding_color=padding_color,
|
|
127
|
+
)
|
|
128
|
+
for i in image_paths
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def postprocess_model_output(
|
|
133
|
+
prediction: npt.NDArray,
|
|
134
|
+
alphabet: str,
|
|
135
|
+
max_plate_slots: int,
|
|
136
|
+
vocab_size: int,
|
|
137
|
+
) -> tuple[str, npt.NDArray]:
|
|
138
|
+
"""
|
|
139
|
+
Return plate text and confidence scores from raw model output.
|
|
140
|
+
"""
|
|
141
|
+
prediction = prediction.reshape((max_plate_slots, vocab_size))
|
|
142
|
+
probs = np.max(prediction, axis=-1)
|
|
143
|
+
prediction = np.argmax(prediction, axis=-1)
|
|
144
|
+
plate = "".join([alphabet[x] for x in prediction])
|
|
145
|
+
return plate, probs
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def low_confidence_positions(probs, thresh=0.3) -> npt.NDArray:
|
|
149
|
+
"""Returns indices of elements in `probs` less than `thresh`, indicating low confidence."""
|
|
150
|
+
return np.where(np.array(probs) < thresh)[0]
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def display_predictions(
|
|
154
|
+
image: npt.NDArray,
|
|
155
|
+
plate: str,
|
|
156
|
+
probs: npt.NDArray,
|
|
157
|
+
low_conf_thresh: float,
|
|
158
|
+
) -> None:
|
|
159
|
+
"""
|
|
160
|
+
Display plate and corresponding prediction.
|
|
161
|
+
"""
|
|
162
|
+
plate_str = "".join(plate)
|
|
163
|
+
logging.info("Plate: %s", plate_str)
|
|
164
|
+
logging.info("Confidence: %s", probs)
|
|
165
|
+
image_to_show = cv2.resize(image, None, fx=3, fy=3, interpolation=cv2.INTER_LINEAR)
|
|
166
|
+
if len(image_to_show.shape) == 2:
|
|
167
|
+
image_to_show = cv2.cvtColor(image_to_show, cv2.COLOR_GRAY2RGB)
|
|
168
|
+
elif image_to_show.shape[2] == 3:
|
|
169
|
+
image_to_show = cv2.cvtColor(image_to_show, cv2.COLOR_BGR2RGB)
|
|
170
|
+
# Average probabilities
|
|
171
|
+
avg_prob = np.mean(probs) * 100
|
|
172
|
+
cv2.putText(
|
|
173
|
+
image_to_show,
|
|
174
|
+
f"{plate_str} {avg_prob:.{2}f}%",
|
|
175
|
+
org=(5, 30),
|
|
176
|
+
fontFace=cv2.FONT_HERSHEY_SIMPLEX,
|
|
177
|
+
fontScale=1,
|
|
178
|
+
color=(0, 0, 0),
|
|
179
|
+
lineType=1,
|
|
180
|
+
thickness=6,
|
|
181
|
+
)
|
|
182
|
+
cv2.putText(
|
|
183
|
+
image_to_show,
|
|
184
|
+
f"{plate_str} {avg_prob:.{2}f}%",
|
|
185
|
+
org=(5, 30),
|
|
186
|
+
fontFace=cv2.FONT_HERSHEY_SIMPLEX,
|
|
187
|
+
fontScale=1,
|
|
188
|
+
color=(255, 255, 255),
|
|
189
|
+
lineType=1,
|
|
190
|
+
thickness=2,
|
|
191
|
+
)
|
|
192
|
+
# Display character with low confidence
|
|
193
|
+
low_conf_chars = "Low conf. on: " + " ".join(
|
|
194
|
+
[plate[i] for i in low_confidence_positions(probs, thresh=low_conf_thresh)]
|
|
195
|
+
)
|
|
196
|
+
cv2.putText(
|
|
197
|
+
image_to_show,
|
|
198
|
+
low_conf_chars,
|
|
199
|
+
org=(5, 200),
|
|
200
|
+
fontFace=cv2.FONT_HERSHEY_SIMPLEX,
|
|
201
|
+
fontScale=0.7,
|
|
202
|
+
color=(0, 0, 220),
|
|
203
|
+
lineType=1,
|
|
204
|
+
thickness=2,
|
|
205
|
+
)
|
|
206
|
+
try:
|
|
207
|
+
cv2.imshow("plates", image_to_show)
|
|
208
|
+
if cv2.waitKey(0) & 0xFF == ord("q"):
|
|
209
|
+
return
|
|
210
|
+
except cv2.error as e: # pylint: disable=catching-non-exception
|
|
211
|
+
raise RuntimeError( # pylint: disable=bad-exception-cause
|
|
212
|
+
"This visualization requires full OpenCV with GUI support. "
|
|
213
|
+
"Install with `pip install opencv-python` instead of headless."
|
|
214
|
+
) from e
|
|
@@ -9,7 +9,6 @@ class TextPostprocessor:
|
|
|
9
9
|
Args:
|
|
10
10
|
logging_level: The level of logging detail. Default is INFO.
|
|
11
11
|
"""
|
|
12
|
-
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging_level)
|
|
13
12
|
self.logger = logging.getLogger('TextPostprocessor')
|
|
14
13
|
|
|
15
14
|
self.task_processors = {
|
|
@@ -312,6 +312,7 @@ class PostProcessor:
|
|
|
312
312
|
) -> None:
|
|
313
313
|
"""Remove parameters that aren't needed for specific use cases."""
|
|
314
314
|
facial_recognition_usecases = {"face_recognition"}
|
|
315
|
+
license_plate_monitoring_usecases = {"license_plate_monitor"}
|
|
315
316
|
|
|
316
317
|
if usecase not in facial_recognition_usecases:
|
|
317
318
|
if "facial_recognition_server_id" in config_params:
|
|
@@ -319,7 +320,14 @@ class PostProcessor:
|
|
|
319
320
|
f"Removing facial_recognition_server_id from {usecase} config"
|
|
320
321
|
)
|
|
321
322
|
config_params.pop("facial_recognition_server_id", None)
|
|
322
|
-
|
|
323
|
+
|
|
324
|
+
if usecase not in license_plate_monitoring_usecases:
|
|
325
|
+
if "lpr_server_id" in config_params:
|
|
326
|
+
logging.debug(f"Removing lpr_server_id from {usecase} config")
|
|
327
|
+
config_params.pop("lpr_server_id", None)
|
|
328
|
+
|
|
329
|
+
# Keep session and lpr_server_id only for use cases that need them
|
|
330
|
+
if usecase not in facial_recognition_usecases and usecase not in license_plate_monitoring_usecases:
|
|
323
331
|
if "session" in config_params:
|
|
324
332
|
logging.debug(f"Removing session from {usecase} config")
|
|
325
333
|
config_params.pop("session", None)
|
|
@@ -660,8 +668,13 @@ class PostProcessor:
|
|
|
660
668
|
if not use_case_class:
|
|
661
669
|
raise ValueError(f"Use case '{config.category}/{config.usecase}' not found")
|
|
662
670
|
|
|
663
|
-
|
|
664
|
-
|
|
671
|
+
|
|
672
|
+
if isinstance(use_case_class, FaceRecognitionEmbeddingUseCase):
|
|
673
|
+
use_case = use_case_class(config=config)
|
|
674
|
+
else:
|
|
675
|
+
use_case = use_case_class()
|
|
676
|
+
logger.info(f"Created use case instance for: {config.category}/{config.usecase}")
|
|
677
|
+
|
|
665
678
|
|
|
666
679
|
# Cache the instance
|
|
667
680
|
self._use_case_cache[cache_key] = use_case
|
|
@@ -689,7 +702,8 @@ class PostProcessor:
|
|
|
689
702
|
FlareAnalysisUseCase,
|
|
690
703
|
LicensePlateMonitorUseCase,
|
|
691
704
|
AgeGenderUseCase,
|
|
692
|
-
PeopleTrackingUseCase
|
|
705
|
+
PeopleTrackingUseCase,
|
|
706
|
+
FaceRecognitionEmbeddingUseCase
|
|
693
707
|
}
|
|
694
708
|
|
|
695
709
|
# Async use cases
|
|
@@ -1017,7 +1031,7 @@ class PostProcessor:
|
|
|
1017
1031
|
"total_processing_time": 0.0,
|
|
1018
1032
|
}
|
|
1019
1033
|
|
|
1020
|
-
def _parse_config(
|
|
1034
|
+
def _parse_config( # TODO: remove all of the kwargs that are not in the use case config
|
|
1021
1035
|
self, config: Union[BaseConfig, Dict[str, Any], str, Path]
|
|
1022
1036
|
) -> BaseConfig:
|
|
1023
1037
|
"""Parse configuration from various input formats."""
|