depthai-nodes 0.0.1__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.
Files changed (80) hide show
  1. depthai_nodes/__init__.py +3 -0
  2. depthai_nodes/ml/__init__.py +0 -0
  3. depthai_nodes/ml/messages/__init__.py +21 -0
  4. depthai_nodes/ml/messages/classification.py +48 -0
  5. depthai_nodes/ml/messages/creators/__init__.py +24 -0
  6. depthai_nodes/ml/messages/creators/classification.py +79 -0
  7. depthai_nodes/ml/messages/creators/depth.py +93 -0
  8. depthai_nodes/ml/messages/creators/detection.py +235 -0
  9. depthai_nodes/ml/messages/creators/image.py +53 -0
  10. depthai_nodes/ml/messages/creators/keypoints.py +198 -0
  11. depthai_nodes/ml/messages/creators/misc.py +49 -0
  12. depthai_nodes/ml/messages/creators/segmentation.py +61 -0
  13. depthai_nodes/ml/messages/creators/thermal.py +46 -0
  14. depthai_nodes/ml/messages/creators/tracked_features.py +94 -0
  15. depthai_nodes/ml/messages/img_detections.py +139 -0
  16. depthai_nodes/ml/messages/keypoints.py +103 -0
  17. depthai_nodes/ml/messages/lines.py +137 -0
  18. depthai_nodes/ml/messages/misc.py +34 -0
  19. depthai_nodes/ml/messages/segmentation.py +41 -0
  20. depthai_nodes/ml/parsers/__init__.py +37 -0
  21. depthai_nodes/ml/parsers/age_gender.py +43 -0
  22. depthai_nodes/ml/parsers/classification.py +97 -0
  23. depthai_nodes/ml/parsers/fastsam.py +223 -0
  24. depthai_nodes/ml/parsers/hrnet.py +80 -0
  25. depthai_nodes/ml/parsers/image_output.py +81 -0
  26. depthai_nodes/ml/parsers/keypoints.py +107 -0
  27. depthai_nodes/ml/parsers/mediapipe_hand_landmarker.py +99 -0
  28. depthai_nodes/ml/parsers/mediapipe_palm_detection.py +155 -0
  29. depthai_nodes/ml/parsers/mlsd.py +102 -0
  30. depthai_nodes/ml/parsers/monocular_depth.py +94 -0
  31. depthai_nodes/ml/parsers/scrfd.py +184 -0
  32. depthai_nodes/ml/parsers/segmentation.py +95 -0
  33. depthai_nodes/ml/parsers/superanimal_landmarker.py +88 -0
  34. depthai_nodes/ml/parsers/thermal_image.py +48 -0
  35. depthai_nodes/ml/parsers/utils/__init__.py +9 -0
  36. depthai_nodes/ml/parsers/utils/decode_detections.py +108 -0
  37. depthai_nodes/ml/parsers/utils/denormalize.py +25 -0
  38. depthai_nodes/ml/parsers/utils/fastsam.py +320 -0
  39. depthai_nodes/ml/parsers/utils/medipipe.py +382 -0
  40. depthai_nodes/ml/parsers/utils/mlsd.py +80 -0
  41. depthai_nodes/ml/parsers/utils/nms.py +43 -0
  42. depthai_nodes/ml/parsers/utils/scrfd.py +140 -0
  43. depthai_nodes/ml/parsers/utils/superanimal.py +62 -0
  44. depthai_nodes/ml/parsers/utils/xfeat.py +354 -0
  45. depthai_nodes/ml/parsers/utils/yolo.py +351 -0
  46. depthai_nodes/ml/parsers/xfeat.py +133 -0
  47. depthai_nodes/ml/parsers/yolo.py +235 -0
  48. depthai_nodes/ml/parsers/yunet.py +168 -0
  49. depthai_nodes-0.0.1.dist-info/LICENSE +201 -0
  50. depthai_nodes-0.0.1.dist-info/METADATA +287 -0
  51. depthai_nodes-0.0.1.dist-info/RECORD +80 -0
  52. depthai_nodes-0.0.1.dist-info/WHEEL +5 -0
  53. depthai_nodes-0.0.1.dist-info/top_level.txt +3 -0
  54. examples/main.py +78 -0
  55. examples/utils/__init__.py +0 -0
  56. examples/utils/arguments.py +43 -0
  57. examples/utils/model.py +87 -0
  58. examples/utils/parser.py +137 -0
  59. examples/visualization/__init__.py +0 -0
  60. examples/visualization/classification.py +57 -0
  61. examples/visualization/colors.py +66 -0
  62. examples/visualization/detection.py +177 -0
  63. examples/visualization/image.py +15 -0
  64. examples/visualization/keypoints.py +23 -0
  65. examples/visualization/mapping.py +28 -0
  66. examples/visualization/messages.py +64 -0
  67. examples/visualization/segmentation.py +41 -0
  68. examples/visualization/visualize.py +12 -0
  69. tests/unittests/__init__.py +0 -0
  70. tests/unittests/test_creators/test_classification.py +168 -0
  71. tests/unittests/test_creators/test_depth.py +120 -0
  72. tests/unittests/test_creators/test_detections.py +191 -0
  73. tests/unittests/test_creators/test_hand_keypoints.py +137 -0
  74. tests/unittests/test_creators/test_image.py +51 -0
  75. tests/unittests/test_creators/test_keypoints.py +167 -0
  76. tests/unittests/test_creators/test_line_detections.py +103 -0
  77. tests/unittests/test_creators/test_misc.py +55 -0
  78. tests/unittests/test_creators/test_segmentation.py +44 -0
  79. tests/unittests/test_creators/test_thermal.py +45 -0
  80. tests/unittests/test_creators/test_tracked_features.py +99 -0
