From 30180597131584862c81af3323f58c7dac7cd772 Mon Sep 17 00:00:00 2001 From: Vladisalv Sovrasov Date: Fri, 25 Jul 2025 02:47:27 +0900 Subject: [PATCH 01/20] Add support of holes to contours generation --- .../model_api/models/result/segmentation.py | 11 ++++++++-- src/python/model_api/models/segmentation.py | 22 ++++++++++++++----- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/python/model_api/models/result/segmentation.py b/src/python/model_api/models/result/segmentation.py index 37ffff85..7babc47f 100644 --- a/src/python/model_api/models/result/segmentation.py +++ b/src/python/model_api/models/result/segmentation.py @@ -149,13 +149,20 @@ def rotated_rects(self, value): class Contour: - def __init__(self, label: str, probability: float, shape: list[tuple[int, int]]): + def __init__(self, label: str, probability: float, shape: np.ndarray, child_shapes: list[np.ndarray] | None = None): self.shape = shape self.label = label self.probability = probability + self.child_shapes = child_shapes def __str__(self): - return f"{self.label}: {self.probability:.3f}, {len(self.shape)}" + repr = f"{self.label}: {self.probability:.3f}, {len(self.shape)}" + if self.child_shapes is not None: + repr += f", {len(self.child_shapes)}" + return repr + + def __repr__(self): + return self.__str__() class ImageResultWithSoftPrediction(Result): diff --git a/src/python/model_api/models/segmentation.py b/src/python/model_api/models/segmentation.py index 24d58fb7..727428b6 100644 --- a/src/python/model_api/models/segmentation.py +++ b/src/python/model_api/models/segmentation.py @@ -186,7 +186,7 @@ def postprocess(self, outputs: dict, meta: dict) -> ImageResultWithSoftPredictio def get_contours( self, prediction: ImageResultWithSoftPrediction, - ) -> list: + ) -> list[Contour]: n_layers = prediction.soft_prediction.shape[2] if n_layers == 1: @@ -207,13 +207,25 @@ def get_contours( obj_group = prediction.resultImage == layer_index label_index_map = obj_group.astype(np.uint8) * 255 - contours, _hierarchy = cv2.findContours( + contours, hierarchy = cv2.findContours( label_index_map, - cv2.RETR_EXTERNAL, + cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE, ) + hierarchy = hierarchy.squeeze() - for contour in contours: + for i, contour in enumerate(contours): + children = None + + if hierarchy[i][3] >= 0: + continue + + children = [] + if hierarchy[i][2] >= 0: + child_next_idx = hierarchy[i][2] + while child_next_idx >= 0: + children.append(contours[child_next_idx]) + child_next_idx = hierarchy[child_next_idx][0] mask = np.zeros(prediction.resultImage.shape, dtype=np.uint8) cv2.drawContours( mask, @@ -223,7 +235,7 @@ def get_contours( thickness=-1, ) probability = cv2.mean(current_label_soft_prediction, mask)[0] - combined_contours.append(Contour(label, probability, contour)) + combined_contours.append(Contour(label, probability, contour, children)) return combined_contours From 2304c38ce9d7b253b9afbe430b14c7cc7004327b Mon Sep 17 00:00:00 2001 From: Vladisalv Sovrasov Date: Fri, 25 Jul 2025 20:37:54 +0900 Subject: [PATCH 02/20] Fix logic in case of absence of nested contours --- src/python/model_api/models/segmentation.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/python/model_api/models/segmentation.py b/src/python/model_api/models/segmentation.py index 727428b6..3b4f13e4 100644 --- a/src/python/model_api/models/segmentation.py +++ b/src/python/model_api/models/segmentation.py @@ -215,8 +215,6 @@ def get_contours( hierarchy = hierarchy.squeeze() for i, contour in enumerate(contours): - children = None - if hierarchy[i][3] >= 0: continue @@ -226,6 +224,10 @@ def get_contours( while child_next_idx >= 0: children.append(contours[child_next_idx]) child_next_idx = hierarchy[child_next_idx][0] + + if not children: + children = None + mask = np.zeros(prediction.resultImage.shape, dtype=np.uint8) cv2.drawContours( mask, From 2b9b0a464e3413688ec0791cb7109d4233a1489d Mon Sep 17 00:00:00 2001 From: Vladisalv Sovrasov Date: Fri, 25 Jul 2025 21:05:08 +0900 Subject: [PATCH 03/20] Cleanup python impl --- src/python/model_api/models/segmentation.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/python/model_api/models/segmentation.py b/src/python/model_api/models/segmentation.py index 3b4f13e4..42874af9 100644 --- a/src/python/model_api/models/segmentation.py +++ b/src/python/model_api/models/segmentation.py @@ -219,11 +219,10 @@ def get_contours( continue children = [] - if hierarchy[i][2] >= 0: - child_next_idx = hierarchy[i][2] - while child_next_idx >= 0: - children.append(contours[child_next_idx]) - child_next_idx = hierarchy[child_next_idx][0] + next_child_idx = hierarchy[i][2] + while next_child_idx >= 0: + children.append(contours[next_child_idx]) + next_child_idx = hierarchy[next_child_idx][0] if not children: children = None From 93b514d5707ce544e66e5067cedba3b3039ff773 Mon Sep 17 00:00:00 2001 From: Vladisalv Sovrasov Date: Fri, 25 Jul 2025 21:05:41 +0900 Subject: [PATCH 04/20] Add cpp implementation --- src/cpp/models/include/models/results.h | 5 +++-- src/cpp/models/src/segmentation_model.cpp | 18 +++++++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/cpp/models/include/models/results.h b/src/cpp/models/include/models/results.h index 1a648723..9aadb87b 100644 --- a/src/cpp/models/include/models/results.h +++ b/src/cpp/models/include/models/results.h @@ -315,10 +315,11 @@ struct Contour { std::string label; float probability; std::vector shape; + std::vector> child_shapes; friend std::ostream& operator<<(std::ostream& os, const Contour& contour) { return os << contour.label << ": " << std::fixed << std::setprecision(3) << contour.probability << ", " - << contour.shape.size(); + << contour.shape.size() << ", " << contour.child_shapes.size(); } }; @@ -332,7 +333,7 @@ static inline std::vector getContours(const std::vector SegmentationModel::getContours(const ImageResultWithSoftPre cv::Scalar(index, index, index), label_index_map); std::vector> contours; - cv::findContours(label_index_map, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + std::vector hierarchy; + cv::findContours(label_index_map, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_NONE); std::string label = getLabelName(index - 1); - for (unsigned int i = 0; i < contours.size(); i++) { + for (size_t i = 0; i < contours.size(); ++i) { + if (hierarchy[i][3] >= 0) { + continue; + } + + std::vector> children; + int next_child_idx = hierarchy[i][2]; + while (next_child_idx >= 0) { + children.push_back(contours[next_child_idx]); + next_child_idx = hierarchy[next_child_idx][0]; + } + cv::Mat mask = cv::Mat::zeros(imageResult.resultImage.rows, imageResult.resultImage.cols, imageResult.resultImage.type()); cv::drawContours(mask, contours, i, 255, -1); float probability = (float)cv::mean(current_label_soft_prediction, mask)[0]; - combined_contours.push_back({label, probability, contours[i]}); + combined_contours.push_back({label, probability, contours[i], children}); } } From 3726152ac98720fd24c4bc9b0f628df86a85ee16 Mon Sep 17 00:00:00 2001 From: Vladisalv Sovrasov Date: Fri, 25 Jul 2025 21:08:32 +0900 Subject: [PATCH 05/20] Unify contour str reprs --- src/python/model_api/models/result/segmentation.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/python/model_api/models/result/segmentation.py b/src/python/model_api/models/result/segmentation.py index 7babc47f..36c15060 100644 --- a/src/python/model_api/models/result/segmentation.py +++ b/src/python/model_api/models/result/segmentation.py @@ -156,10 +156,8 @@ def __init__(self, label: str, probability: float, shape: np.ndarray, child_shap self.child_shapes = child_shapes def __str__(self): - repr = f"{self.label}: {self.probability:.3f}, {len(self.shape)}" - if self.child_shapes is not None: - repr += f", {len(self.child_shapes)}" - return repr + num_children = len(self.child_shapes) if self.child_shapes is not None else 0 + return f"{self.label}: {self.probability:.3f}, {len(self.shape)}, {num_children}" def __repr__(self): return self.__str__() From e444834891fcf45cd3b22b2ae9c1cf7e96059c75 Mon Sep 17 00:00:00 2001 From: Vladisalv Sovrasov Date: Fri, 25 Jul 2025 21:24:45 +0900 Subject: [PATCH 06/20] Fix np warning --- src/python/model_api/tilers/instance_segmentation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python/model_api/tilers/instance_segmentation.py b/src/python/model_api/tilers/instance_segmentation.py index 13ec4a65..0021532c 100644 --- a/src/python/model_api/tilers/instance_segmentation.py +++ b/src/python/model_api/tilers/instance_segmentation.py @@ -123,7 +123,7 @@ def _merge_results(self, results, shape) -> InstanceSegmentationResult: labels = labels.astype(np.int32) resized_masks, label_names = [], [] for mask, box, label_idx in zip(masks, bboxes, labels): - label_names.append(self.model.labels[int(label_idx)]) + label_names.append(self.model.labels[int(label_idx.squeeze())]) resized_masks.append(_segm_postprocess(box, mask, *shape[:-1])) resized_masks = np.stack(resized_masks) if resized_masks else masks From 91e5fff145b20ad8cff3fe84aa38dd3ee0d4eea2 Mon Sep 17 00:00:00 2001 From: Vladisalv Sovrasov Date: Fri, 25 Jul 2025 21:25:22 +0900 Subject: [PATCH 07/20] Resolve some of corner-cases --- src/python/model_api/models/segmentation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/python/model_api/models/segmentation.py b/src/python/model_api/models/segmentation.py index 42874af9..b9fe7254 100644 --- a/src/python/model_api/models/segmentation.py +++ b/src/python/model_api/models/segmentation.py @@ -212,7 +212,8 @@ def get_contours( cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE, ) - hierarchy = hierarchy.squeeze() + if len(contours): + hierarchy = hierarchy.squeeze(axis=0) for i, contour in enumerate(contours): if hierarchy[i][3] >= 0: From 1a2ecf2f42c22cc8d81e449ee26bf3b2a0d2fab0 Mon Sep 17 00:00:00 2001 From: Vladisalv Sovrasov Date: Fri, 25 Jul 2025 21:25:36 +0900 Subject: [PATCH 08/20] Update refs --- tests/python/accuracy/public_scope.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/python/accuracy/public_scope.json b/tests/python/accuracy/public_scope.json index f198a02f..567b0839 100644 --- a/tests/python/accuracy/public_scope.json +++ b/tests/python/accuracy/public_scope.json @@ -285,7 +285,7 @@ { "image": "coco128/images/train2017/000000000074.jpg", "reference": [ - "0: 1.000, 1: 0.000, [426,640,3], [426,640,3], [1,600,1,1]; backpack: 0.505, 2, " + "0: 1.000, 1: 0.000, [426,640,3], [426,640,3], [1,600,1,1]; backpack: 0.505, 2, 0, " ] } ] @@ -313,7 +313,7 @@ { "image": "coco128/images/train2017/000000000074.jpg", "reference": [ - "1535, 585, 1662, 697, 2 (ellipse): 0.643, 9822, RotatedRect: 1598.500 641.500 111.000 109.000 90.000; 3091, 3097, 3105, 3112, 1 (rectangle): 0.483, 197, RotatedRect: 3097.500 3104.000 14.000 13.000 90.000; 2734, 60, 2867, 324, 1 (rectangle): 0.401, 30622, RotatedRect: 2800.000 188.500 255.000 132.000 90.000; 2; [1,1280,1,1]; ellipse: 0.643, 331; rectangle: 0.483, 48; rectangle: 0.401, 758; " + "1535, 585, 1662, 697, 2 (ellipse): 0.643, 9822, RotatedRect: 1598.500 641.500 111.000 109.000 90.000; 3091, 3097, 3105, 3112, 1 (rectangle): 0.483, 197, RotatedRect: 3097.500 3104.000 14.000 13.000 90.000; 2734, 60, 2867, 324, 1 (rectangle): 0.401, 30622, RotatedRect: 2800.000 188.500 255.000 132.000 90.000; 2; [1,1280,1,1]; ellipse: 0.643, 331, 0; rectangle: 0.483, 48, 0; rectangle: 0.401, 758, 0; " ] } ] @@ -427,7 +427,7 @@ { "image": "coco128/images/train2017/000000000074.jpg", "reference": [ - "0: 0.272, 1: 0.728, [3500,3500,5], [0], [0]; background: 1.404, 311, background: 1.397, 44, background: 1.371, 34, background: 1.377, 12, background: 1.356, 155, background: 1.345, 12, background: 1.183, 219, background: 1.524, 8, background: 1.533, 4, background: 1.519, 2, background: 1.524, 4, background: 1.530, 6, background: 1.537, 2, background: 1.514, 4, background: 1.519, 8, background: 1.529, 6, background: 1.550, 6, background: 1.558, 4, background: 1.520, 2, background: 1.529, 4, background: 1.532, 6, background: 1.535, 6, background: 1.530, 2, background: 1.529, 50, background: 1.528, 22, background: 1.527, 38, background: 1.451, 1476, background: 1.345, 2743, background: 1.609, 2987, background: 1.636, 29909, " + "0: 0.272, 1: 0.728, [3500,3500,5], [0], [0]; background: 1.404, 311, 0, background: 1.397, 44, 0, background: 1.371, 34, 0, background: 1.377, 12, 0, background: 1.356, 155, 0, background: 1.345, 12, 0, background: 1.183, 219, 0, background: 1.524, 8, 0, background: 1.533, 4, 0, background: 1.519, 2, 0, background: 1.524, 4, 0, background: 1.530, 6, 0, background: 1.537, 2, 0, background: 1.514, 4, 0, background: 1.519, 8, 0, background: 1.529, 6, 0, background: 1.550, 6, 0, background: 1.558, 4, 0, background: 1.520, 2, 0, background: 1.529, 4, 0, background: 1.532, 6, 0, background: 1.535, 6, 0, background: 1.530, 2, 0, background: 1.529, 50, 0, background: 1.528, 22, 0, background: 1.527, 38, 0, background: 1.451, 1476, 0, background: 1.345, 2743, 9, background: 1.609, 2987, 1, background: 1.398, 20, 0, background: 1.636, 29909, 8, " ] } ] From dde7416613a60784bdcdb16fd99400e0c34877dc Mon Sep 17 00:00:00 2001 From: Vladisalv Sovrasov Date: Wed, 23 Jul 2025 00:50:52 +0900 Subject: [PATCH 09/20] Update pre-commit config --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 63f95eaf..ca1cbce0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -30,8 +30,8 @@ repos: - id: mypy additional_dependencies: [types-PyYAML, types-setuptools] - - repo: https://github.com/pre-commit/mirrors-prettier - rev: v4.0.0-alpha.8 + - repo: https://github.com/rbubley/mirrors-prettier + rev: v3.6.2 hooks: - id: prettier From 67bfce5821f31265fcb46903e27c1969bfba1852 Mon Sep 17 00:00:00 2001 From: Vladisalv Sovrasov Date: Fri, 25 Jul 2025 21:41:38 +0900 Subject: [PATCH 10/20] Fix numpy issue --- src/python/model_api/models/segmentation.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/python/model_api/models/segmentation.py b/src/python/model_api/models/segmentation.py index b9fe7254..8fc2c89d 100644 --- a/src/python/model_api/models/segmentation.py +++ b/src/python/model_api/models/segmentation.py @@ -224,9 +224,6 @@ def get_contours( while next_child_idx >= 0: children.append(contours[next_child_idx]) next_child_idx = hierarchy[next_child_idx][0] - - if not children: - children = None mask = np.zeros(prediction.resultImage.shape, dtype=np.uint8) cv2.drawContours( From 3510428709792ad7853a20c904f62ad3e9aa14c7 Mon Sep 17 00:00:00 2001 From: Vladisalv Sovrasov Date: Tue, 29 Jul 2025 00:58:40 +0900 Subject: [PATCH 11/20] Update ref tests results --- tests/python/accuracy/public_scope.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/python/accuracy/public_scope.json b/tests/python/accuracy/public_scope.json index 567b0839..2d8db8ad 100644 --- a/tests/python/accuracy/public_scope.json +++ b/tests/python/accuracy/public_scope.json @@ -6,7 +6,7 @@ { "image": "coco128/images/train2017/000000000074.jpg", "reference": [ - "0: 0.537, 1: 0.463, [426,640,2], [0], [0]; object: 0.675, 508, object: 0.527, 65, object: 0.507, 18, object: 0.624, 144, object: 0.538, 67, object: 0.507, 15, object: 0.518, 41, object: 0.507, 8, object: 0.505, 14, object: 0.885, 2138, " + "0: 0.537, 1: 0.463, [426,640,2], [0], [0]; object: 0.675, 508, 0, object: 0.527, 65, 1, object: 0.507, 18, 0, object: 0.624, 144, 0, object: 0.538, 67, 0, object: 0.507, 15, 0, object: 0.518, 41, 0, object: 0.507, 8, 0, object: 0.505, 14, 0, object: 0.885, 2138, 2, " ] } ] @@ -18,7 +18,7 @@ { "image": "coco128/images/train2017/000000000074.jpg", "reference": [ - "0: 0.992, 1: 0.008, [426,640,2], [0], [0]; object: 0.555, 112, object: 0.506, 17, object: 0.555, 154, object: 0.511, 19, object: 0.514, 52, " + "0: 0.992, 1: 0.008, [426,640,2], [0], [0]; object: 0.555, 112, 0, object: 0.506, 17, 0, object: 0.555, 154, 0, object: 0.511, 19, 0, object: 0.514, 52, 0, " ] } ] @@ -30,7 +30,7 @@ { "image": "coco128/images/train2017/000000000074.jpg", "reference": [ - "0: 0.563, 1: 0.437, [426,640,2], [0], [0]; object: 0.520, 26, object: 0.530, 42, object: 0.501, 4, object: 0.507, 27, object: 0.503, 8, object: 0.502, 6, object: 0.505, 18, object: 0.504, 13, object: 0.524, 87, object: 0.521, 89, object: 0.757, 2706, " + "0: 0.563, 1: 0.437, [426,640,2], [0], [0]; object: 0.520, 26, 0, object: 0.530, 42, 0, object: 0.501, 4, 0, object: 0.507, 27, 0, object: 0.503, 8, 0, object: 0.502, 6, 0, object: 0.505, 18, 0, object: 0.504, 13, 0, object: 0.524, 87, 0, object: 0.521, 89, 0, object: 0.757, 2706, 2, " ] } ] @@ -42,7 +42,7 @@ { "image": "coco128/images/train2017/000000000074.jpg", "reference": [ - "0: 0.563, 1: 0.437, [426,640,2], [0], [0]; object: 0.520, 26, object: 0.530, 42, object: 0.501, 4, object: 0.507, 27, object: 0.503, 8, object: 0.502, 6, object: 0.505, 18, object: 0.504, 13, object: 0.524, 87, object: 0.521, 89, object: 0.757, 2706, " + "0: 0.563, 1: 0.437, [426,640,2], [0], [0]; object: 0.520, 26, 0, object: 0.530, 42, 0, object: 0.501, 4, 0, object: 0.507, 27, 0, object: 0.503, 8, 0, object: 0.502, 6, 0, object: 0.505, 18, 0, object: 0.504, 13, 0, object: 0.524, 87, 0, object: 0.521, 89, 0, object: 0.757, 2706, 2, " ] } ] @@ -55,7 +55,7 @@ { "image": "coco128/images/train2017/000000000074.jpg", "reference": [ - "0: 0.561, 1: 0.439, [426,640,2], [0], [0]; object: 0.519, 26, object: 0.531, 42, object: 0.502, 21, object: 0.505, 9, object: 0.501, 4, object: 0.509, 22, object: 0.524, 85, object: 0.520, 93, object: 0.754, 2564, " + "0: 0.561, 1: 0.439, [426,640,2], [0], [0]; object: 0.519, 26, 0, object: 0.531, 42, 0, object: 0.502, 21, 0, object: 0.505, 9, 0, object: 0.501, 4, 0, object: 0.509, 22, 0, object: 0.524, 85, 0, object: 0.520, 93, 0, object: 0.754, 2564, 3, " ] } ] @@ -67,7 +67,7 @@ { "image": "coco128/images/train2017/000000000074.jpg", "reference": [ - "0: 0.944, 1: 0.056, [426,640,2], [0], [0]; object: 0.505, 2, object: 0.518, 8, object: 0.512, 5, object: 0.506, 4, object: 0.526, 8, object: 0.529, 21, object: 0.513, 12, object: 0.535, 49, object: 0.505, 2, object: 0.512, 4, object: 0.547, 6, object: 0.511, 6, object: 0.503, 1, object: 0.539, 6, object: 0.543, 39, object: 0.529, 2, object: 0.516, 9, object: 0.565, 157, object: 0.524, 6, object: 0.528, 15, object: 0.521, 18, object: 0.502, 1, object: 0.537, 73, object: 0.513, 4, object: 0.524, 27, object: 0.513, 6, object: 0.538, 65, object: 0.501, 6, object: 0.504, 1, object: 0.507, 4, object: 0.502, 1, object: 0.518, 8, object: 0.530, 11, object: 0.502, 2, object: 0.516, 2, object: 0.506, 1, object: 0.567, 17, object: 0.502, 1, object: 0.512, 7, object: 0.538, 24, object: 0.507, 1, object: 0.534, 12, object: 0.537, 6, object: 0.519, 13, object: 0.505, 2, object: 0.517, 16, object: 0.505, 5, object: 0.506, 20, object: 0.508, 6, object: 0.519, 24, object: 0.507, 4, object: 0.506, 2, object: 0.511, 4, object: 0.556, 47, object: 0.510, 10, object: 0.500, 1, object: 0.504, 5, object: 0.501, 1, object: 0.510, 6, object: 0.549, 13, object: 0.509, 2, object: 0.510, 3, object: 0.514, 1, object: 0.529, 15, object: 0.551, 110, object: 0.504, 2, object: 0.503, 3, object: 0.518, 16, object: 0.511, 14, object: 0.502, 1, object: 0.523, 1, object: 0.533, 16, object: 0.568, 66, object: 0.582, 1793, " + "0: 0.944, 1: 0.056, [426,640,2], [0], [0]; object: 0.505, 2, 0, object: 0.518, 8, 0, object: 0.512, 5, 0, object: 0.506, 4, 0, object: 0.526, 8, 0, object: 0.529, 21, 0, object: 0.513, 12, 0, object: 0.536, 49, 0, object: 0.505, 2, 0, object: 0.512, 4, 0, object: 0.547, 6, 0, object: 0.511, 6, 0, object: 0.503, 1, 0, object: 0.539, 6, 0, object: 0.543, 39, 0, object: 0.529, 2, 0, object: 0.516, 9, 0, object: 0.565, 157, 4, object: 0.524, 6, 0, object: 0.528, 15, 0, object: 0.521, 18, 0, object: 0.503, 1, 0, object: 0.537, 73, 0, object: 0.513, 4, 0, object: 0.524, 27, 0, object: 0.513, 6, 0, object: 0.538, 65, 1, object: 0.501, 6, 0, object: 0.504, 1, 0, object: 0.507, 4, 0, object: 0.502, 1, 0, object: 0.518, 8, 0, object: 0.530, 11, 0, object: 0.502, 2, 0, object: 0.516, 2, 0, object: 0.506, 1, 0, object: 0.567, 17, 0, object: 0.502, 1, 0, object: 0.512, 7, 0, object: 0.538, 24, 0, object: 0.507, 1, 0, object: 0.534, 12, 0, object: 0.510, 5, 0, object: 0.537, 6, 0, object: 0.519, 13, 0, object: 0.505, 2, 0, object: 0.540, 6, 0, object: 0.517, 16, 0, object: 0.505, 5, 0, object: 0.506, 20, 0, object: 0.508, 6, 0, object: 0.519, 24, 0, object: 0.507, 4, 0, object: 0.506, 2, 0, object: 0.511, 4, 0, object: 0.556, 47, 0, object: 0.510, 10, 0, object: 0.500, 1, 0, object: 0.504, 5, 0, object: 0.501, 1, 0, object: 0.510, 6, 0, object: 0.549, 13, 0, object: 0.509, 2, 0, object: 0.510, 3, 0, object: 0.514, 1, 0, object: 0.529, 15, 0, object: 0.551, 110, 1, object: 0.504, 2, 0, object: 0.503, 3, 0, object: 0.518, 16, 0, object: 0.511, 14, 0, object: 0.502, 1, 0, object: 0.523, 1, 0, object: 0.533, 16, 0, object: 0.568, 65, 0, object: 0.582, 1793, 73, " ] } ] @@ -214,7 +214,7 @@ { "image": "coco128/images/train2017/000000000074.jpg", "reference": [ - "458, 106, 495, 150, 1 (bicycle): 0.818, 852, RotatedRect: 478.119 130.332 28.677 46.408 46.637; 0, 30, 178, 323, 2 (car): 0.753, 26728, RotatedRect: 79.739 177.262 251.785 156.656 87.397; 0; [0]; bicycle: 0.818, 139; car: 0.753, 622; " + "458, 106, 495, 150, 1 (bicycle): 0.818, 852, RotatedRect: 478.119 130.332 28.677 46.408 46.637; 0, 30, 178, 323, 2 (car): 0.753, 26728, RotatedRect: 79.739 177.262 251.785 156.656 87.397; 0; [0]; bicycle: 0.818, 139, 0; car: 0.753, 622, 0; " ] } ] @@ -226,7 +226,7 @@ { "image": "coco128/images/train2017/000000000074.jpg", "reference": [ - "458, 106, 495, 150, 1 (person): 0.818, 852, RotatedRect: 478.119 130.332 28.677 46.408 46.637; 0, 30, 178, 323, 2 (bicycle): 0.753, 26728, RotatedRect: 79.739 177.262 251.785 156.656 87.397; 0; [0]; person: 0.818, 139; bicycle: 0.753, 622; " + "458, 106, 495, 150, 1 (person): 0.818, 852, RotatedRect: 478.119 130.332 28.677 46.408 46.637; 0, 30, 178, 323, 2 (bicycle): 0.753, 26728, RotatedRect: 79.739 177.262 251.785 156.656 87.397; 0; [0]; person: 0.818, 139, 0; bicycle: 0.753, 622, 0; " ] } ] @@ -239,7 +239,7 @@ { "image": "coco128/images/train2017/000000000074.jpg", "reference": [ - "458, 106, 495, 150, 1 (person): 0.816, 851, RotatedRect: 478.119 130.332 28.677 46.408 46.637; 0, 30, 178, 323, 2 (bicycle): 0.754, 26748, RotatedRect: 79.762 177.261 251.785 156.702 87.397; 0; [0]; person: 0.816, 142; bicycle: 0.754, 622; " + "458, 106, 495, 150, 1 (person): 0.816, 851, RotatedRect: 478.119 130.332 28.677 46.408 46.637; 0, 30, 178, 323, 2 (bicycle): 0.754, 26748, RotatedRect: 79.762 177.261 251.785 156.702 87.397; 0; [0]; person: 0.816, 142, 0; bicycle: 0.754, 622, 0; " ] } ] @@ -251,7 +251,7 @@ { "image": "coco128/images/train2017/000000000074.jpg", "reference": [ - "59, 277, 360, 380, 16 (horse): 0.999, 19053, RotatedRect: 210.000 327.500 101.000 296.000 90.000; 2, 9, 162, 318, 2 (car): 0.999, 31153, RotatedRect: 82.086 163.312 307.394 156.997 89.669; 294, 94, 316, 153, 1 (bicycle): 0.985, 840, RotatedRect: 305.000 123.500 59.000 18.000 90.000; 326, 97, 341, 136, 1 (bicycle): 0.974, 397, RotatedRect: 332.500 116.000 38.000 13.000 90.000; 461, 105, 493, 150, 1 (bicycle): 0.918, 846, RotatedRect: 476.052 126.972 27.619 47.834 16.928; 350, 92, 386, 149, 1 (bicycle): 0.807, 1458, RotatedRect: 369.319 119.891 54.848 34.230 82.405; 279, 110, 291, 146, 1 (bicycle): 0.788, 312, RotatedRect: 284.000 127.500 35.000 10.000 90.000; 0; [0]; horse: 0.999, 668; car: 0.999, 782; bicycle: 0.985, 127; bicycle: 0.974, 87; bicycle: 0.918, 122; bicycle: 0.807, 140; bicycle: 0.788, 79; " + "59, 277, 360, 380, 16 (horse): 0.999, 19053, RotatedRect: 210.000 327.500 101.000 296.000 90.000; 2, 9, 162, 318, 2 (car): 0.999, 31153, RotatedRect: 82.086 163.312 307.394 156.997 89.669; 294, 94, 316, 153, 1 (bicycle): 0.985, 840, RotatedRect: 305.000 123.500 59.000 18.000 90.000; 326, 97, 341, 136, 1 (bicycle): 0.974, 397, RotatedRect: 332.500 116.000 38.000 13.000 90.000; 461, 105, 493, 150, 1 (bicycle): 0.918, 846, RotatedRect: 476.052 126.972 27.619 47.834 16.928; 350, 92, 386, 149, 1 (bicycle): 0.807, 1458, RotatedRect: 369.319 119.891 54.848 34.230 82.405; 279, 110, 291, 146, 1 (bicycle): 0.788, 312, RotatedRect: 284.000 127.500 35.000 10.000 90.000; 0; [0]; horse: 0.999, 668, 0; car: 0.999, 782, 0; bicycle: 0.985, 127, 0; bicycle: 0.974, 87, 0; bicycle: 0.918, 122, 0; bicycle: 0.807, 140, 0; bicycle: 0.788, 79, 0; " ] } ] From cc6e3cf12df80bc182f5b884e91fa504e83630a4 Mon Sep 17 00:00:00 2001 From: Vladisalv Sovrasov Date: Tue, 29 Jul 2025 01:00:18 +0900 Subject: [PATCH 12/20] Retain old data type for Contour init args --- src/python/model_api/models/result/segmentation.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/python/model_api/models/result/segmentation.py b/src/python/model_api/models/result/segmentation.py index 36c15060..68428d73 100644 --- a/src/python/model_api/models/result/segmentation.py +++ b/src/python/model_api/models/result/segmentation.py @@ -149,7 +149,13 @@ def rotated_rects(self, value): class Contour: - def __init__(self, label: str, probability: float, shape: np.ndarray, child_shapes: list[np.ndarray] | None = None): + def __init__( + self, + label: str, + probability: float, + shape: np.ndarray | list[tuple[int, int]], + child_shapes: list[np.ndarray] | list[tuple[int, int]] | None = None, + ): self.shape = shape self.label = label self.probability = probability From 7d03560c145d9681a880864cb70083b96df2e16a Mon Sep 17 00:00:00 2001 From: Vladisalv Sovrasov Date: Tue, 29 Jul 2025 01:06:16 +0900 Subject: [PATCH 13/20] Exclude nested contours from contour confidence calculation --- src/python/model_api/models/segmentation.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/python/model_api/models/segmentation.py b/src/python/model_api/models/segmentation.py index 8fc2c89d..4d4faceb 100644 --- a/src/python/model_api/models/segmentation.py +++ b/src/python/model_api/models/segmentation.py @@ -233,6 +233,14 @@ def get_contours( color=1, thickness=-1, ) + for c in children: + cv2.drawContours( + mask, + np.asarray([c]), + contourIdx=-1, + color=0, + thickness=-1, + ) probability = cv2.mean(current_label_soft_prediction, mask)[0] combined_contours.append(Contour(label, probability, contour, children)) From 17f364df3b6c0b00a7f08b0d5ba323d67f09a4a2 Mon Sep 17 00:00:00 2001 From: Vladisalv Sovrasov Date: Tue, 29 Jul 2025 01:21:42 +0900 Subject: [PATCH 14/20] Exclude nested contours from prob computation --- src/cpp/models/src/segmentation_model.cpp | 10 ++++++---- .../model_api/models/result/segmentation.py | 8 ++++++++ src/python/model_api/models/segmentation.py | 16 ++++++++-------- tests/python/accuracy/public_scope.json | 8 ++++---- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/cpp/models/src/segmentation_model.cpp b/src/cpp/models/src/segmentation_model.cpp index 64a47ef4..b80d1961 100644 --- a/src/cpp/models/src/segmentation_model.cpp +++ b/src/cpp/models/src/segmentation_model.cpp @@ -308,17 +308,19 @@ std::vector SegmentationModel::getContours(const ImageResultWithSoftPre continue; } + cv::Mat mask = cv::Mat::zeros(imageResult.resultImage.rows, + imageResult.resultImage.cols, + imageResult.resultImage.type()); + cv::drawContours(mask, contours, i, 255, -1); + std::vector> children; int next_child_idx = hierarchy[i][2]; while (next_child_idx >= 0) { children.push_back(contours[next_child_idx]); + cv::drawContours(mask, contours, next_child_idx, 0, -1); next_child_idx = hierarchy[next_child_idx][0]; } - cv::Mat mask = cv::Mat::zeros(imageResult.resultImage.rows, - imageResult.resultImage.cols, - imageResult.resultImage.type()); - cv::drawContours(mask, contours, i, 255, -1); float probability = (float)cv::mean(current_label_soft_prediction, mask)[0]; combined_contours.push_back({label, probability, contours[i], children}); } diff --git a/src/python/model_api/models/result/segmentation.py b/src/python/model_api/models/result/segmentation.py index 68428d73..603ab904 100644 --- a/src/python/model_api/models/result/segmentation.py +++ b/src/python/model_api/models/result/segmentation.py @@ -149,6 +149,14 @@ def rotated_rects(self, value): class Contour: + """Represents a contour object with label, probability, shape, and optional excluded shapes. + Args: + label (str): The label of the contour. + probability (float): The probability associated with the contour. + shape (np.ndarray | list[tuple[int, int]]): The shape of the contour. + child_shapes (list[np.ndarray] | list[tuple[int, int]] | None, optional): Shapes of excluded contours. Defaults to None. + """ + def __init__( self, label: str, diff --git a/src/python/model_api/models/segmentation.py b/src/python/model_api/models/segmentation.py index 4d4faceb..3baba8a8 100644 --- a/src/python/model_api/models/segmentation.py +++ b/src/python/model_api/models/segmentation.py @@ -219,12 +219,6 @@ def get_contours( if hierarchy[i][3] >= 0: continue - children = [] - next_child_idx = hierarchy[i][2] - while next_child_idx >= 0: - children.append(contours[next_child_idx]) - next_child_idx = hierarchy[next_child_idx][0] - mask = np.zeros(prediction.resultImage.shape, dtype=np.uint8) cv2.drawContours( mask, @@ -233,14 +227,20 @@ def get_contours( color=1, thickness=-1, ) - for c in children: + + children = [] + next_child_idx = hierarchy[i][2] + while next_child_idx >= 0: + children.append(contours[next_child_idx]) cv2.drawContours( mask, - np.asarray([c]), + np.asarray([contours[next_child_idx]]), contourIdx=-1, color=0, thickness=-1, ) + next_child_idx = hierarchy[next_child_idx][0] + probability = cv2.mean(current_label_soft_prediction, mask)[0] combined_contours.append(Contour(label, probability, contour, children)) diff --git a/tests/python/accuracy/public_scope.json b/tests/python/accuracy/public_scope.json index 2d8db8ad..31afc17f 100644 --- a/tests/python/accuracy/public_scope.json +++ b/tests/python/accuracy/public_scope.json @@ -6,7 +6,7 @@ { "image": "coco128/images/train2017/000000000074.jpg", "reference": [ - "0: 0.537, 1: 0.463, [426,640,2], [0], [0]; object: 0.675, 508, 0, object: 0.527, 65, 1, object: 0.507, 18, 0, object: 0.624, 144, 0, object: 0.538, 67, 0, object: 0.507, 15, 0, object: 0.518, 41, 0, object: 0.507, 8, 0, object: 0.505, 14, 0, object: 0.885, 2138, 2, " + "0: 0.537, 1: 0.463, [426,640,2], [0], [0]; object: 0.675, 508, 0, object: 0.530, 65, 1, object: 0.507, 18, 0, object: 0.624, 144, 0, object: 0.538, 67, 0, object: 0.507, 15, 0, object: 0.518, 41, 0, object: 0.507, 8, 0, object: 0.505, 14, 0, object: 0.885, 2138, 2, " ] } ] @@ -55,7 +55,7 @@ { "image": "coco128/images/train2017/000000000074.jpg", "reference": [ - "0: 0.561, 1: 0.439, [426,640,2], [0], [0]; object: 0.519, 26, 0, object: 0.531, 42, 0, object: 0.502, 21, 0, object: 0.505, 9, 0, object: 0.501, 4, 0, object: 0.509, 22, 0, object: 0.524, 85, 0, object: 0.520, 93, 0, object: 0.754, 2564, 3, " + "0: 0.561, 1: 0.439, [426,640,2], [0], [0]; object: 0.519, 26, 0, object: 0.531, 42, 0, object: 0.502, 21, 0, object: 0.505, 9, 0, object: 0.501, 4, 0, object: 0.509, 22, 0, object: 0.524, 85, 0, object: 0.520, 93, 0, object: 0.757, 2564, 3, " ] } ] @@ -67,7 +67,7 @@ { "image": "coco128/images/train2017/000000000074.jpg", "reference": [ - "0: 0.944, 1: 0.056, [426,640,2], [0], [0]; object: 0.505, 2, 0, object: 0.518, 8, 0, object: 0.512, 5, 0, object: 0.506, 4, 0, object: 0.526, 8, 0, object: 0.529, 21, 0, object: 0.513, 12, 0, object: 0.536, 49, 0, object: 0.505, 2, 0, object: 0.512, 4, 0, object: 0.547, 6, 0, object: 0.511, 6, 0, object: 0.503, 1, 0, object: 0.539, 6, 0, object: 0.543, 39, 0, object: 0.529, 2, 0, object: 0.516, 9, 0, object: 0.565, 157, 4, object: 0.524, 6, 0, object: 0.528, 15, 0, object: 0.521, 18, 0, object: 0.503, 1, 0, object: 0.537, 73, 0, object: 0.513, 4, 0, object: 0.524, 27, 0, object: 0.513, 6, 0, object: 0.538, 65, 1, object: 0.501, 6, 0, object: 0.504, 1, 0, object: 0.507, 4, 0, object: 0.502, 1, 0, object: 0.518, 8, 0, object: 0.530, 11, 0, object: 0.502, 2, 0, object: 0.516, 2, 0, object: 0.506, 1, 0, object: 0.567, 17, 0, object: 0.502, 1, 0, object: 0.512, 7, 0, object: 0.538, 24, 0, object: 0.507, 1, 0, object: 0.534, 12, 0, object: 0.510, 5, 0, object: 0.537, 6, 0, object: 0.519, 13, 0, object: 0.505, 2, 0, object: 0.540, 6, 0, object: 0.517, 16, 0, object: 0.505, 5, 0, object: 0.506, 20, 0, object: 0.508, 6, 0, object: 0.519, 24, 0, object: 0.507, 4, 0, object: 0.506, 2, 0, object: 0.511, 4, 0, object: 0.556, 47, 0, object: 0.510, 10, 0, object: 0.500, 1, 0, object: 0.504, 5, 0, object: 0.501, 1, 0, object: 0.510, 6, 0, object: 0.549, 13, 0, object: 0.509, 2, 0, object: 0.510, 3, 0, object: 0.514, 1, 0, object: 0.529, 15, 0, object: 0.551, 110, 1, object: 0.504, 2, 0, object: 0.503, 3, 0, object: 0.518, 16, 0, object: 0.511, 14, 0, object: 0.502, 1, 0, object: 0.523, 1, 0, object: 0.533, 16, 0, object: 0.568, 65, 0, object: 0.582, 1793, 73, " + "0: 0.944, 1: 0.056, [426,640,2], [0], [0]; object: 0.505, 2, 0, object: 0.518, 8, 0, object: 0.512, 5, 0, object: 0.506, 4, 0, object: 0.526, 8, 0, object: 0.529, 21, 0, object: 0.513, 12, 0, object: 0.536, 49, 0, object: 0.505, 2, 0, object: 0.512, 4, 0, object: 0.547, 6, 0, object: 0.511, 6, 0, object: 0.503, 1, 0, object: 0.539, 6, 0, object: 0.543, 39, 0, object: 0.529, 2, 0, object: 0.516, 9, 0, object: 0.571, 157, 4, object: 0.524, 6, 0, object: 0.528, 15, 0, object: 0.521, 18, 0, object: 0.503, 1, 0, object: 0.537, 73, 0, object: 0.513, 4, 0, object: 0.524, 27, 0, object: 0.513, 6, 0, object: 0.539, 65, 1, object: 0.501, 6, 0, object: 0.504, 1, 0, object: 0.507, 4, 0, object: 0.502, 1, 0, object: 0.518, 8, 0, object: 0.530, 11, 0, object: 0.502, 2, 0, object: 0.516, 2, 0, object: 0.506, 1, 0, object: 0.567, 17, 0, object: 0.502, 1, 0, object: 0.512, 7, 0, object: 0.538, 24, 0, object: 0.507, 1, 0, object: 0.534, 12, 0, object: 0.510, 5, 0, object: 0.537, 6, 0, object: 0.519, 13, 0, object: 0.505, 2, 0, object: 0.540, 6, 0, object: 0.517, 16, 0, object: 0.505, 5, 0, object: 0.506, 20, 0, object: 0.508, 6, 0, object: 0.519, 24, 0, object: 0.507, 4, 0, object: 0.506, 2, 0, object: 0.511, 4, 0, object: 0.556, 47, 0, object: 0.510, 10, 0, object: 0.500, 1, 0, object: 0.504, 5, 0, object: 0.501, 1, 0, object: 0.510, 6, 0, object: 0.549, 13, 0, object: 0.509, 2, 0, object: 0.510, 3, 0, object: 0.514, 1, 0, object: 0.529, 15, 0, object: 0.554, 110, 1, object: 0.504, 2, 0, object: 0.503, 3, 0, object: 0.518, 16, 0, object: 0.511, 14, 0, object: 0.502, 1, 0, object: 0.523, 1, 0, object: 0.533, 16, 0, object: 0.568, 65, 0, object: 0.603, 1793, 73, " ] } ] @@ -427,7 +427,7 @@ { "image": "coco128/images/train2017/000000000074.jpg", "reference": [ - "0: 0.272, 1: 0.728, [3500,3500,5], [0], [0]; background: 1.404, 311, 0, background: 1.397, 44, 0, background: 1.371, 34, 0, background: 1.377, 12, 0, background: 1.356, 155, 0, background: 1.345, 12, 0, background: 1.183, 219, 0, background: 1.524, 8, 0, background: 1.533, 4, 0, background: 1.519, 2, 0, background: 1.524, 4, 0, background: 1.530, 6, 0, background: 1.537, 2, 0, background: 1.514, 4, 0, background: 1.519, 8, 0, background: 1.529, 6, 0, background: 1.550, 6, 0, background: 1.558, 4, 0, background: 1.520, 2, 0, background: 1.529, 4, 0, background: 1.532, 6, 0, background: 1.535, 6, 0, background: 1.530, 2, 0, background: 1.529, 50, 0, background: 1.528, 22, 0, background: 1.527, 38, 0, background: 1.451, 1476, 0, background: 1.345, 2743, 9, background: 1.609, 2987, 1, background: 1.398, 20, 0, background: 1.636, 29909, 8, " + "0: 0.272, 1: 0.728, [3500,3500,5], [0], [0]; background: 1.404, 311, 0, background: 1.397, 44, 0, background: 1.371, 34, 0, background: 1.377, 12, 0, background: 1.356, 155, 0, background: 1.345, 12, 0, background: 1.183, 219, 0, background: 1.524, 8, 0, background: 1.533, 4, 0, background: 1.519, 2, 0, background: 1.524, 4, 0, background: 1.530, 6, 0, background: 1.537, 2, 0, background: 1.514, 4, 0, background: 1.519, 8, 0, background: 1.529, 6, 0, background: 1.550, 6, 0, background: 1.558, 4, 0, background: 1.520, 2, 0, background: 1.529, 4, 0, background: 1.532, 6, 0, background: 1.535, 6, 0, background: 1.530, 2, 0, background: 1.529, 50, 0, background: 1.528, 22, 0, background: 1.527, 38, 0, background: 1.451, 1476, 0, background: 1.344, 2743, 9, background: 1.609, 2987, 1, background: 1.398, 20, 0, background: 1.644, 29909, 8, " ] } ] From c7124a4237e8f430c77472db927350de8dca4d70 Mon Sep 17 00:00:00 2001 From: Vladisalv Sovrasov Date: Tue, 29 Jul 2025 01:44:05 +0900 Subject: [PATCH 15/20] Rename child_shapes for clarity --- src/cpp/models/include/models/results.h | 4 ++-- .../model_api/models/result/segmentation.py | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/cpp/models/include/models/results.h b/src/cpp/models/include/models/results.h index 9aadb87b..24f662fa 100644 --- a/src/cpp/models/include/models/results.h +++ b/src/cpp/models/include/models/results.h @@ -315,11 +315,11 @@ struct Contour { std::string label; float probability; std::vector shape; - std::vector> child_shapes; + std::vector> excluded_shapes; friend std::ostream& operator<<(std::ostream& os, const Contour& contour) { return os << contour.label << ": " << std::fixed << std::setprecision(3) << contour.probability << ", " - << contour.shape.size() << ", " << contour.child_shapes.size(); + << contour.shape.size() << ", " << contour.excluded_shapes.size(); } }; diff --git a/src/python/model_api/models/result/segmentation.py b/src/python/model_api/models/result/segmentation.py index 603ab904..a5144bd3 100644 --- a/src/python/model_api/models/result/segmentation.py +++ b/src/python/model_api/models/result/segmentation.py @@ -149,12 +149,15 @@ def rotated_rects(self, value): class Contour: - """Represents a contour object with label, probability, shape, and optional excluded shapes. + """Represents a semantic segmentation mask as internals of a contour with "holes". Args: label (str): The label of the contour. probability (float): The probability associated with the contour. - shape (np.ndarray | list[tuple[int, int]]): The shape of the contour. - child_shapes (list[np.ndarray] | list[tuple[int, int]] | None, optional): Shapes of excluded contours. Defaults to None. + shape (np.ndarray | list[tuple[int, int]]): The shape of the contour. Shape is represented as a + list of 2d points or an equivalent numpy array (N, 2). + excluded_shapes (list[np.ndarray] | list[tuple[int, int]] | None, optional): Shapes of excluded contours. + If empty, the main shape is simply connected. Otherwise, excluded_shapes + represent "holes". Defaults to None. """ def __init__( @@ -162,15 +165,15 @@ def __init__( label: str, probability: float, shape: np.ndarray | list[tuple[int, int]], - child_shapes: list[np.ndarray] | list[tuple[int, int]] | None = None, + excluded_shapes: list[np.ndarray] | list[tuple[int, int]] | None = None, ): self.shape = shape self.label = label self.probability = probability - self.child_shapes = child_shapes + self.excluded_shapes = excluded_shapes def __str__(self): - num_children = len(self.child_shapes) if self.child_shapes is not None else 0 + num_children = len(self.excluded_shapes) if self.excluded_shapes is not None else 0 return f"{self.label}: {self.probability:.3f}, {len(self.shape)}, {num_children}" def __repr__(self): From 77320a8a95dd0d2bab3567abd9e775a01173455c Mon Sep 17 00:00:00 2001 From: Vladisalv Sovrasov Date: Tue, 29 Jul 2025 01:50:47 +0900 Subject: [PATCH 16/20] Unify calls of drawContours --- src/python/model_api/models/segmentation.py | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/python/model_api/models/segmentation.py b/src/python/model_api/models/segmentation.py index 3baba8a8..bbafae95 100644 --- a/src/python/model_api/models/segmentation.py +++ b/src/python/model_api/models/segmentation.py @@ -220,25 +220,13 @@ def get_contours( continue mask = np.zeros(prediction.resultImage.shape, dtype=np.uint8) - cv2.drawContours( - mask, - np.asarray([contour]), - contourIdx=-1, - color=1, - thickness=-1, - ) + cv2.drawContours(mask, contours, contourIdx=i, color=1, thickness=-1) children = [] next_child_idx = hierarchy[i][2] while next_child_idx >= 0: children.append(contours[next_child_idx]) - cv2.drawContours( - mask, - np.asarray([contours[next_child_idx]]), - contourIdx=-1, - color=0, - thickness=-1, - ) + cv2.drawContours(mask, contours, contourIdx=next_child_idx, color=0, thickness=-1) next_child_idx = hierarchy[next_child_idx][0] probability = cv2.mean(current_label_soft_prediction, mask)[0] From 8094ff5b9c90c4e58b6ae0a1f60cd836c1f86381 Mon Sep 17 00:00:00 2001 From: Vladisalv Sovrasov Date: Tue, 29 Jul 2025 03:17:12 +0900 Subject: [PATCH 17/20] Tests debug --- .github/workflows/test_accuracy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_accuracy.yml b/.github/workflows/test_accuracy.yml index 74f16cee..b65af1ca 100644 --- a/.github/workflows/test_accuracy.yml +++ b/.github/workflows/test_accuracy.yml @@ -35,7 +35,7 @@ jobs: - name: Run Python Test run: | source venv/bin/activate - pytest --data=./data tests/python/accuracy/test_accuracy.py + pytest -v --data=./data tests/python/accuracy/test_accuracy.py - name: Install CPP dependencies run: | sudo bash src/cpp/install_dependencies.sh From ca6568e3476f91b5b24eb05abe27021224269bb7 Mon Sep 17 00:00:00 2001 From: Vladisalv Sovrasov Date: Tue, 29 Jul 2025 03:33:00 +0900 Subject: [PATCH 18/20] Update ref results --- tests/python/accuracy/public_scope.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python/accuracy/public_scope.json b/tests/python/accuracy/public_scope.json index 31afc17f..3ff8d0e1 100644 --- a/tests/python/accuracy/public_scope.json +++ b/tests/python/accuracy/public_scope.json @@ -67,7 +67,7 @@ { "image": "coco128/images/train2017/000000000074.jpg", "reference": [ - "0: 0.944, 1: 0.056, [426,640,2], [0], [0]; object: 0.505, 2, 0, object: 0.518, 8, 0, object: 0.512, 5, 0, object: 0.506, 4, 0, object: 0.526, 8, 0, object: 0.529, 21, 0, object: 0.513, 12, 0, object: 0.536, 49, 0, object: 0.505, 2, 0, object: 0.512, 4, 0, object: 0.547, 6, 0, object: 0.511, 6, 0, object: 0.503, 1, 0, object: 0.539, 6, 0, object: 0.543, 39, 0, object: 0.529, 2, 0, object: 0.516, 9, 0, object: 0.571, 157, 4, object: 0.524, 6, 0, object: 0.528, 15, 0, object: 0.521, 18, 0, object: 0.503, 1, 0, object: 0.537, 73, 0, object: 0.513, 4, 0, object: 0.524, 27, 0, object: 0.513, 6, 0, object: 0.539, 65, 1, object: 0.501, 6, 0, object: 0.504, 1, 0, object: 0.507, 4, 0, object: 0.502, 1, 0, object: 0.518, 8, 0, object: 0.530, 11, 0, object: 0.502, 2, 0, object: 0.516, 2, 0, object: 0.506, 1, 0, object: 0.567, 17, 0, object: 0.502, 1, 0, object: 0.512, 7, 0, object: 0.538, 24, 0, object: 0.507, 1, 0, object: 0.534, 12, 0, object: 0.510, 5, 0, object: 0.537, 6, 0, object: 0.519, 13, 0, object: 0.505, 2, 0, object: 0.540, 6, 0, object: 0.517, 16, 0, object: 0.505, 5, 0, object: 0.506, 20, 0, object: 0.508, 6, 0, object: 0.519, 24, 0, object: 0.507, 4, 0, object: 0.506, 2, 0, object: 0.511, 4, 0, object: 0.556, 47, 0, object: 0.510, 10, 0, object: 0.500, 1, 0, object: 0.504, 5, 0, object: 0.501, 1, 0, object: 0.510, 6, 0, object: 0.549, 13, 0, object: 0.509, 2, 0, object: 0.510, 3, 0, object: 0.514, 1, 0, object: 0.529, 15, 0, object: 0.554, 110, 1, object: 0.504, 2, 0, object: 0.503, 3, 0, object: 0.518, 16, 0, object: 0.511, 14, 0, object: 0.502, 1, 0, object: 0.523, 1, 0, object: 0.533, 16, 0, object: 0.568, 65, 0, object: 0.603, 1793, 73, " + "0: 0.944, 1: 0.056, [426,640,2], [0], [0]; object: 0.505, 2, 0, object: 0.518, 8, 0, object: 0.512, 5, 0, object: 0.506, 4, 0, object: 0.526, 8, 0, object: 0.529, 21, 0, object: 0.513, 12, 0, object: 0.535, 49, 0, object: 0.505, 2, 0, object: 0.512, 4, 0, object: 0.547, 6, 0, object: 0.511, 6, 0, object: 0.503, 1, 0, object: 0.539, 6, 0, object: 0.543, 39, 0, object: 0.529, 2, 0, object: 0.516, 9, 0, object: 0.571, 157, 4, object: 0.524, 6, 0, object: 0.528, 15, 0, object: 0.521, 18, 0, object: 0.502, 1, 0, object: 0.537, 73, 0, object: 0.513, 4, 0, object: 0.524, 27, 0, object: 0.513, 6, 0, object: 0.539, 65, 1, object: 0.501, 6, 0, object: 0.504, 1, 0, object: 0.507, 4, 0, object: 0.502, 1, 0, object: 0.518, 8, 0, object: 0.530, 11, 0, object: 0.502, 2, 0, object: 0.516, 2, 0, object: 0.506, 1, 0, object: 0.567, 17, 0, object: 0.502, 1, 0, object: 0.512, 7, 0, object: 0.538, 24, 0, object: 0.507, 1, 0, object: 0.534, 12, 0, object: 0.510, 5, 0, object: 0.537, 6, 0, object: 0.519, 13, 0, object: 0.505, 2, 0, object: 0.540, 6, 0, object: 0.517, 16, 0, object: 0.505, 5, 0, object: 0.506, 20, 0, object: 0.508, 6, 0, object: 0.519, 24, 0, object: 0.507, 4, 0, object: 0.506, 2, 0, object: 0.511, 4, 0, object: 0.556, 47, 0, object: 0.510, 10, 0, object: 0.500, 1, 0, object: 0.504, 5, 0, object: 0.501, 1, 0, object: 0.510, 6, 0, object: 0.549, 13, 0, object: 0.509, 2, 0, object: 0.510, 3, 0, object: 0.514, 1, 0, object: 0.529, 15, 0, object: 0.554, 110, 1, object: 0.504, 2, 0, object: 0.503, 3, 0, object: 0.518, 16, 0, object: 0.511, 14, 0, object: 0.502, 1, 0, object: 0.523, 1, 0, object: 0.533, 16, 0, object: 0.568, 66, 0, object: 0.603, 1793, 73, " ] } ] From 410f3883bf0953ed0030b8941c887eae942ed33d Mon Sep 17 00:00:00 2001 From: Vladisalv Sovrasov Date: Wed, 30 Jul 2025 00:22:53 +0900 Subject: [PATCH 19/20] Add numpy conversion to shape --- .../model_api/models/result/segmentation.py | 4 ++-- tests/python/unit/results/test_sseg_result.py | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 tests/python/unit/results/test_sseg_result.py diff --git a/src/python/model_api/models/result/segmentation.py b/src/python/model_api/models/result/segmentation.py index a5144bd3..38830f39 100644 --- a/src/python/model_api/models/result/segmentation.py +++ b/src/python/model_api/models/result/segmentation.py @@ -167,10 +167,10 @@ def __init__( shape: np.ndarray | list[tuple[int, int]], excluded_shapes: list[np.ndarray] | list[tuple[int, int]] | None = None, ): - self.shape = shape + self.shape = np.array(shape) self.label = label self.probability = probability - self.excluded_shapes = excluded_shapes + self.excluded_shapes = np.array(excluded_shapes) if excluded_shapes is not None else None def __str__(self): num_children = len(self.excluded_shapes) if self.excluded_shapes is not None else 0 diff --git a/tests/python/unit/results/test_sseg_result.py b/tests/python/unit/results/test_sseg_result.py new file mode 100644 index 00000000..b1481704 --- /dev/null +++ b/tests/python/unit/results/test_sseg_result.py @@ -0,0 +1,22 @@ +# +# Copyright (C) 2025 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +# + +import numpy as np +from model_api.models.result import Contour + + +def test_contour_type(): + contour = Contour( + shape=[(100, 100)], + label=1, + probability=0.9, + excluded_shapes=[(50, 50), (60, 60)], + ) + + assert isinstance(contour.shape, np.ndarray) + assert isinstance(contour.excluded_shapes, np.ndarray) + assert contour.label == 1 + assert contour.probability == 0.9 + assert np.array_equal(contour.excluded_shapes, np.array([(50, 50), (60, 60)])) From f0e812a743a1efe2d8ec15a2ca6a9c151935039b Mon Sep 17 00:00:00 2001 From: Vladisalv Sovrasov Date: Wed, 30 Jul 2025 00:58:31 +0900 Subject: [PATCH 20/20] Fix list of contours conversion --- src/python/model_api/models/result/segmentation.py | 4 ++-- tests/python/unit/results/test_sseg_result.py | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/python/model_api/models/result/segmentation.py b/src/python/model_api/models/result/segmentation.py index 38830f39..cad6b8a3 100644 --- a/src/python/model_api/models/result/segmentation.py +++ b/src/python/model_api/models/result/segmentation.py @@ -165,12 +165,12 @@ def __init__( label: str, probability: float, shape: np.ndarray | list[tuple[int, int]], - excluded_shapes: list[np.ndarray] | list[tuple[int, int]] | None = None, + excluded_shapes: list[np.ndarray] | list[list[tuple[int, int]]] | None = None, ): self.shape = np.array(shape) self.label = label self.probability = probability - self.excluded_shapes = np.array(excluded_shapes) if excluded_shapes is not None else None + self.excluded_shapes = [np.array(x) for x in excluded_shapes] if excluded_shapes is not None else None def __str__(self): num_children = len(self.excluded_shapes) if self.excluded_shapes is not None else 0 diff --git a/tests/python/unit/results/test_sseg_result.py b/tests/python/unit/results/test_sseg_result.py index b1481704..26d0f83d 100644 --- a/tests/python/unit/results/test_sseg_result.py +++ b/tests/python/unit/results/test_sseg_result.py @@ -12,11 +12,12 @@ def test_contour_type(): shape=[(100, 100)], label=1, probability=0.9, - excluded_shapes=[(50, 50), (60, 60)], + excluded_shapes=[[(50, 50)], [(60, 60)]], ) assert isinstance(contour.shape, np.ndarray) - assert isinstance(contour.excluded_shapes, np.ndarray) + assert isinstance(contour.excluded_shapes, list) + assert isinstance(contour.excluded_shapes[0], np.ndarray) assert contour.label == 1 assert contour.probability == 0.9 - assert np.array_equal(contour.excluded_shapes, np.array([(50, 50), (60, 60)])) + assert np.array_equal(contour.excluded_shapes, np.array([[(50, 50)], [(60, 60)]]))