supervisely 6.73.238__py3-none-any.whl → 6.73.240__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 supervisely might be problematic. Click here for more details.
- supervisely/annotation/annotation.py +2 -2
- supervisely/api/entity_annotation/tag_api.py +11 -4
- supervisely/api/file_api.py +17 -3
- supervisely/nn/__init__.py +1 -0
- supervisely/nn/benchmark/__init__.py +14 -2
- supervisely/nn/benchmark/base_benchmark.py +84 -37
- supervisely/nn/benchmark/base_evaluator.py +120 -0
- supervisely/nn/benchmark/base_visualizer.py +265 -0
- supervisely/nn/benchmark/comparison/detection_visualization/text_templates.py +5 -5
- supervisely/nn/benchmark/comparison/detection_visualization/vis_metrics/calibration_score.py +2 -2
- supervisely/nn/benchmark/comparison/detection_visualization/vis_metrics/explore_predicttions.py +39 -16
- supervisely/nn/benchmark/comparison/detection_visualization/vis_metrics/localization_accuracy.py +1 -1
- supervisely/nn/benchmark/comparison/detection_visualization/vis_metrics/outcome_counts.py +4 -4
- supervisely/nn/benchmark/comparison/detection_visualization/vis_metrics/overview.py +12 -11
- supervisely/nn/benchmark/comparison/detection_visualization/vis_metrics/pr_curve.py +1 -1
- supervisely/nn/benchmark/comparison/detection_visualization/vis_metrics/precision_recal_f1.py +6 -6
- supervisely/nn/benchmark/comparison/detection_visualization/vis_metrics/speedtest.py +3 -3
- supervisely/nn/benchmark/{instance_segmentation_benchmark.py → instance_segmentation/benchmark.py} +9 -3
- supervisely/nn/benchmark/instance_segmentation/evaluator.py +58 -0
- supervisely/nn/benchmark/{visualization/text_templates/instance_segmentation_text.py → instance_segmentation/text_templates.py} +53 -69
- supervisely/nn/benchmark/instance_segmentation/visualizer.py +18 -0
- supervisely/nn/benchmark/object_detection/__init__.py +0 -0
- supervisely/nn/benchmark/object_detection/base_vis_metric.py +51 -0
- supervisely/nn/benchmark/{object_detection_benchmark.py → object_detection/benchmark.py} +4 -2
- supervisely/nn/benchmark/object_detection/evaluation_params.yaml +2 -0
- supervisely/nn/benchmark/{evaluation/object_detection_evaluator.py → object_detection/evaluator.py} +67 -9
- supervisely/nn/benchmark/{evaluation/coco → object_detection}/metric_provider.py +13 -14
- supervisely/nn/benchmark/{visualization/text_templates/object_detection_text.py → object_detection/text_templates.py} +49 -41
- supervisely/nn/benchmark/object_detection/vis_metrics/__init__.py +48 -0
- supervisely/nn/benchmark/{visualization → object_detection}/vis_metrics/confidence_distribution.py +20 -24
- supervisely/nn/benchmark/object_detection/vis_metrics/confidence_score.py +119 -0
- supervisely/nn/benchmark/{visualization → object_detection}/vis_metrics/confusion_matrix.py +34 -22
- supervisely/nn/benchmark/object_detection/vis_metrics/explore_predictions.py +129 -0
- supervisely/nn/benchmark/{visualization → object_detection}/vis_metrics/f1_score_at_different_iou.py +21 -26
- supervisely/nn/benchmark/object_detection/vis_metrics/frequently_confused.py +137 -0
- supervisely/nn/benchmark/object_detection/vis_metrics/iou_distribution.py +106 -0
- supervisely/nn/benchmark/object_detection/vis_metrics/key_metrics.py +136 -0
- supervisely/nn/benchmark/{visualization → object_detection}/vis_metrics/model_predictions.py +53 -49
- supervisely/nn/benchmark/object_detection/vis_metrics/outcome_counts.py +188 -0
- supervisely/nn/benchmark/object_detection/vis_metrics/outcome_counts_per_class.py +191 -0
- supervisely/nn/benchmark/object_detection/vis_metrics/overview.py +116 -0
- supervisely/nn/benchmark/object_detection/vis_metrics/pr_curve.py +106 -0
- supervisely/nn/benchmark/object_detection/vis_metrics/pr_curve_by_class.py +49 -0
- supervisely/nn/benchmark/object_detection/vis_metrics/precision.py +72 -0
- supervisely/nn/benchmark/object_detection/vis_metrics/precision_avg_per_class.py +59 -0
- supervisely/nn/benchmark/object_detection/vis_metrics/recall.py +71 -0
- supervisely/nn/benchmark/object_detection/vis_metrics/recall_vs_precision.py +56 -0
- supervisely/nn/benchmark/object_detection/vis_metrics/reliability_diagram.py +110 -0
- supervisely/nn/benchmark/object_detection/vis_metrics/speedtest.py +151 -0
- supervisely/nn/benchmark/object_detection/visualizer.py +697 -0
- supervisely/nn/benchmark/semantic_segmentation/__init__.py +9 -0
- supervisely/nn/benchmark/semantic_segmentation/base_vis_metric.py +55 -0
- supervisely/nn/benchmark/semantic_segmentation/benchmark.py +32 -0
- supervisely/nn/benchmark/semantic_segmentation/evaluation_params.yaml +0 -0
- supervisely/nn/benchmark/semantic_segmentation/evaluator.py +162 -0
- supervisely/nn/benchmark/semantic_segmentation/metric_provider.py +153 -0
- supervisely/nn/benchmark/semantic_segmentation/text_templates.py +130 -0
- supervisely/nn/benchmark/semantic_segmentation/vis_metrics/__init__.py +0 -0
- supervisely/nn/benchmark/semantic_segmentation/vis_metrics/acknowledgement.py +15 -0
- supervisely/nn/benchmark/semantic_segmentation/vis_metrics/classwise_error_analysis.py +57 -0
- supervisely/nn/benchmark/semantic_segmentation/vis_metrics/confusion_matrix.py +92 -0
- supervisely/nn/benchmark/semantic_segmentation/vis_metrics/explore_predictions.py +84 -0
- supervisely/nn/benchmark/semantic_segmentation/vis_metrics/frequently_confused.py +101 -0
- supervisely/nn/benchmark/semantic_segmentation/vis_metrics/iou_eou.py +45 -0
- supervisely/nn/benchmark/semantic_segmentation/vis_metrics/key_metrics.py +60 -0
- supervisely/nn/benchmark/semantic_segmentation/vis_metrics/model_predictions.py +107 -0
- supervisely/nn/benchmark/semantic_segmentation/vis_metrics/overview.py +112 -0
- supervisely/nn/benchmark/semantic_segmentation/vis_metrics/renormalized_error_ou.py +48 -0
- supervisely/nn/benchmark/semantic_segmentation/vis_metrics/speedtest.py +178 -0
- supervisely/nn/benchmark/semantic_segmentation/vis_metrics/vis_texts.py +21 -0
- supervisely/nn/benchmark/semantic_segmentation/visualizer.py +304 -0
- supervisely/nn/benchmark/utils/__init__.py +12 -0
- supervisely/nn/benchmark/utils/detection/__init__.py +2 -0
- supervisely/nn/benchmark/{evaluation/coco → utils/detection}/calculate_metrics.py +6 -4
- supervisely/nn/benchmark/utils/detection/metric_provider.py +533 -0
- supervisely/nn/benchmark/{coco_utils → utils/detection}/sly2coco.py +4 -4
- supervisely/nn/benchmark/{coco_utils/utils.py → utils/detection/utlis.py} +11 -0
- supervisely/nn/benchmark/utils/semantic_segmentation/__init__.py +0 -0
- supervisely/nn/benchmark/utils/semantic_segmentation/calculate_metrics.py +35 -0
- supervisely/nn/benchmark/utils/semantic_segmentation/evaluator.py +804 -0
- supervisely/nn/benchmark/utils/semantic_segmentation/loader.py +65 -0
- supervisely/nn/benchmark/utils/semantic_segmentation/utils.py +109 -0
- supervisely/nn/benchmark/visualization/evaluation_result.py +17 -3
- supervisely/nn/benchmark/visualization/vis_click_data.py +1 -1
- supervisely/nn/benchmark/visualization/widgets/__init__.py +3 -0
- supervisely/nn/benchmark/visualization/widgets/chart/chart.py +12 -4
- supervisely/nn/benchmark/visualization/widgets/gallery/gallery.py +35 -8
- supervisely/nn/benchmark/visualization/widgets/gallery/template.html +8 -4
- supervisely/nn/benchmark/visualization/widgets/markdown/markdown.py +1 -1
- supervisely/nn/benchmark/visualization/widgets/notification/notification.py +11 -7
- supervisely/nn/benchmark/visualization/widgets/radio_group/__init__.py +0 -0
- supervisely/nn/benchmark/visualization/widgets/radio_group/radio_group.py +34 -0
- supervisely/nn/benchmark/visualization/widgets/table/table.py +9 -3
- supervisely/nn/benchmark/visualization/widgets/widget.py +4 -0
- supervisely/project/project.py +18 -6
- {supervisely-6.73.238.dist-info → supervisely-6.73.240.dist-info}/METADATA +3 -1
- {supervisely-6.73.238.dist-info → supervisely-6.73.240.dist-info}/RECORD +104 -82
- supervisely/nn/benchmark/coco_utils/__init__.py +0 -2
- supervisely/nn/benchmark/evaluation/__init__.py +0 -3
- supervisely/nn/benchmark/evaluation/base_evaluator.py +0 -64
- supervisely/nn/benchmark/evaluation/coco/__init__.py +0 -2
- supervisely/nn/benchmark/evaluation/instance_segmentation_evaluator.py +0 -88
- supervisely/nn/benchmark/utils.py +0 -13
- supervisely/nn/benchmark/visualization/inference_speed/__init__.py +0 -19
- supervisely/nn/benchmark/visualization/inference_speed/speedtest_batch.py +0 -161
- supervisely/nn/benchmark/visualization/inference_speed/speedtest_intro.py +0 -28
- supervisely/nn/benchmark/visualization/inference_speed/speedtest_overview.py +0 -141
- supervisely/nn/benchmark/visualization/inference_speed/speedtest_real_time.py +0 -63
- supervisely/nn/benchmark/visualization/text_templates/inference_speed_text.py +0 -23
- supervisely/nn/benchmark/visualization/vis_metric_base.py +0 -337
- supervisely/nn/benchmark/visualization/vis_metrics/__init__.py +0 -67
- supervisely/nn/benchmark/visualization/vis_metrics/classwise_error_analysis.py +0 -55
- supervisely/nn/benchmark/visualization/vis_metrics/confidence_score.py +0 -93
- supervisely/nn/benchmark/visualization/vis_metrics/explorer_grid.py +0 -144
- supervisely/nn/benchmark/visualization/vis_metrics/frequently_confused.py +0 -115
- supervisely/nn/benchmark/visualization/vis_metrics/iou_distribution.py +0 -86
- supervisely/nn/benchmark/visualization/vis_metrics/outcome_counts.py +0 -119
- supervisely/nn/benchmark/visualization/vis_metrics/outcome_counts_per_class.py +0 -148
- supervisely/nn/benchmark/visualization/vis_metrics/overall_error_analysis.py +0 -109
- supervisely/nn/benchmark/visualization/vis_metrics/overview.py +0 -189
- supervisely/nn/benchmark/visualization/vis_metrics/percision_avg_per_class.py +0 -57
- supervisely/nn/benchmark/visualization/vis_metrics/pr_curve.py +0 -101
- supervisely/nn/benchmark/visualization/vis_metrics/pr_curve_by_class.py +0 -46
- supervisely/nn/benchmark/visualization/vis_metrics/precision.py +0 -56
- supervisely/nn/benchmark/visualization/vis_metrics/recall.py +0 -54
- supervisely/nn/benchmark/visualization/vis_metrics/recall_vs_precision.py +0 -57
- supervisely/nn/benchmark/visualization/vis_metrics/reliability_diagram.py +0 -88
- supervisely/nn/benchmark/visualization/vis_metrics/what_is.py +0 -23
- supervisely/nn/benchmark/visualization/vis_templates.py +0 -241
- supervisely/nn/benchmark/visualization/vis_widgets.py +0 -128
- supervisely/nn/benchmark/visualization/visualizer.py +0 -729
- /supervisely/nn/benchmark/{visualization/text_templates → instance_segmentation}/__init__.py +0 -0
- /supervisely/nn/benchmark/{evaluation/coco → instance_segmentation}/evaluation_params.yaml +0 -0
- /supervisely/nn/benchmark/{evaluation/coco → utils/detection}/metrics.py +0 -0
- {supervisely-6.73.238.dist-info → supervisely-6.73.240.dist-info}/LICENSE +0 -0
- {supervisely-6.73.238.dist-info → supervisely-6.73.240.dist-info}/WHEEL +0 -0
- {supervisely-6.73.238.dist-info → supervisely-6.73.240.dist-info}/entry_points.txt +0 -0
- {supervisely-6.73.238.dist-info → supervisely-6.73.240.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from supervisely.nn.benchmark.semantic_segmentation.benchmark import (
|
|
2
|
+
SemanticSegmentationBenchmark,
|
|
3
|
+
)
|
|
4
|
+
from supervisely.nn.benchmark.semantic_segmentation.evaluator import (
|
|
5
|
+
SemanticSegmentationEvaluator,
|
|
6
|
+
)
|
|
7
|
+
from supervisely.nn.benchmark.semantic_segmentation.visualizer import (
|
|
8
|
+
SemanticSegmentationVisualizer,
|
|
9
|
+
)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from typing import Dict, Optional
|
|
2
|
+
|
|
3
|
+
from supervisely.nn.benchmark.base_visualizer import BaseVisMetric
|
|
4
|
+
from supervisely.nn.benchmark.semantic_segmentation.evaluator import (
|
|
5
|
+
SemanticSegmentationEvalResult,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SemanticSegmVisMetric(BaseVisMetric):
|
|
10
|
+
|
|
11
|
+
def __init__(self, *args, **kwargs):
|
|
12
|
+
super().__init__(*args, **kwargs)
|
|
13
|
+
self.eval_result: SemanticSegmentationEvalResult
|
|
14
|
+
|
|
15
|
+
def get_click_data(self) -> Optional[Dict]:
|
|
16
|
+
if not self.clickable:
|
|
17
|
+
return
|
|
18
|
+
|
|
19
|
+
res = {}
|
|
20
|
+
|
|
21
|
+
res["layoutTemplate"] = [None, None, None]
|
|
22
|
+
res["clickData"] = {}
|
|
23
|
+
|
|
24
|
+
for key, v in self.eval_result.images_by_class.items():
|
|
25
|
+
res["clickData"][key] = {}
|
|
26
|
+
|
|
27
|
+
title = f"Class {key}: {len(v)} image{'s' if len(v) > 1 else ''}"
|
|
28
|
+
res["clickData"][key]["title"] = title
|
|
29
|
+
img_ids = [
|
|
30
|
+
self.eval_result.matched_pair_data[gt_img_id].pred_image_info.id for gt_img_id in v
|
|
31
|
+
]
|
|
32
|
+
res["clickData"][key]["imagesIds"] = img_ids
|
|
33
|
+
|
|
34
|
+
return res
|
|
35
|
+
|
|
36
|
+
def get_diff_data(self) -> Dict:
|
|
37
|
+
res = {}
|
|
38
|
+
|
|
39
|
+
res["layoutTemplate"] = [
|
|
40
|
+
{"columnTitle": "Original Image"},
|
|
41
|
+
{"columnTitle": "Ground Truth Masks"},
|
|
42
|
+
{"columnTitle": "Predicted Masks"},
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
click_data = res.setdefault("clickData", {})
|
|
46
|
+
|
|
47
|
+
for pairs_data in self.eval_result.matched_pair_data.values():
|
|
48
|
+
gt = pairs_data.gt_image_info
|
|
49
|
+
pred = pairs_data.pred_image_info
|
|
50
|
+
diff = pairs_data.diff_image_info
|
|
51
|
+
assert gt.name == pred.name == diff.name
|
|
52
|
+
key = click_data.setdefault(str(pred.id), {})
|
|
53
|
+
key["imagesIds"] = [diff.id, gt.id, pred.id]
|
|
54
|
+
key["title"] = f"Image: {pred.name}"
|
|
55
|
+
return res
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from supervisely.nn.benchmark.base_benchmark import BaseBenchmark
|
|
2
|
+
from supervisely.nn.benchmark.cv_tasks import CVTask
|
|
3
|
+
from supervisely.nn.benchmark.semantic_segmentation.evaluator import (
|
|
4
|
+
SemanticSegmentationEvaluator,
|
|
5
|
+
)
|
|
6
|
+
from supervisely.nn.benchmark.semantic_segmentation.visualizer import (
|
|
7
|
+
SemanticSegmentationVisualizer,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SemanticSegmentationBenchmark(BaseBenchmark):
|
|
12
|
+
visualizer_cls = SemanticSegmentationVisualizer
|
|
13
|
+
|
|
14
|
+
@property
|
|
15
|
+
def cv_task(self) -> str:
|
|
16
|
+
return CVTask.SEMANTIC_SEGMENTATION
|
|
17
|
+
|
|
18
|
+
def _get_evaluator_class(self) -> type:
|
|
19
|
+
return SemanticSegmentationEvaluator
|
|
20
|
+
|
|
21
|
+
def _evaluate(self, gt_project_path, pred_project_path):
|
|
22
|
+
eval_results_dir = self.get_eval_results_dir()
|
|
23
|
+
self.evaluator = self._get_evaluator_class()(
|
|
24
|
+
gt_project_path=gt_project_path,
|
|
25
|
+
pred_project_path=pred_project_path,
|
|
26
|
+
result_dir=eval_results_dir,
|
|
27
|
+
progress=self.pbar,
|
|
28
|
+
items_count=self.dt_project_info.items_count,
|
|
29
|
+
classes_whitelist=self.classes_whitelist,
|
|
30
|
+
evaluation_params=self.evaluation_params,
|
|
31
|
+
)
|
|
32
|
+
self.evaluator.evaluate()
|
|
File without changes
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import pickle
|
|
3
|
+
import shutil
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import List
|
|
6
|
+
|
|
7
|
+
import cv2
|
|
8
|
+
import numpy as np
|
|
9
|
+
from tqdm import tqdm
|
|
10
|
+
|
|
11
|
+
from supervisely.io.json import load_json_file
|
|
12
|
+
from supervisely.nn.benchmark.base_evaluator import BaseEvalResult, BaseEvaluator
|
|
13
|
+
from supervisely.nn.benchmark.semantic_segmentation.metric_provider import (
|
|
14
|
+
MetricProvider,
|
|
15
|
+
)
|
|
16
|
+
from supervisely.nn.benchmark.utils import (
|
|
17
|
+
calculate_semsegm_metrics as calculate_metrics,
|
|
18
|
+
)
|
|
19
|
+
from supervisely.project.project import Dataset, OpenMode, Project
|
|
20
|
+
from supervisely.project.project_meta import ProjectMeta
|
|
21
|
+
from supervisely.sly_logger import logger
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SemanticSegmentationEvalResult(BaseEvalResult):
|
|
25
|
+
mp_cls = MetricProvider
|
|
26
|
+
|
|
27
|
+
def _read_eval_data(self):
|
|
28
|
+
self.eval_data = pickle.load(open(Path(self.directory, "eval_data.pkl"), "rb"))
|
|
29
|
+
self.inference_info = load_json_file(Path(self.directory, "inference_info.json"))
|
|
30
|
+
speedtest_info_path = Path(self.directory).parent / "speedtest" / "speedtest.json"
|
|
31
|
+
self.speedtest_info = None
|
|
32
|
+
if speedtest_info_path.exists():
|
|
33
|
+
self.speedtest_info = load_json_file(speedtest_info_path)
|
|
34
|
+
|
|
35
|
+
self.mp = MetricProvider(self.eval_data)
|
|
36
|
+
# self.mp.calculate()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class SemanticSegmentationEvaluator(BaseEvaluator):
|
|
40
|
+
EVALUATION_PARAMS_YAML_PATH = f"{Path(__file__).parent}/evaluation_params.yaml"
|
|
41
|
+
eval_result_cls = SemanticSegmentationEvalResult
|
|
42
|
+
|
|
43
|
+
def __init__(self, *args, **kwargs):
|
|
44
|
+
super().__init__(*args, **kwargs)
|
|
45
|
+
self.bg_cls_name = None
|
|
46
|
+
self.bg_cls_color = None
|
|
47
|
+
|
|
48
|
+
def evaluate(self):
|
|
49
|
+
self.bg_cls_name = self._get_bg_class_name()
|
|
50
|
+
if self.bg_cls_name not in self.classes_whitelist:
|
|
51
|
+
self.classes_whitelist.append(self.bg_cls_name)
|
|
52
|
+
|
|
53
|
+
gt_prep_path, pred_prep_path = self.prepare_segmentation_data()
|
|
54
|
+
|
|
55
|
+
self.eval_data = calculate_metrics(
|
|
56
|
+
gt_dir=gt_prep_path,
|
|
57
|
+
pred_dir=pred_prep_path,
|
|
58
|
+
boundary_width=0.01,
|
|
59
|
+
boundary_iou_d=0.02,
|
|
60
|
+
num_workers=4,
|
|
61
|
+
class_names=self.classes_whitelist,
|
|
62
|
+
result_dir=self.result_dir,
|
|
63
|
+
progress=self.pbar,
|
|
64
|
+
)
|
|
65
|
+
self.eval_data["bg_cls_name"] = self.bg_cls_name
|
|
66
|
+
logger.info("Successfully calculated evaluation metrics")
|
|
67
|
+
self._dump_eval_results()
|
|
68
|
+
logger.info("Evaluation results are saved")
|
|
69
|
+
|
|
70
|
+
def _get_palette(self, project_path):
|
|
71
|
+
meta_path = Path(project_path) / "meta.json"
|
|
72
|
+
meta = ProjectMeta.from_json(load_json_file(meta_path))
|
|
73
|
+
|
|
74
|
+
palette = [obj.color for obj in meta.obj_classes if obj.name in self.classes_whitelist]
|
|
75
|
+
|
|
76
|
+
return palette
|
|
77
|
+
|
|
78
|
+
def _dump_eval_results(self):
|
|
79
|
+
eval_data_path = self._get_eval_data_path()
|
|
80
|
+
self._dump_pickle(self.eval_data, eval_data_path) # TODO: maybe dump JSON?
|
|
81
|
+
|
|
82
|
+
def _get_eval_data_path(self):
|
|
83
|
+
base_dir = self.result_dir
|
|
84
|
+
eval_data_path = os.path.join(base_dir, "eval_data.pkl")
|
|
85
|
+
return eval_data_path
|
|
86
|
+
|
|
87
|
+
def prepare_segmentation_data(self):
|
|
88
|
+
src_dirs = [self.gt_project_path, self.pred_project_path]
|
|
89
|
+
output_dirs = [
|
|
90
|
+
Path(self.gt_project_path).parent / "preprocessed_gt",
|
|
91
|
+
Path(self.pred_project_path).parent / "preprocessed_pred",
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
for src_dir, output_dir in zip(src_dirs, output_dirs):
|
|
95
|
+
if output_dir.exists():
|
|
96
|
+
logger.info(f"Preprocessed data already exists in {output_dir} directory")
|
|
97
|
+
continue
|
|
98
|
+
|
|
99
|
+
palette = self._get_palette(src_dir)
|
|
100
|
+
bg_color = palette[self.classes_whitelist.index(self.bg_cls_name)]
|
|
101
|
+
output_dir.mkdir(parents=True)
|
|
102
|
+
temp_seg_dir = src_dir + "_temp"
|
|
103
|
+
if not os.path.exists(temp_seg_dir):
|
|
104
|
+
Project.to_segmentation_task(
|
|
105
|
+
src_dir,
|
|
106
|
+
temp_seg_dir,
|
|
107
|
+
target_classes=self.classes_whitelist,
|
|
108
|
+
bg_name=self.bg_cls_name,
|
|
109
|
+
bg_color=bg_color,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
palette_lookup = np.zeros(256**3, dtype=np.int32)
|
|
113
|
+
for idx, color in enumerate(palette, 1):
|
|
114
|
+
key = (color[0] << 16) | (color[1] << 8) | color[2]
|
|
115
|
+
palette_lookup[key] = idx
|
|
116
|
+
|
|
117
|
+
temp_project = Project(temp_seg_dir, mode=OpenMode.READ)
|
|
118
|
+
temp_project.total_items
|
|
119
|
+
for dataset in temp_project.datasets:
|
|
120
|
+
dataset: Dataset
|
|
121
|
+
names = dataset.get_items_names()
|
|
122
|
+
for name in names:
|
|
123
|
+
mask_path = dataset.get_seg_path(name)
|
|
124
|
+
mask = cv2.imread(mask_path)[:, :, ::-1]
|
|
125
|
+
|
|
126
|
+
mask_keys = (
|
|
127
|
+
(mask[:, :, 0].astype(np.int32) << 16)
|
|
128
|
+
| (mask[:, :, 1].astype(np.int32) << 8)
|
|
129
|
+
| mask[:, :, 2].astype(np.int32)
|
|
130
|
+
)
|
|
131
|
+
result = palette_lookup[mask_keys]
|
|
132
|
+
if name.count(".png") > 1:
|
|
133
|
+
name = name[:-4]
|
|
134
|
+
cv2.imwrite(os.path.join(output_dir, name), result)
|
|
135
|
+
|
|
136
|
+
shutil.rmtree(temp_seg_dir)
|
|
137
|
+
|
|
138
|
+
return output_dirs
|
|
139
|
+
|
|
140
|
+
def _get_bg_class_name(self):
|
|
141
|
+
possible_names = ["background", "bg", "unlabeled", "neutral", "__bg__"]
|
|
142
|
+
logger.info(f"Searching for background class in projects. Possible names: {possible_names}")
|
|
143
|
+
|
|
144
|
+
bg_cls_names = []
|
|
145
|
+
for project_path in [self.gt_project_path, self.pred_project_path]:
|
|
146
|
+
meta_path = Path(project_path) / "meta.json"
|
|
147
|
+
meta = ProjectMeta.from_json(load_json_file(meta_path))
|
|
148
|
+
|
|
149
|
+
for obj_cls in meta.obj_classes:
|
|
150
|
+
if obj_cls.name in possible_names:
|
|
151
|
+
bg_cls_names.append(obj_cls.name)
|
|
152
|
+
break
|
|
153
|
+
|
|
154
|
+
if len(bg_cls_names) == 0:
|
|
155
|
+
raise ValueError("Background class not found in GT and Pred projects")
|
|
156
|
+
|
|
157
|
+
if len(set(bg_cls_names)) > 1:
|
|
158
|
+
raise ValueError(
|
|
159
|
+
f"Founds multiple background class names in GT and Pred projects: {set(bg_cls_names)}"
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
return bg_cls_names[0]
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
from typing import Any, Dict
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
METRIC_NAMES = {
|
|
6
|
+
"mPixel": "mPixel accuracy",
|
|
7
|
+
"mF1": "mF1-score",
|
|
8
|
+
"mPrecision": "mPrecision",
|
|
9
|
+
"mRecall": "mRecall",
|
|
10
|
+
"mIoU": "mIoU",
|
|
11
|
+
"mBoundaryIoU": "mBoundaryIoU",
|
|
12
|
+
"calibration_score": "Calibration Score",
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MetricProvider:
|
|
17
|
+
def __init__(self, eval_data: Dict[str, Any]):
|
|
18
|
+
# self.params = params
|
|
19
|
+
self.metric_names = METRIC_NAMES
|
|
20
|
+
|
|
21
|
+
# eval_data
|
|
22
|
+
self.eval_data = eval_data["result"]
|
|
23
|
+
self.bg_cls_name = eval_data["bg_cls_name"]
|
|
24
|
+
self.per_image_metrics = eval_data["per_image_metrics"]
|
|
25
|
+
self.cmat_cell_img_names = eval_data["cell_img_names"]
|
|
26
|
+
self.class_names = self.eval_data.index.tolist()
|
|
27
|
+
|
|
28
|
+
self.num_classes = len(self.class_names)
|
|
29
|
+
|
|
30
|
+
# base metrics
|
|
31
|
+
overall_TP = self.eval_data["TP"][: self.num_classes].sum()
|
|
32
|
+
overall_FN = self.eval_data["FN"][: self.num_classes].sum()
|
|
33
|
+
self.pixel_accuracy = overall_TP / (overall_TP + overall_FN)
|
|
34
|
+
self.overall_TP = overall_TP
|
|
35
|
+
self.overall_FN = overall_FN
|
|
36
|
+
self.precision = round(self.eval_data.loc["mean", "precision"] * 100, 1)
|
|
37
|
+
self.recall = round(self.eval_data.loc["mean", "recall"] * 100, 1)
|
|
38
|
+
self.f1_score = round(self.eval_data.loc["mean", "F1_score"] * 100, 1)
|
|
39
|
+
self.iou = round(self.eval_data.loc["mean", "IoU"] * 100, 1)
|
|
40
|
+
self.boundary_iou = round(self.eval_data.loc["mean", "boundary_IoU"] * 100, 1)
|
|
41
|
+
|
|
42
|
+
# error metrics
|
|
43
|
+
# labels = ["mIoU", "mBoundaryEoU", "mExtentEoU", "mSegmentEoU"]
|
|
44
|
+
self.boundary_eou = round(self.eval_data.loc["mean", "E_boundary_oU"] * 100, 1)
|
|
45
|
+
self.extent_eou = round(self.eval_data.loc["mean", "E_extent_oU"] * 100, 1)
|
|
46
|
+
self.segment_eou = round(self.eval_data.loc["mean", "E_segment_oU"] * 100, 1)
|
|
47
|
+
|
|
48
|
+
# renormalized error metrics
|
|
49
|
+
# labels = ["boundary", "extent", "segment"]
|
|
50
|
+
self.boundary_renormed_eou = round(
|
|
51
|
+
self.eval_data.loc["mean", "E_boundary_oU_renormed"] * 100, 1
|
|
52
|
+
)
|
|
53
|
+
self.extent_renormed_eou = round(
|
|
54
|
+
self.eval_data.loc["mean", "E_extent_oU_renormed"] * 100, 1
|
|
55
|
+
)
|
|
56
|
+
self.segment_renormed_eou = round(
|
|
57
|
+
self.eval_data.loc["mean", "E_segment_oU_renormed"] * 100, 1
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# classwise error data
|
|
61
|
+
self.classwise_segm_error_data = self.get_classwise_error_data()
|
|
62
|
+
|
|
63
|
+
# confusion matrix
|
|
64
|
+
self.confusion_matrix = self.get_confusion_matrix(eval_data["confusion_matrix"].copy())
|
|
65
|
+
|
|
66
|
+
# frequently confused classes
|
|
67
|
+
self.frequently_confused = self.get_frequently_confused(
|
|
68
|
+
eval_data["confusion_matrix"].copy()
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def json_metrics(self):
|
|
72
|
+
return {
|
|
73
|
+
"mIoU": self.eval_data.loc["mean"]["IoU"] * 100,
|
|
74
|
+
"mE_boundary_oU": self.eval_data.loc["mean"]["E_boundary_oU"] * 100,
|
|
75
|
+
"mFP_boundary_oU": self.eval_data.loc["mean"]["FP_boundary_oU"] * 100,
|
|
76
|
+
"mFN_boundary_oU": self.eval_data.loc["mean"]["FN_boundary_oU"] * 100,
|
|
77
|
+
"mE_boundary_oU_renormed": self.eval_data.loc["mean"]["E_boundary_oU_renormed"] * 100,
|
|
78
|
+
"mE_extent_oU": self.eval_data.loc["mean"]["E_extent_oU"] * 100,
|
|
79
|
+
"mFP_extent_oU": self.eval_data.loc["mean"]["FP_extent_oU"] * 100,
|
|
80
|
+
"mFN_extent_oU": self.eval_data.loc["mean"]["FN_extent_oU"] * 100,
|
|
81
|
+
"mE_extent_oU_renormed": self.eval_data.loc["mean"]["E_extent_oU_renormed"] * 100,
|
|
82
|
+
"mE_segment_oU": self.eval_data.loc["mean"]["E_segment_oU"] * 100,
|
|
83
|
+
"mFP_segment_oU": self.eval_data.loc["mean"]["FP_segment_oU"] * 100,
|
|
84
|
+
"mFN_segment_oU": self.eval_data.loc["mean"]["FN_segment_oU"] * 100,
|
|
85
|
+
"mE_segment_oU_renormed": self.eval_data.loc["mean"]["E_segment_oU_renormed"] * 100,
|
|
86
|
+
"mPrecision": self.eval_data.loc["mean"]["precision"] * 100,
|
|
87
|
+
"mRecall": self.eval_data.loc["mean"]["recall"] * 100,
|
|
88
|
+
"mF1_score": self.eval_data.loc["mean"]["F1_score"] * 100,
|
|
89
|
+
"PixelAcc": self.pixel_accuracy * 100,
|
|
90
|
+
"mBoundaryIoU": self.eval_data.loc["mean"]["boundary_IoU"] * 100,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
def metric_table(self):
|
|
94
|
+
names_map = {
|
|
95
|
+
"img_names": "Image name",
|
|
96
|
+
"pixel_acc": "Pixel accuracy",
|
|
97
|
+
"precision": "Precision",
|
|
98
|
+
"recall": "Recall",
|
|
99
|
+
"f1_score": "F1 score",
|
|
100
|
+
"iou": "IoU",
|
|
101
|
+
"boundary_iou": "Boundary IoU",
|
|
102
|
+
"boundary_eou": "Boundary EoU",
|
|
103
|
+
"extent_eou": "Extent EoU",
|
|
104
|
+
"segment_eou": "Segment EoU",
|
|
105
|
+
"boundary_eou_renormed": "Boundary EoU renormed",
|
|
106
|
+
"extent_eou_renormed": "Extent EoU renormed",
|
|
107
|
+
"segment_eou_renormed": "Segment EoU renormed",
|
|
108
|
+
}
|
|
109
|
+
prediction_table = self.per_image_metrics.rename(columns=names_map)
|
|
110
|
+
return prediction_table
|
|
111
|
+
|
|
112
|
+
def get_confusion_matrix(self, confusion_matrix: np.ndarray):
|
|
113
|
+
class_names = self.eval_data.index.tolist()
|
|
114
|
+
confusion_matrix = confusion_matrix[::-1]
|
|
115
|
+
return confusion_matrix, class_names
|
|
116
|
+
|
|
117
|
+
def get_frequently_confused(self, confusion_matrix: np.ndarray):
|
|
118
|
+
n_pairs = 20
|
|
119
|
+
|
|
120
|
+
non_diagonal_indexes = {}
|
|
121
|
+
for i, idx in enumerate(np.ndindex(confusion_matrix.shape)):
|
|
122
|
+
if idx[0] != idx[1]:
|
|
123
|
+
non_diagonal_indexes[i] = idx
|
|
124
|
+
|
|
125
|
+
indexes_1d = np.argsort(confusion_matrix, axis=None)
|
|
126
|
+
indexes_2d = [
|
|
127
|
+
non_diagonal_indexes[idx] for idx in indexes_1d if idx in non_diagonal_indexes
|
|
128
|
+
][-n_pairs:]
|
|
129
|
+
indexes_2d = np.asarray(indexes_2d[::-1])
|
|
130
|
+
|
|
131
|
+
rows = indexes_2d[:, 0]
|
|
132
|
+
cols = indexes_2d[:, 1]
|
|
133
|
+
probs = confusion_matrix[rows, cols]
|
|
134
|
+
return probs, indexes_2d
|
|
135
|
+
|
|
136
|
+
def key_metrics(self):
|
|
137
|
+
return {
|
|
138
|
+
"mPixel accuracy": self.pixel_accuracy,
|
|
139
|
+
"mPrecision": self.precision,
|
|
140
|
+
"mRecall": self.recall,
|
|
141
|
+
"mF1-score": self.f1_score,
|
|
142
|
+
"mIoU": self.iou,
|
|
143
|
+
"mBoundaryIoU": self.boundary_iou,
|
|
144
|
+
"mPixel accuracy": self.pixel_accuracy,
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
def get_classwise_error_data(self):
|
|
148
|
+
self.eval_data.drop(["mean"], inplace=True)
|
|
149
|
+
bar_data = self.eval_data.copy()
|
|
150
|
+
bar_data = bar_data[["IoU", "E_extent_oU", "E_boundary_oU", "E_segment_oU"]]
|
|
151
|
+
bar_data.sort_values(by="IoU", ascending=False, inplace=True)
|
|
152
|
+
labels = list(bar_data.index)
|
|
153
|
+
return bar_data, labels
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
from types import SimpleNamespace
|
|
2
|
+
|
|
3
|
+
docs_url = (
|
|
4
|
+
"https://docs.supervisely.com/neural-networks/model-evaluation-benchmark/semantic-segmentation"
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
clickable_label = """
|
|
8
|
+
> <span style="color: #5a6772">
|
|
9
|
+
> Click on the chart to explore corresponding images.
|
|
10
|
+
> </span>
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
markdown_header = """
|
|
14
|
+
<h1>{}</h1>
|
|
15
|
+
|
|
16
|
+
<div class="model-info-block">
|
|
17
|
+
<div>Created by <b>{}</b></div>
|
|
18
|
+
<div><i class="zmdi zmdi-calendar-alt"></i><span>{}</span></div>
|
|
19
|
+
</div>
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
markdown_overview = """
|
|
24
|
+
- **Model**: {}
|
|
25
|
+
- **Checkpoint**: {}
|
|
26
|
+
- **Architecture**: {}
|
|
27
|
+
- **Task type**: {}
|
|
28
|
+
- **Runtime**: {}
|
|
29
|
+
- **Checkpoint file**: <a class="checkpoint-url" href="{}" target="_blank">{}</a>
|
|
30
|
+
- **Ground Truth project**: <a href="/projects/{}/datasets" target="_blank">{}</a>, {}{}
|
|
31
|
+
{}
|
|
32
|
+
|
|
33
|
+
Learn more about Model Benchmark, implementation details, and how to use the charts in our <a href="{}" target="_blank">Technical Report</a>.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
markdown_key_metrics = """## Key Metrics
|
|
38
|
+
|
|
39
|
+
We provide a comprehensive model performance analysis using a set of metrics, including both basic (precision, recall, f1-score, IoU, etc.) and advanced (boundary IoU, error over unio decomposition, etc.) metrics.
|
|
40
|
+
|
|
41
|
+
- **Pixel accuracy**: reflects the percent of image pixels which were correctly classified.
|
|
42
|
+
- **Precision**: reflects the number of correctly predicted positive segmentations divided by the total number of predicted positive segmentations.
|
|
43
|
+
- **Recall8**: reflects the number of correctly predicted positive segmentations divided by the number of all samples that should have been segmented as positive.
|
|
44
|
+
- **F1-score**: reflects the tradeoff between precision and recall. It is equivalent to the Dice coefficient and calculated as a harmonic mean of precision and recall.
|
|
45
|
+
- **Intersection over union (IoU, also known as the Jaccard index)**: measures the overlap between ground truth mask and predicted mask. It is calculated as the ratio of the intersection of the two masks areas to their combined areas.
|
|
46
|
+
- **Boundary intersection over union**: a segmentation consistency measure that first computes the sets of ground truth and predicted masks pixels that are located within the distance d from each contour and then computes intersection over union of these two sets. Pixel distance parameter d (pixel width of the boundary region) controls the sensitivity of the metric, it is usually set as 2% of the image diagonal for normal resolution images and 0.5% of the image diagonal for high resolution images.
|
|
47
|
+
- **Error over union and its components (boundary, extent, segment)**: a metric opposite to intersection over union and can be interpreted as what the model lacked in order to show the perfect performance with IoU = 1. It reflects the ratio of incorrectly segmented pixels of ground truth and predicted masks to their combined areas. It is usually decomposed into boundary, extent and segment errors over union in order to get exhaustive information about the model's strengths and weaknesses.
|
|
48
|
+
- **Renormalized error over union**: postprocessed variant of error over union which takes into consideration cause and effect relationships between different types of segmentation errors.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
markdown_explorer = """## Explore Predictions
|
|
52
|
+
This section contains visual comparison of model predictions and ground truth masks. Sometimes a simple visualization can be more informative than any performance metric.
|
|
53
|
+
|
|
54
|
+
> Click on the image to view the **Original Image** with **Ground Truth** and **Prediction** annotations side-by-side.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
markdown_predictions_table = """### Prediction details for every image
|
|
59
|
+
|
|
60
|
+
Table of per image metrics allows to get performance metrics for every image. It can be helpful when there is a need to find the most problematic images where the model performed worst.
|
|
61
|
+
|
|
62
|
+
**Example**: you can sort by **Pixel accuracy** in ascending order to find images where the model performed worst in terms of pixel-wise accuracy.
|
|
63
|
+
|
|
64
|
+
> Click on the row to view the **Original Image** with **Ground Truth** and **Prediction** annotations side-by-side.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
markdown_iou = """## Intersection & Error Over Union
|
|
68
|
+
|
|
69
|
+
The pie chart below demonstrates what the model lacked in order to show the perfect performance with IoU = 1. It is done with the help of Error over Union (EoU) decomposition into boundary, extent and segment errors over union. This chart helps to draw conclusions on the model's strongest and weakest sides.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
markdown_renormalized_error_ou = """## Renormalized Error Over Union
|
|
74
|
+
|
|
75
|
+
The pie chart below is dedicated to decomposition of postprocessed variant of error over union which takes into consideration cause and effect relationships between different types of segmentation errors. Error over union decomposition has its own pitfalls. It is important to understand that models which tend to produce segment errors (when entire segments are mispredicted and there is no intersection between ground truth and predicted mask) will face less occasions to produce boundary and extent errors - as a result, boundary and extent error over union values will be underestimated.
|
|
76
|
+
|
|
77
|
+
In terms of localization, segment error is more fundamental than extent, while extent error is more fundamental than boundary. In order to overcome this problem, renormalized error over union proposes a slightly different calculation method - by removing more fundamental errors from the denominator - read more in our <a href="{}" target="_blank">technical report</a>
|
|
78
|
+
""".format(
|
|
79
|
+
docs_url
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
markdown_eou_per_class = """## Classwise Segmentation Error Analysis
|
|
84
|
+
|
|
85
|
+
This section contains information about classwise segmentation error decomposition. Each column of the chart represents a certain class from the training dataset, demonstrating model performance in terms of segmenting this specific class on images and what model lacked in order to show the perfect performance. All classes are sorted in IoU descending order.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
markdown_confusion_matrix = """## Confusion Matrix
|
|
89
|
+
|
|
90
|
+
The confusion matrix below reveals which classes the model commonly confuses with each other.
|
|
91
|
+
|
|
92
|
+
- **Each row** of the matrix corresponds to the actual instances of a class.
|
|
93
|
+
- **Each column** corresponds to the instances as predicted by the model.
|
|
94
|
+
- **The diagonal elements** of the matrix represent correctly predicted instances.
|
|
95
|
+
- By examining the **off-diagonal elements**, you can see if the model is confusing two classes by frequently mislabeling one as the other.
|
|
96
|
+
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
markdown_frequently_confused = """## Frequently Confused Classes
|
|
100
|
+
|
|
101
|
+
The bar chart below reveals pairs of classes which were most frequently confused by the model. Each column of the chart demonstrates the probability of confusion of a given pair of classes. It is necessary to remember that this probability is not symmetric: the probability of confusing class A with class B is not equal to the probability of confusing class B with class A.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
markdown_frequently_confused_empty = """### Frequently Confused Classes
|
|
105
|
+
|
|
106
|
+
No frequently confused class pairs found
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
markdown_speedtest_intro = """## Inference Speed
|
|
110
|
+
|
|
111
|
+
This is a speed test benchmark for this model. The model was tested with the following configuration:
|
|
112
|
+
|
|
113
|
+
- **Device**: {}
|
|
114
|
+
- **Hardware**: {}
|
|
115
|
+
- **Runtime**: {}
|
|
116
|
+
|
|
117
|
+
The table below shows the speed test results. For each test, the time taken to process one batch of images is shown, as well as the model's throughput (i.e, the number of images processed per second, or FPS). Results are averaged across **{}** iterations.
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
markdown_batch_inference = """
|
|
121
|
+
This chart shows how the model's speed changes with different batch sizes. As the batch size increases, you can observe an increase in FPS (images per second).
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
markdown_acknowledgement = """---
|
|
125
|
+
### Acknowledgement
|
|
126
|
+
|
|
127
|
+
[1] Maximilian Bernhard, Roberto Amoroso, Yannic Kindermann, Lorenzo Baraldi, Rita Cucchiara, Volker Tresp, Matthias Schubert. <a href="https://openaccess.thecvf.com/content/WACV2024/html/Bernhard_Whats_Outside_the_Intersection_Fine-Grained_Error_Analysis_for_Semantic_Segmentation_WACV_2024_paper.html" target="_blank">What's Outside the Intersection? Fine-grained Error Analysis for Semantic Segmentation Beyond IoU.</a> In Proceedings of the IEEE / CVF Conference on Computer Vision and Pattern Recognition, pages 969 - 977, 2024.
|
|
128
|
+
|
|
129
|
+
[2] Bowen Cheng, Ross Girshick, Piotr Dollar, Alexander C. Berg, Alexander Kirillov. <a href="https://arxiv.org/abs/2103.16562" target="_blank">Boundary IoU: Improving object-centric image segmentation evaluation.</a> In Proceedings of the IEEE / CVF Conference on Computer Vision and Pattern Recognition, pages 15334 - 15342, 2021.
|
|
130
|
+
"""
|
|
File without changes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from supervisely.nn.benchmark.semantic_segmentation.base_vis_metric import (
|
|
2
|
+
SemanticSegmVisMetric,
|
|
3
|
+
)
|
|
4
|
+
from supervisely.nn.benchmark.visualization.widgets import MarkdownWidget
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Acknowledgement(SemanticSegmVisMetric):
|
|
8
|
+
|
|
9
|
+
@property
|
|
10
|
+
def md(self) -> MarkdownWidget:
|
|
11
|
+
return MarkdownWidget(
|
|
12
|
+
"acknowledgement",
|
|
13
|
+
"Acknowledgement",
|
|
14
|
+
text=self.vis_texts.markdown_acknowledgement,
|
|
15
|
+
)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
|
|
3
|
+
from supervisely.nn.benchmark.semantic_segmentation.base_vis_metric import (
|
|
4
|
+
SemanticSegmVisMetric,
|
|
5
|
+
)
|
|
6
|
+
from supervisely.nn.benchmark.visualization.widgets import ChartWidget, MarkdownWidget
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ClasswiseErrorAnalysis(SemanticSegmVisMetric):
|
|
10
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
11
|
+
super().__init__(*args, **kwargs)
|
|
12
|
+
self.clickable = True
|
|
13
|
+
|
|
14
|
+
@property
|
|
15
|
+
def md(self) -> MarkdownWidget:
|
|
16
|
+
return MarkdownWidget(
|
|
17
|
+
"classwise_error_analysis",
|
|
18
|
+
"Classwise Segmentation Error Analysis",
|
|
19
|
+
text=self.vis_texts.markdown_eou_per_class,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def chart(self) -> ChartWidget:
|
|
24
|
+
chart = ChartWidget("classwise_error_analysis", self.get_figure())
|
|
25
|
+
chart.set_click_data(
|
|
26
|
+
self.explore_modal_table.id,
|
|
27
|
+
self.get_click_data(),
|
|
28
|
+
chart_click_extra="'getKey': (payload) => `${payload.points[0].label}`,",
|
|
29
|
+
)
|
|
30
|
+
return chart
|
|
31
|
+
|
|
32
|
+
def get_figure(self):
|
|
33
|
+
import plotly.graph_objects as go # pylint: disable=import-error
|
|
34
|
+
|
|
35
|
+
fig = go.Figure()
|
|
36
|
+
|
|
37
|
+
# # Classwise Segmentation Error Analysis figure
|
|
38
|
+
bar_data, labels = self.eval_result.mp.classwise_segm_error_data
|
|
39
|
+
color_palette = ["#8ACAA1", "#FFE4B5", "#F7ADAA", "#dd3f3f"]
|
|
40
|
+
|
|
41
|
+
for i, column in enumerate(bar_data.columns):
|
|
42
|
+
fig.add_trace(
|
|
43
|
+
go.Bar(
|
|
44
|
+
name=column,
|
|
45
|
+
y=bar_data[column],
|
|
46
|
+
x=labels,
|
|
47
|
+
marker_color=color_palette[i],
|
|
48
|
+
hovertemplate="Class: %{x}<br> %{name}: %{y:.2f}<extra></extra>",
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
fig.update_layout(barmode="stack", xaxis_title="Class")
|
|
53
|
+
if len(labels) < 10:
|
|
54
|
+
fig.update_layout(width=800)
|
|
55
|
+
fig.update_yaxes(range=[0, 1])
|
|
56
|
+
|
|
57
|
+
return fig
|