@@ -0,0 +1,3 @@
1
+ from .ml.parsers import *
2
+
3
+ __version__ = "0.0.1"
File without changes
@@ -0,0 +1,21 @@
1
+ from .classification import Classifications
2
+ from .img_detections import (
3
+ ImgDetectionExtended,
4
+ ImgDetectionsExtended,
5
+ )
6
+ from .keypoints import HandKeypoints, Keypoints
7
+ from .lines import Line, Lines
8
+ from .misc import AgeGender
9
+ from .segmentation import SegmentationMasks
10
+
11
+ __all__ = [
12
+ "ImgDetectionExtended",
13
+ "ImgDetectionsExtended",
14
+ "HandKeypoints",
15
+ "Keypoints",
16
+ "Line",
17
+ "Lines",
18
+ "Classifications",
19
+ "SegmentationMasks",
20
+ "AgeGender",
21
+ ]
@@ -0,0 +1,48 @@
1
+ from typing import List
2
+
3
+ import depthai as dai
4
+
5
+
6
+ class Classifications(dai.Buffer):
7
+ """Classification class for storing the class names and their respective scores.
8
+
9
+ Attributes
10
+ ----------
11
+ classes : list[str]
12
+ A list of classes.
13
+ scores : list[float]
14
+ A list of corresponding probability scores.
15
+ """
16
+
17
+ def __init__(self):
18
+ """Initializes the Classifications object and sets the classes and scores to
19
+ empty lists."""
20
+ dai.Buffer.__init__(self)
21
+ self._classes: List[str] = []
22
+ self._scores: List[float] = []
23
+
24
+ @property
25
+ def classes(self) -> List:
26
+ """Returns the list of classes."""
27
+ return self._classes
28
+
29
+ @property
30
+ def scores(self) -> List:
31
+ """Returns the list of scores."""
32
+ return self._scores
33
+
34
+ @classes.setter
35
+ def classes(self, class_names: List[str]):
36
+ """Sets the list of classes.
37
+
38
+ @param classes: A list of class names.
39
+ """
40
+ self._classes = class_names
41
+
42
+ @scores.setter
43
+ def scores(self, scores: List[float]):
44
+ """Sets the list of scores.
45
+
46
+ @param scores: A list of scores.
47
+ """
48
+ self._scores = scores
@@ -0,0 +1,24 @@
1
+ from .classification import create_classification_message
2
+ from .depth import create_depth_message
3
+ from .detection import create_detection_message, create_line_detection_message
4
+ from .image import create_image_message
5
+ from .keypoints import create_hand_keypoints_message, create_keypoints_message
6
+ from .misc import create_age_gender_message
7
+ from .segmentation import create_sam_message, create_segmentation_message
8
+ from .thermal import create_thermal_message
9
+ from .tracked_features import create_tracked_features_message
10
+
11
+ __all__ = [
12
+ "create_image_message",
13
+ "create_segmentation_message",
14
+ "create_hand_keypoints_message",
15
+ "create_detection_message",
16
+ "create_depth_message",
17
+ "create_line_detection_message",
18
+ "create_tracked_features_message",
19
+ "create_keypoints_message",
20
+ "create_thermal_message",
21
+ "create_classification_message",
22
+ "create_sam_message",
23
+ "create_age_gender_message",
24
+ ]
@@ -0,0 +1,79 @@
1
+ from typing import List, Union
2
+
3
+ import numpy as np
4
+
5
+ from ...messages import Classifications
6
+
7
+
8
+ def create_classification_message(
9
+ classes: List, scores: Union[np.ndarray, List]
10
+ ) -> Classifications:
11
+ """Create a message for classification. The message contains the class names and
12
+ their respective scores, sorted in descending order of scores.
13
+
14
+ @param classes: A list containing class names.
15
+ @type classes: List
16
+ @param scores: A numpy array of shape (n_classes,) containing the probability score of each class.
17
+ @type scores: np.ndarray
18
+
19
+ @return: A message with attributes `classes` and `scores`. `classes` is a list of classes, sorted in descending order of scores. `scores` is a list of the corresponding scores.
20
+ @rtype: Classifications
21
+
22
+ @raises ValueError: If the provided classes are None.
23
+ @raises ValueError: If the provided classes are not a list.
24
+ @raises ValueError: If the provided classes are empty.
25
+ @raises ValueError: If the provided scores are None.
26
+ @raises ValueError: If the provided scores are not a list or a numpy array.
27
+ @raises ValueError: If the provided scores are empty.
28
+ @raises ValueError: If the provided scores are not a 1D array.
29
+ @raises ValueError: If the provided scores are not of type float.
30
+ @raises ValueError: If the provided scores do not sum to 1.
31
+ @raises ValueError: If the number of labels and scores mismatch.
32
+ """
33
+ if isinstance(classes, type(None)):
34
+ raise ValueError("Classes should not be None.")
35
+
36
+ if not isinstance(classes, list):
37
+ raise ValueError(f"Classes should be a list, got {type(classes)}.")
38
+
39
+ if len(classes) == 0:
40
+ raise ValueError("Classes should not be empty.")
41
+
42
+ if type(scores) == type(None):
43
+ raise ValueError("Scores should not be None.")
44
+
45
+ if not isinstance(scores, np.ndarray) and not isinstance(scores, list):
46
+ raise ValueError(
47
+ f"Scores should be a list or a numpy array, got {type(scores)}."
48
+ )
49
+
50
+ if isinstance(scores, list):
51
+ scores = np.array(scores)
52
+
53
+ if len(scores) == 0:
54
+ raise ValueError("Scores should not be empty.")
55
+
56
+ if len(scores) != len(scores.flatten()):
57
+ raise ValueError(f"Scores should be a 1D array, got {scores.shape}.")
58
+
59
+ scores = scores.flatten()
60
+
61
+ if not np.issubdtype(scores.dtype, np.floating):
62
+ raise ValueError(f"Scores should be of type float, got {scores.dtype}.")
63
+
64
+ if not np.isclose(np.sum(scores), 1.0, atol=1e-2):
65
+ raise ValueError(f"Scores should sum to 1, got {np.sum(scores)}.")
66
+
67
+ if len(scores) != len(classes):
68
+ raise ValueError(
69
+ f"Number of labels and scores mismatch. Provided {len(scores)} scores and {len(classes)} class names."
70
+ )
71
+
72
+ classification_msg = Classifications()
73
+ sorted_args = np.argsort(-scores, kind="stable")
74
+ scores = scores[sorted_args]
75
+
76
+ classification_msg.classes = [classes[i] for i in sorted_args]
77
+ classification_msg.scores = scores.tolist()
78
+
79
+ return classification_msg
@@ -0,0 +1,93 @@
1
+ from typing import Literal
2
+
3
+ import depthai as dai
4
+ import numpy as np
5
+
6
+ UINT16_MAX_VALUE = 65535
7
+
8
+
9
+ def create_depth_message(
10
+ depth_map: np.ndarray,
11
+ depth_type: Literal["relative", "metric"],
12
+ depth_limit: float = 0.0,
13
+ ) -> dai.ImgFrame:
14
+ """Create a DepthAI message for a depth map.
15
+
16
+ @param depth_map: A NumPy array representing the depth map with shape HW or NHW/HWN.
17
+ Here N stands for batch dimension.
18
+ @type depth_map: np.array
19
+ @param depth_type: A string indicating the type of depth map. It can either be
20
+ 'relative' or 'metric'.
21
+ @type depth_type: Literal['relative', 'metric']
22
+ @param depth_limit: The maximum depth value (in meters) to be used in the depth map.
23
+ The default value is 0, which means no limit.
24
+ @type depth_limit: float
25
+ @return: An ImgFrame object containing the depth information.
26
+ @rtype: dai.ImgFrame
27
+ @raise ValueError: If the depth map is not a NumPy array.
28
+ @raise ValueError: If the depth map is not 2D or 3D.
29
+ @raise ValueError: If the depth map shape is not NHW or HWN.
30
+ @raise ValueError: If the depth type is not 'relative' or 'metric'.
31
+ @raise ValueError: If the depth limit is not 0 and the depth type is 'relative'.
32
+ @raise ValueError: If the depth limit is 0 and the depth type is 'metric'.
33
+ @raise ValueError: If the depth limit is negative.
34
+ """
35
+
36
+ if not isinstance(depth_map, np.ndarray):
37
+ raise ValueError(f"Expected numpy array, got {type(depth_map)}.")
38
+
39
+ if len(depth_map.shape) == 3:
40
+ if depth_map.shape[0] == 1:
41
+ depth_map = depth_map[0, :, :] # NHW to HW
42
+ elif depth_map.shape[2] == 1:
43
+ depth_map = depth_map[:, :, 0] # HWN to HW
44
+ else:
45
+ raise ValueError(
46
+ f"Unexpected image shape. Expected NHW or HWN, got {depth_map.shape}."
47
+ )
48
+
49
+ if len(depth_map.shape) != 2:
50
+ raise ValueError(f"Expected 2D or 3D input, got {len(depth_map.shape)}D input.")
51
+
52
+ if not (depth_type == "relative" or depth_type == "metric"):
53
+ raise ValueError(
54
+ f"Invalid depth type: {depth_type}. Only 'relative' and 'metric' are supported."
55
+ )
56
+
57
+ if depth_type == "relative" and depth_limit != 0:
58
+ raise ValueError(
59
+ f"Invalid depth limit: {depth_limit}. For relative depth, depth limit must be equal to 0."
60
+ )
61
+
62
+ if depth_type == "metric" and depth_limit == 0:
63
+ raise ValueError(
64
+ f"Invalid depth limit: {depth_limit}. For metric depth, depth limit must be bigger than 0."
65
+ )
66
+
67
+ if depth_limit < 0:
68
+ raise ValueError(
69
+ f"Invalid depth limit: {depth_limit}. Depth limit must be bigger than 0."
70
+ )
71
+
72
+ data_type = dai.ImgFrame.Type.RAW16
73
+
74
+ min_val = depth_map.min() if depth_type == "relative" else 0
75
+ max_val = depth_map.max() if depth_type == "relative" else depth_limit
76
+
77
+ # clip values bigger than max_val
78
+ depth_map = np.clip(depth_map, a_min=None, a_max=max_val)
79
+
80
+ # normalize depth map to UINT16 range [0, UINT16_MAX_VALUE]
81
+ if min_val == max_val: # avoid division by zero
82
+ depth_map = np.zeros_like(depth_map)
83
+ else:
84
+ depth_map = (depth_map - min_val) / (max_val - min_val) * UINT16_MAX_VALUE
85
+ depth_map = depth_map.astype(np.uint16)
86
+
87
+ imgFrame = dai.ImgFrame()
88
+ imgFrame.setFrame(depth_map)
89
+ imgFrame.setWidth(depth_map.shape[1])
90
+ imgFrame.setHeight(depth_map.shape[0])
91
+ imgFrame.setType(data_type)
92
+
93
+ return imgFrame
@@ -0,0 +1,235 @@
1
+ from typing import List, Tuple, Union
2
+
3
+ import depthai as dai
4
+ import numpy as np
5
+
6
+ from ...messages import (
7
+ ImgDetectionExtended,
8
+ ImgDetectionsExtended,
9
+ Line,
10
+ Lines,
11
+ )
12
+
13
+
14
+ def create_detection_message(
15
+ bboxes: np.ndarray,
16
+ scores: np.ndarray,
17
+ labels: List[int] = None,
18
+ keypoints: Union[
19
+ List[Tuple[float, float]], List[Tuple[float, float, float]]
20
+ ] = None,
21
+ masks: List[np.ndarray] = None,
22
+ ) -> dai.ImgDetections:
23
+ """Create a DepthAI message for an object detection.
24
+
25
+ @param bbox: Bounding boxes of detected objects of shape (N,4) meaning [...,[x_min, y_min, x_max, y_max],...].
26
+ @type bbox: np.ndarray
27
+ @param scores: Confidence scores of detected objects of shape (N,).
28
+ @type scores: np.ndarray
29
+ @param labels: Labels of detected objects of shape (N,).
30
+ @type labels: List[int]
31
+ @param keypoints: Keypoints of detected objects of shape (N,2) or (N,3).
32
+ @type keypoints: Optional[Union[List[Tuple[float, float]], List[Tuple[float, float, float]]]]
33
+ @param masks: Masks of detected objects of shape (N, H, W).
34
+ @type masks: List[np.ndarray]
35
+
36
+ @return: Message containing the bounding boxes, labels, confidence scores, and keypoints of detected objects.
37
+ @rtype: dai.ImgDetections OR ImgDetectionsExtended
38
+
39
+ @raise ValueError: If the bboxes are not a numpy array.
40
+ @raise ValueError: If the bboxes are not of shape (N,4).
41
+ @raise ValueError: If the bboxes 2nd dimension is not of size 4.
42
+ @raise ValueError: If the bboxes are not in format [x_min, y_min, x_max, y_max] where xmin < xmax and ymin < ymax.
43
+ @raise ValueError: If the scores are not a numpy array.
44
+ @raise ValueError: If the scores are not of shape (N,).
45
+ @raise ValueError: If the scores do not have the same length as bboxes.
46
+ @raise ValueError: If the labels are not a list.
47
+ @raise ValueError: If each label is not an integer.
48
+ @raise ValueError: If the labels do not have the same length as bboxes.
49
+ @raise ValueError: If the keypoints are not a list.
50
+ @raise ValueError: If each keypoint pair is not a tuple of two or three floats.
51
+ @raise ValueError: If the keypoints do not have the same length as bboxes.
52
+ @raise ValueError: If the masks are not a list.
53
+ @raise ValueError: If each mask is not a 2D numpy array.
54
+ """
55
+
56
+ # checks for bboxes
57
+ if not isinstance(bboxes, np.ndarray):
58
+ raise ValueError(f"Bounding boxes should be numpy array, got {type(bboxes)}.")
59
+
60
+ if len(bboxes) != 0:
61
+ if len(bboxes.shape) != 2:
62
+ raise ValueError(
63
+ f"Bounding boxes should be of shape (N,4) meaning [...,[x_min, y_min, x_max, y_max],...], got {bboxes.shape}."
64
+ )
65
+ if bboxes.shape[1] != 4:
66
+ raise ValueError(
67
+ f"Bounding boxes 2nd dimension should be of size 4 e.g. [x_min, y_min, x_max, y_max] got {bboxes.shape[1]}."
68
+ )
69
+
70
+ x_valid = bboxes[:, 0] < bboxes[:, 2]
71
+ y_valid = bboxes[:, 1] < bboxes[:, 3]
72
+ if not (np.all(x_valid) and np.all(y_valid)):
73
+ raise ValueError(
74
+ "Bounding boxes should be in format [x_min, y_min, x_max, y_max] where xmin < xmax and ymin < ymax."
75
+ )
76
+
77
+ # checks for scores
78
+ if not isinstance(scores, np.ndarray):
79
+ raise ValueError(f"Scores should be numpy array, got {type(scores)}.")
80
+
81
+ if len(scores) != 0:
82
+ if len(scores.shape) != 1:
83
+ raise ValueError(
84
+ f"Scores should be of shape (N,) meaning, got {scores.shape}."
85
+ )
86
+ if scores.shape[0] != bboxes.shape[0]:
87
+ raise ValueError(
88
+ f"Scores should have same length as bboxes, got {scores.shape[0]} and {bboxes.shape[0]}."
89
+ )
90
+
91
+ # checks for labels
92
+ if labels is not None:
93
+ if not isinstance(labels, List):
94
+ raise ValueError(f"Labels should be list, got {type(labels)}.")
95
+ for label in labels:
96
+ if not isinstance(label, int):
97
+ raise ValueError(
98
+ f"Labels should be list of integers, got {type(label)}."
99
+ )
100
+ if len(labels) != bboxes.shape[0]:
101
+ raise ValueError(
102
+ f"Labels should have same length as bboxes, got {len(labels)} and {bboxes.shape[0]}."
103
+ )
104
+
105
+ # checks for keypoints
106
+ if keypoints is not None:
107
+ if not isinstance(keypoints, List):
108
+ raise ValueError(f"Keypoints should be list, got {type(keypoints)}.")
109
+ if len(keypoints) != bboxes.shape[0]:
110
+ raise ValueError(
111
+ f"Keypoints should have same length as bboxes, got {len(keypoints)} and {bboxes.shape[0]}."
112
+ )
113
+
114
+ for object_keypoints in keypoints:
115
+ dim = len(object_keypoints[0]) if len(object_keypoints) != 0 else 0
116
+ for point in object_keypoints:
117
+ if not isinstance(point, (Tuple, List)):
118
+ raise ValueError(
119
+ f"Keypoint pairs should be list of tuples, got {type(point)}."
120
+ )
121
+ if len(point) not in [2, 3]:
122
+ raise ValueError(
123
+ f"Keypoint pairs should be list of tuples of length 2 or 3, got {len(point)}."
124
+ )
125
+ if len(point) != dim:
126
+ raise ValueError(
127
+ "All keypoints should be of same dimension e.g. [x, y] or [x, y, z], got mixed inner dimensions."
128
+ )
129
+
130
+ if masks is not None and len(masks) != 0:
131
+ if not isinstance(masks, List):
132
+ raise ValueError(f"masks should be list, got {type(masks)}.")
133
+ for mask in masks:
134
+ if not isinstance(mask, np.ndarray):
135
+ raise ValueError(f"mask should be numpy array, got {type(mask)}.")
136
+ if len(mask.shape) != 2:
137
+ raise ValueError(f"mask should be a 2D matrix, got {mask.shape}.")
138
+
139
+ if len(masks) != bboxes.shape[0]:
140
+ raise ValueError(
141
+ f"masks should have same length as bboxes, got {len(masks)} and {bboxes.shape[0]}."
142
+ )
143
+
144
+ if keypoints is not None or masks is not None:
145
+ img_detection = ImgDetectionExtended
146
+ img_detections = ImgDetectionsExtended
147
+ else:
148
+ img_detection = dai.ImgDetection
149
+ img_detections = dai.ImgDetections
150
+
151
+ detections = []
152
+ for i in range(bboxes.shape[0]):
153
+ detection = img_detection()
154
+ detection.xmin = bboxes[i][0]
155
+ detection.ymin = bboxes[i][1]
156
+ detection.xmax = bboxes[i][2]
157
+ detection.ymax = bboxes[i][3]
158
+ detection.confidence = scores[i]
159
+ if labels is None:
160
+ detection.label = 0
161
+ else:
162
+ detection.label = labels[i]
163
+ if keypoints is not None:
164
+ detection.keypoints = keypoints[i]
165
+ if masks is not None:
166
+ detection.mask = masks[i]
167
+ detections.append(detection)
168
+
169
+ detections_msg = img_detections()
170
+ detections_msg.detections = detections
171
+ return detections_msg
172
+
173
+
174
+ def create_line_detection_message(lines: np.ndarray, scores: np.ndarray):
175
+ """Create a DepthAI message for a line detection.
176
+
177
+ @param lines: Detected lines of shape (N,4) meaning [...,[x_start, y_start, x_end, y_end],...].
178
+ @type lines: np.ndarray
179
+ @param scores: Confidence scores of detected lines of shape (N,).
180
+ @type scores: np.ndarray
181
+
182
+ @return: Message containing the lines and confidence scores of detected lines.
183
+ @rtype: Lines
184
+
185
+ @raise ValueError: If the lines are not a numpy array.
186
+ @raise ValueError: If the lines are not of shape (N,4).
187
+ @raise ValueError: If the lines 2nd dimension is not of size E{4}.
188
+ @raise ValueError: If the scores are not a numpy array.
189
+ @raise ValueError: If the scores are not of shape (N,).
190
+ @raise ValueError: If the scores do not have the same length as lines.
191
+ """
192
+
193
+ # checks for lines
194
+ if not isinstance(lines, np.ndarray):
195
+ raise ValueError(f"Lines should be numpy array, got {type(lines)}.")
196
+ if len(lines) != 0:
197
+ if len(lines.shape) != 2:
198
+ raise ValueError(
199
+ f"Lines should be of shape (N,4) meaning [...,[x_start, y_start, x_end, y_end],...], got {lines.shape}."
200
+ )
201
+ if lines.shape[1] != 4:
202
+ raise ValueError(
203
+ f"Lines 2nd dimension should be of size 4 e.g. [x_start, y_start, x_end, y_end] got {lines.shape[1]}."
204
+ )
205
+
206
+ # checks for scores
207
+ if not isinstance(scores, np.ndarray):
208
+ raise ValueError(f"Scores should be numpy array, got {type(scores)}.")
209
+
210
+ if len(scores) != 0:
211
+ if len(scores.shape) != 1:
212
+ raise ValueError(
213
+ f"Scores should be of shape (N,) meaning, got {scores.shape}."
214
+ )
215
+
216
+ for score in scores:
217
+ if not isinstance(score, (float, np.floating)):
218
+ raise ValueError(f"Scores should be of type float, got {type(score)}.")
219
+
220
+ if scores.shape[0] != lines.shape[0]:
221
+ raise ValueError(
222
+ f"Scores should have same length as lines, got {scores.shape[0]} and {lines.shape[0]}."
223
+ )
224
+
225
+ line_detections = []
226
+ for i, line in enumerate(lines):
227
+ line_detection = Line()
228
+ line_detection.start_point = dai.Point2f(line[0], line[1])
229
+ line_detection.end_point = dai.Point2f(line[2], line[3])
230
+ line_detection.confidence = float(scores[i])
231
+ line_detections.append(line_detection)
232
+
233
+ lines_msg = Lines()
234
+ lines_msg.lines = line_detections
235
+ return lines_msg
@@ -0,0 +1,53 @@
1
+ import cv2
2
+ import depthai as dai
3
+ import numpy as np
4
+
5
+
6
+ def create_image_message(
7
+ image: np.ndarray,
8
+ is_bgr: bool = True,
9
+ ) -> dai.ImgFrame:
10
+ """Create a DepthAI message for an image array.
11
+
12
+ @param image: Image array in HWC or CHW format.
13
+ @type image: np.array
14
+ @param is_bgr: If True, the image is in BGR format. If False, the image is in RGB
15
+ format. Defaults to True.
16
+ @type is_bgr: bool
17
+ @return: dai.ImgFrame object containing the image information.
18
+ @rtype: dai.ImgFrame
19
+ @raise ValueError: If the image shape is not CHW or HWC.
20
+ """
21
+
22
+ if image.shape[0] in [1, 3]:
23
+ hwc = False
24
+ elif image.shape[2] in [1, 3]:
25
+ hwc = True
26
+ else:
27
+ raise ValueError(
28
+ f"Unexpected image shape. Expected CHW or HWC, got {image.shape}"
29
+ )
30
+
31
+ if not hwc:
32
+ image = np.transpose(image, (1, 2, 0))
33
+
34
+ if isinstance(image[0, 0, 0], (float, np.floating)):
35
+ raise ValueError(f"Expected int type, got {type(image[0, 0, 0])}.")
36
+
37
+ if image.shape[2] == 1: # grayscale
38
+ image = image[:, :, 0]
39
+ img_frame_type = dai.ImgFrame.Type.GRAY8 # HW image
40
+ height, width = image.shape
41
+ else:
42
+ if not is_bgr:
43
+ image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
44
+ img_frame_type = dai.ImgFrame.Type.BGR888i # HWC BGR image
45
+ height, width, _ = image.shape
46
+
47
+ imgFrame = dai.ImgFrame()
48
+ imgFrame.setFrame(image)
49
+ imgFrame.setWidth(width)
50
+ imgFrame.setHeight(height)
51
+ imgFrame.setType(img_frame_type)
52
+
53
+ return imgFrame