From 3d781937fb99415f98e0dd5e14a8e0831b1f6f08 Mon Sep 17 00:00:00 2001 From: tzhong518 Date: Fri, 12 Dec 2025 12:49:21 +0900 Subject: [PATCH 1/6] add: comlops dataset and ioumetrc Signed-off-by: tzhong518 --- .../detection2d/dataset/t4dataset/comlops.py | 92 +++++++++++++++++++ autoware_ml/detection2d/metrics/__init__.py | 5 +- 2 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 autoware_ml/configs/detection2d/dataset/t4dataset/comlops.py diff --git a/autoware_ml/configs/detection2d/dataset/t4dataset/comlops.py b/autoware_ml/configs/detection2d/dataset/t4dataset/comlops.py new file mode 100644 index 000000000..647cbeb86 --- /dev/null +++ b/autoware_ml/configs/detection2d/dataset/t4dataset/comlops.py @@ -0,0 +1,92 @@ +dataset_version_config_root = "autoware_ml/configs/detection2d/dataset/t4dataset/" +dataset_version_list = [ + "comlops", +] + +classes = ( + "animal", + "bicycle", + "building", + "bus", + "car", + "cone", + "construction", + "crosswalk", + "dashed_lane_marking", + "deceleration_line", + "gate", + "guide_post", + "laneline_dash_white", + "laneline_dash_yellow", + "laneline_solid_green", + "laneline_solid_red", + "laneline_solid_white", + "laneline_solid_yellow", + "marking_arrow", + "marking_character", + "marking_other", + "motorcycle", + "other_obstacle", + "other_pedestrian", + "other_vehicle", + "parking_lot", + "pedestrian", + "pole", + "road", + "road_debris", + "sidewalk", + "sky", + "stopline", + "striped_road_marking", + "traffic_light", + "traffic_sign", + "train", + "truck", + # "unknown", + "vegetation/terrain", + "wall/fence", +) + +class_mappings ={ + 'animal': 'animal', + 'bicycle': 'bicycle', + 'building': 'building', + 'bus': 'bus', + 'car': 'car', + 'cone': 'cone', + 'construction': 'construction', + 'crosswalk': 'crosswalk', + 'dashed_lane_marking': 'dashed_lane_marking', + 'deceleration_line': 'deceleration_line', + 'gate': 'gate', + 'guide_post': 'guide_post', + 'laneline_dash_white': 'laneline_dash_white', + 'laneline_dash_yellow': 'laneline_dash_yellow', + 'laneline_solid_green': 'laneline_solid_green', + 'laneline_solid_red': 'laneline_solid_red', + 'laneline_solid_white': 'laneline_solid_white', + 'laneline_solid_yellow': 'laneline_solid_yellow', + 'marking_arrow': 'marking_arrow', + 'marking_character': 'marking_character', + 'marking_other': 'marking_other', + 'motorcycle': 'motorcycle', + 'other_obstacle': 'other_obstacle', + 'other_pedestrian': 'other_pedestrian', + 'other_vehicle': 'other_vehicle', + 'parking_lot': 'parking_lot', + 'pedestrian': 'pedestrian', + 'pole': 'pole', + 'road': 'road', + 'road_debris': 'road_debris', + 'sidewalk': 'sidewalk', + 'sky': 'sky', + 'stopline': 'stopline', + 'striped_road_marking': 'striped_road_marking', + 'traffic_light': 'traffic_light', + 'traffic_sign': 'traffic_sign', + 'train': 'train', + 'truck': 'truck', + 'vegetation/terrain': 'vegetation/terrain', + 'wall/fence': 'wall/fence', + 'unknown': 'unknown' +} diff --git a/autoware_ml/detection2d/metrics/__init__.py b/autoware_ml/detection2d/metrics/__init__.py index 7d0b5e8c1..0abe4d6ea 100644 --- a/autoware_ml/detection2d/metrics/__init__.py +++ b/autoware_ml/detection2d/metrics/__init__.py @@ -1,3 +1,6 @@ from .tlr_metrics import TLRFineDetectorEvaluator +from mmseg.evaluation.metrics import IoUMetric +from mmengine.registry import METRICS +METRICS.register_module()(IoUMetric) -__all__ = ["TLRFineDetectorEvaluator"] +__all__ = ["TLRFineDetectorEvaluator", "IoUMetric"] From e4a435bb67955bba33bf4d0b5f90161c0ecdaccb Mon Sep 17 00:00:00 2001 From: tzhong518 Date: Fri, 12 Dec 2025 13:08:32 +0900 Subject: [PATCH 2/6] add: convertion of semseg mask Signed-off-by: tzhong518 --- tools/detection2d/create_data_t4dataset.py | 155 +++++++++++++++++++-- 1 file changed, 144 insertions(+), 11 deletions(-) diff --git a/tools/detection2d/create_data_t4dataset.py b/tools/detection2d/create_data_t4dataset.py index a958e75c3..d99a1063e 100644 --- a/tools/detection2d/create_data_t4dataset.py +++ b/tools/detection2d/create_data_t4dataset.py @@ -4,10 +4,11 @@ import re import warnings from dataclasses import dataclass, field -from typing import Dict, List +from typing import Dict, List, Optional import mmengine import numpy as np +import cv2 import yaml from mmengine.config import Config from mmengine.logging import print_log @@ -22,43 +23,149 @@ class Instance: mask: List[List[int]] = field(default_factory=list) extra_anns: List[str] = field(default_factory=list) - @dataclass class DataEntry: img_path: str width: int height: int instances: List[Instance] = field(default_factory=list) - + surfaces: List[Instance] = field(default_factory=list) + gt_semantic_seg: Optional[str] = None @dataclass class DetectionData: metainfo: Dict[str, str] data_list: List[DataEntry] = field(default_factory=list) +def save_semantic_mask_png(semantic_mask, output_path): + + os.makedirs(os.path.dirname(output_path), exist_ok=True) + + cv2.imwrite(output_path, semantic_mask.astype(np.uint8)) + +def convert_entry_instances_to_semantic(entry, allowed_classes): + height, width = entry.height, entry.width + # Initialize with ignore index (usually 255) + semantic_mask = np.full((height, width), 255, dtype=np.uint8) + + # Draw surfaces first (background/stuff) if any exist + for surf in entry.surfaces: + cls_id = surf.bbox_label + for poly_flat in surf.mask: + if len(poly_flat) < 6: + continue + poly = np.array(poly_flat, dtype=np.int32).reshape(-1, 2) + cv2.fillPoly(semantic_mask, [poly], color=cls_id) + + # Draw instances (objects/things) on top + for inst in entry.instances: + cls_id = inst.bbox_label + for poly_flat in inst.mask: + if len(poly_flat) < 6: + continue + poly = np.array(poly_flat, dtype=np.int32).reshape(-1, 2) + cv2.fillPoly(semantic_mask, [poly], color=cls_id) + + return semantic_mask + +def generate_colormap(num_classes): + np.random.seed(42) + colors = np.random.randint(0, 256, size=(num_classes, 3), dtype=np.uint8) + return colors + +def save_colored_mask(semantic_mask, output_path, colormap): + height, width = semantic_mask.shape + color_mask = np.zeros((height, width, 3), dtype=np.uint8) + + for cls_id, color in enumerate(colormap): + color_mask[semantic_mask == cls_id] = color + + os.makedirs(os.path.dirname(output_path), exist_ok=True) + cv2.imwrite(output_path, color_mask[:, :, ::-1]) # OpenCV BGR def update_detection_data_annotations( data_list: Dict[str, DataEntry], object_ann: List[ObjectAnn], + surface_ann: List[ObjectAnn], attributes: Dict[str, str], categories: Dict[str, str], class_mappings: Dict[str, str], allowed_classes: List[str], + root_path: str, + save_colored_masks: bool = False, + save_semantic_segmentation: bool = False, ) -> None: + + # Instance (Objects) for ann in object_ann: - class_name = class_mappings[categories[ann.category_token]] + class_name = class_mappings.get(categories[ann.category_token], None) if class_name not in allowed_classes: continue bbox_label = allowed_classes.index(class_name) + + # decode mask + binary = ann.mask.decode().astype(np.uint8) + + contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + + flat_polygons = [] + for cnt in contours: + if len(cnt) >= 3: + poly = cnt.reshape(-1, 2).tolist() # [[x1,y1], [x2,y2], ...] + poly_flat = [coord for point in poly for coord in point] + flat_polygons.append(poly_flat) + instance = Instance( bbox=ann.bbox, bbox_label=bbox_label, - # TODO(someone): Please check this operation is correct!!! - mask=[[int(x), int(y)] for y, x in zip(*np.where(ann.mask.decode() == 1))], + mask=flat_polygons, extra_anns=[attributes[x] for x in ann.attribute_tokens], ) data_list[ann.sample_data_token].instances.append(instance) + # Surface (Background) - Only process if flag is True + if save_semantic_segmentation: + for ann in surface_ann: + class_name = class_mappings.get(categories[ann.category_token], None) + + if class_name not in allowed_classes: + continue + + bbox_label = allowed_classes.index(class_name) + + binary = ann.mask.decode().astype(np.uint8) + contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + + flat_polygons = [] + for cnt in contours: + if len(cnt) >= 3: + poly = cnt.reshape(-1, 2).tolist() + poly_flat = [coord for point in poly for coord in point] + flat_polygons.append(poly_flat) + + surface_instance = Instance( + bbox=ann.bbox if hasattr(ann, 'bbox') else [0,0,0,0], + bbox_label=bbox_label, + mask=flat_polygons, + extra_anns=[] + ) + + if ann.sample_data_token in data_list: + data_list[ann.sample_data_token].surfaces.append(surface_instance) + + # Generate and save semantic masks images + colormap = generate_colormap(len(allowed_classes) + 1) + for key, entry in data_list.items(): + gray_mask = convert_entry_instances_to_semantic(entry, allowed_classes) + + mask_file = f"{root_path}/masks/{(entry.img_path).split('/')[-5] +'_' + (entry.img_path).split('/')[-2] +'_' + (entry.img_path).split('/')[-1]}.png" + os.makedirs(os.path.dirname(mask_file), exist_ok=True) + cv2.imwrite(mask_file, gray_mask.astype(np.uint8)) + entry.gt_semantic_seg = mask_file + + if save_colored_masks: + color_file = f"{root_path}/masks_color/{(entry.img_path).split('/')[-5] +'_' +(entry.img_path).split('/')[-2] +'_' + (entry.img_path).split('/')[-1]}.png" + save_colored_mask(gray_mask, color_file, colormap) def get_scene_root_dir_path( root_path: str, @@ -93,6 +200,16 @@ def parse_args() -> argparse.Namespace: help="Will resort to using the available dataset version if the one specified in the config file does not exist.", ) parser.add_argument("-o", "--out_dir", type=str, required=True, help="output directory of info file") + parser.add_argument( + "--save_semantic_segmentation", + action="store_true", + help="Whether to process surface annotations and save semantic segmentation masks.", + ) + parser.add_argument( + "--save_colored_masks", + action="store_true", + help="Whether to save colored semantic masks.", + ) return parser.parse_args() @@ -127,6 +244,8 @@ def assign_ids_and_save_detection_data( } for instance in entry.instances ], + # 如果不保存mask,这里将是 None (JSON中显示为 null) + "seg_map_path": entry.gt_semantic_seg, } for i, entry in enumerate(detection_data.data_list) ], @@ -158,16 +277,26 @@ def main() -> None: for scene_id in dataset_list_dict.get(split, []): print_log(f"Creating data info for scene: {scene_id}") - t4_dataset_id, t4_dataset_version_id = scene_id.split(" ") - if os.path.exists(osp.join(args.root_path, t4_dataset_id, t4_dataset_version_id)): + parts = scene_id.split(" ") + if len(parts) == 2: + t4_dataset_id, t4_dataset_version_id = parts + else: + t4_dataset_id = scene_id.strip() + t4_dataset_version_id = None + + if t4_dataset_version_id and os.path.exists(osp.join(args.root_path, t4_dataset_id, t4_dataset_version_id)): scene_root_dir_path = osp.join(args.root_path, t4_dataset_id, t4_dataset_version_id) - elif args.use_available_dataset_version: + elif os.path.exists(osp.join(args.root_path, dataset_version, t4_dataset_id)): print( - "Warning: The version of the dataset specified in the config file does not exist. Will use whatever is available locally." + f"Warning: {t4_dataset_id} has no t4_dataset_version_id or the specified version is missing. " + "Using the available version on disk." ) + scene_root_dir_path = get_scene_root_dir_path(args.root_path, dataset_version, t4_dataset_id) else: - raise ValueError(f"{t4_dataset_id} does not exist.") + raise ValueError( + f"{t4_dataset_id} does not exist." + ) t4 = Tier4( data_root=scene_root_dir_path, @@ -192,10 +321,14 @@ def main() -> None: update_detection_data_annotations( data_list, t4.object_ann, + t4.surface_ann, attributes, categories, cfg.class_mappings, cfg.classes, + args.root_path, + save_colored_masks=args.save_colored_masks, + save_semantic_segmentation=args.save_semantic_segmentation, ) data_infos[split].extend(data_list.values()) From 893d8ea5336b9aea139ac88efbeb36dd14d54083 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 12 Dec 2025 04:21:59 +0000 Subject: [PATCH 3/6] ci(pre-commit): autofix --- .../detection2d/dataset/t4dataset/comlops.py | 84 +++++++++---------- autoware_ml/detection2d/metrics/__init__.py | 6 +- tools/detection2d/create_data_t4dataset.py | 42 ++++++---- 3 files changed, 71 insertions(+), 61 deletions(-) diff --git a/autoware_ml/configs/detection2d/dataset/t4dataset/comlops.py b/autoware_ml/configs/detection2d/dataset/t4dataset/comlops.py index 647cbeb86..450345935 100644 --- a/autoware_ml/configs/detection2d/dataset/t4dataset/comlops.py +++ b/autoware_ml/configs/detection2d/dataset/t4dataset/comlops.py @@ -47,46 +47,46 @@ "wall/fence", ) -class_mappings ={ - 'animal': 'animal', - 'bicycle': 'bicycle', - 'building': 'building', - 'bus': 'bus', - 'car': 'car', - 'cone': 'cone', - 'construction': 'construction', - 'crosswalk': 'crosswalk', - 'dashed_lane_marking': 'dashed_lane_marking', - 'deceleration_line': 'deceleration_line', - 'gate': 'gate', - 'guide_post': 'guide_post', - 'laneline_dash_white': 'laneline_dash_white', - 'laneline_dash_yellow': 'laneline_dash_yellow', - 'laneline_solid_green': 'laneline_solid_green', - 'laneline_solid_red': 'laneline_solid_red', - 'laneline_solid_white': 'laneline_solid_white', - 'laneline_solid_yellow': 'laneline_solid_yellow', - 'marking_arrow': 'marking_arrow', - 'marking_character': 'marking_character', - 'marking_other': 'marking_other', - 'motorcycle': 'motorcycle', - 'other_obstacle': 'other_obstacle', - 'other_pedestrian': 'other_pedestrian', - 'other_vehicle': 'other_vehicle', - 'parking_lot': 'parking_lot', - 'pedestrian': 'pedestrian', - 'pole': 'pole', - 'road': 'road', - 'road_debris': 'road_debris', - 'sidewalk': 'sidewalk', - 'sky': 'sky', - 'stopline': 'stopline', - 'striped_road_marking': 'striped_road_marking', - 'traffic_light': 'traffic_light', - 'traffic_sign': 'traffic_sign', - 'train': 'train', - 'truck': 'truck', - 'vegetation/terrain': 'vegetation/terrain', - 'wall/fence': 'wall/fence', - 'unknown': 'unknown' +class_mappings = { + "animal": "animal", + "bicycle": "bicycle", + "building": "building", + "bus": "bus", + "car": "car", + "cone": "cone", + "construction": "construction", + "crosswalk": "crosswalk", + "dashed_lane_marking": "dashed_lane_marking", + "deceleration_line": "deceleration_line", + "gate": "gate", + "guide_post": "guide_post", + "laneline_dash_white": "laneline_dash_white", + "laneline_dash_yellow": "laneline_dash_yellow", + "laneline_solid_green": "laneline_solid_green", + "laneline_solid_red": "laneline_solid_red", + "laneline_solid_white": "laneline_solid_white", + "laneline_solid_yellow": "laneline_solid_yellow", + "marking_arrow": "marking_arrow", + "marking_character": "marking_character", + "marking_other": "marking_other", + "motorcycle": "motorcycle", + "other_obstacle": "other_obstacle", + "other_pedestrian": "other_pedestrian", + "other_vehicle": "other_vehicle", + "parking_lot": "parking_lot", + "pedestrian": "pedestrian", + "pole": "pole", + "road": "road", + "road_debris": "road_debris", + "sidewalk": "sidewalk", + "sky": "sky", + "stopline": "stopline", + "striped_road_marking": "striped_road_marking", + "traffic_light": "traffic_light", + "traffic_sign": "traffic_sign", + "train": "train", + "truck": "truck", + "vegetation/terrain": "vegetation/terrain", + "wall/fence": "wall/fence", + "unknown": "unknown", } diff --git a/autoware_ml/detection2d/metrics/__init__.py b/autoware_ml/detection2d/metrics/__init__.py index 0abe4d6ea..ab922629a 100644 --- a/autoware_ml/detection2d/metrics/__init__.py +++ b/autoware_ml/detection2d/metrics/__init__.py @@ -1,6 +1,8 @@ -from .tlr_metrics import TLRFineDetectorEvaluator -from mmseg.evaluation.metrics import IoUMetric from mmengine.registry import METRICS +from mmseg.evaluation.metrics import IoUMetric + +from .tlr_metrics import TLRFineDetectorEvaluator + METRICS.register_module()(IoUMetric) __all__ = ["TLRFineDetectorEvaluator", "IoUMetric"] diff --git a/tools/detection2d/create_data_t4dataset.py b/tools/detection2d/create_data_t4dataset.py index d99a1063e..601dec2a2 100644 --- a/tools/detection2d/create_data_t4dataset.py +++ b/tools/detection2d/create_data_t4dataset.py @@ -6,9 +6,9 @@ from dataclasses import dataclass, field from typing import Dict, List, Optional +import cv2 import mmengine import numpy as np -import cv2 import yaml from mmengine.config import Config from mmengine.logging import print_log @@ -23,6 +23,7 @@ class Instance: mask: List[List[int]] = field(default_factory=list) extra_anns: List[str] = field(default_factory=list) + @dataclass class DataEntry: img_path: str @@ -30,19 +31,22 @@ class DataEntry: height: int instances: List[Instance] = field(default_factory=list) surfaces: List[Instance] = field(default_factory=list) - gt_semantic_seg: Optional[str] = None + gt_semantic_seg: Optional[str] = None + @dataclass class DetectionData: metainfo: Dict[str, str] data_list: List[DataEntry] = field(default_factory=list) + def save_semantic_mask_png(semantic_mask, output_path): os.makedirs(os.path.dirname(output_path), exist_ok=True) cv2.imwrite(output_path, semantic_mask.astype(np.uint8)) + def convert_entry_instances_to_semantic(entry, allowed_classes): height, width = entry.height, entry.width # Initialize with ignore index (usually 255) @@ -52,7 +56,7 @@ def convert_entry_instances_to_semantic(entry, allowed_classes): for surf in entry.surfaces: cls_id = surf.bbox_label for poly_flat in surf.mask: - if len(poly_flat) < 6: + if len(poly_flat) < 6: continue poly = np.array(poly_flat, dtype=np.int32).reshape(-1, 2) cv2.fillPoly(semantic_mask, [poly], color=cls_id) @@ -61,18 +65,20 @@ def convert_entry_instances_to_semantic(entry, allowed_classes): for inst in entry.instances: cls_id = inst.bbox_label for poly_flat in inst.mask: - if len(poly_flat) < 6: + if len(poly_flat) < 6: continue poly = np.array(poly_flat, dtype=np.int32).reshape(-1, 2) cv2.fillPoly(semantic_mask, [poly], color=cls_id) - + return semantic_mask + def generate_colormap(num_classes): np.random.seed(42) colors = np.random.randint(0, 256, size=(num_classes, 3), dtype=np.uint8) return colors + def save_colored_mask(semantic_mask, output_path, colormap): height, width = semantic_mask.shape color_mask = np.zeros((height, width, 3), dtype=np.uint8) @@ -83,6 +89,7 @@ def save_colored_mask(semantic_mask, output_path, colormap): os.makedirs(os.path.dirname(output_path), exist_ok=True) cv2.imwrite(output_path, color_mask[:, :, ::-1]) # OpenCV BGR + def update_detection_data_annotations( data_list: Dict[str, DataEntry], object_ann: List[ObjectAnn], @@ -96,7 +103,7 @@ def update_detection_data_annotations( save_semantic_segmentation: bool = False, ) -> None: - # Instance (Objects) + # Instance (Objects) for ann in object_ann: class_name = class_mappings.get(categories[ann.category_token], None) if class_name not in allowed_classes: @@ -110,9 +117,9 @@ def update_detection_data_annotations( flat_polygons = [] for cnt in contours: - if len(cnt) >= 3: + if len(cnt) >= 3: poly = cnt.reshape(-1, 2).tolist() # [[x1,y1], [x2,y2], ...] - poly_flat = [coord for point in poly for coord in point] + poly_flat = [coord for point in poly for coord in point] flat_polygons.append(poly_flat) instance = Instance( @@ -127,10 +134,10 @@ def update_detection_data_annotations( if save_semantic_segmentation: for ann in surface_ann: class_name = class_mappings.get(categories[ann.category_token], None) - + if class_name not in allowed_classes: continue - + bbox_label = allowed_classes.index(class_name) binary = ann.mask.decode().astype(np.uint8) @@ -144,10 +151,10 @@ def update_detection_data_annotations( flat_polygons.append(poly_flat) surface_instance = Instance( - bbox=ann.bbox if hasattr(ann, 'bbox') else [0,0,0,0], + bbox=ann.bbox if hasattr(ann, "bbox") else [0, 0, 0, 0], bbox_label=bbox_label, mask=flat_polygons, - extra_anns=[] + extra_anns=[], ) if ann.sample_data_token in data_list: @@ -167,6 +174,7 @@ def update_detection_data_annotations( color_file = f"{root_path}/masks_color/{(entry.img_path).split('/')[-5] +'_' +(entry.img_path).split('/')[-2] +'_' + (entry.img_path).split('/')[-1]}.png" save_colored_mask(gray_mask, color_file, colormap) + def get_scene_root_dir_path( root_path: str, dataset_version: str, @@ -284,19 +292,19 @@ def main() -> None: t4_dataset_id = scene_id.strip() t4_dataset_version_id = None - if t4_dataset_version_id and os.path.exists(osp.join(args.root_path, t4_dataset_id, t4_dataset_version_id)): + if t4_dataset_version_id and os.path.exists( + osp.join(args.root_path, t4_dataset_id, t4_dataset_version_id) + ): scene_root_dir_path = osp.join(args.root_path, t4_dataset_id, t4_dataset_version_id) elif os.path.exists(osp.join(args.root_path, dataset_version, t4_dataset_id)): print( f"Warning: {t4_dataset_id} has no t4_dataset_version_id or the specified version is missing. " "Using the available version on disk." ) - + scene_root_dir_path = get_scene_root_dir_path(args.root_path, dataset_version, t4_dataset_id) else: - raise ValueError( - f"{t4_dataset_id} does not exist." - ) + raise ValueError(f"{t4_dataset_id} does not exist.") t4 = Tier4( data_root=scene_root_dir_path, From 050e656f307cee578b7495a61e1dbbf8bf9dbb2b Mon Sep 17 00:00:00 2001 From: tzhong518 Date: Fri, 12 Dec 2025 13:22:53 +0900 Subject: [PATCH 4/6] fix: typos Signed-off-by: tzhong518 --- tools/detection2d/create_data_t4dataset.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/detection2d/create_data_t4dataset.py b/tools/detection2d/create_data_t4dataset.py index d99a1063e..aa5e0e310 100644 --- a/tools/detection2d/create_data_t4dataset.py +++ b/tools/detection2d/create_data_t4dataset.py @@ -244,7 +244,6 @@ def assign_ids_and_save_detection_data( } for instance in entry.instances ], - # 如果不保存mask,这里将是 None (JSON中显示为 null) "seg_map_path": entry.gt_semantic_seg, } for i, entry in enumerate(detection_data.data_list) From cb3226cd0eee228c9097a7b695c62c6bfa9eb98f Mon Sep 17 00:00:00 2001 From: tzhong518 Date: Fri, 19 Dec 2025 11:39:47 +0900 Subject: [PATCH 5/6] fix: filter no-annotation images Signed-off-by: tzhong518 --- tools/detection2d/create_data_t4dataset.py | 50 ++++++++++------------ 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/tools/detection2d/create_data_t4dataset.py b/tools/detection2d/create_data_t4dataset.py index 9511fa4e9..16f51a4ca 100644 --- a/tools/detection2d/create_data_t4dataset.py +++ b/tools/detection2d/create_data_t4dataset.py @@ -6,9 +6,9 @@ from dataclasses import dataclass, field from typing import Dict, List, Optional -import cv2 import mmengine import numpy as np +import cv2 import yaml from mmengine.config import Config from mmengine.logging import print_log @@ -23,7 +23,6 @@ class Instance: mask: List[List[int]] = field(default_factory=list) extra_anns: List[str] = field(default_factory=list) - @dataclass class DataEntry: img_path: str @@ -31,22 +30,19 @@ class DataEntry: height: int instances: List[Instance] = field(default_factory=list) surfaces: List[Instance] = field(default_factory=list) - gt_semantic_seg: Optional[str] = None - + gt_semantic_seg: Optional[str] = None @dataclass class DetectionData: metainfo: Dict[str, str] data_list: List[DataEntry] = field(default_factory=list) - def save_semantic_mask_png(semantic_mask, output_path): os.makedirs(os.path.dirname(output_path), exist_ok=True) cv2.imwrite(output_path, semantic_mask.astype(np.uint8)) - def convert_entry_instances_to_semantic(entry, allowed_classes): height, width = entry.height, entry.width # Initialize with ignore index (usually 255) @@ -56,7 +52,7 @@ def convert_entry_instances_to_semantic(entry, allowed_classes): for surf in entry.surfaces: cls_id = surf.bbox_label for poly_flat in surf.mask: - if len(poly_flat) < 6: + if len(poly_flat) < 6: continue poly = np.array(poly_flat, dtype=np.int32).reshape(-1, 2) cv2.fillPoly(semantic_mask, [poly], color=cls_id) @@ -65,20 +61,18 @@ def convert_entry_instances_to_semantic(entry, allowed_classes): for inst in entry.instances: cls_id = inst.bbox_label for poly_flat in inst.mask: - if len(poly_flat) < 6: + if len(poly_flat) < 6: continue poly = np.array(poly_flat, dtype=np.int32).reshape(-1, 2) cv2.fillPoly(semantic_mask, [poly], color=cls_id) - + return semantic_mask - def generate_colormap(num_classes): np.random.seed(42) colors = np.random.randint(0, 256, size=(num_classes, 3), dtype=np.uint8) return colors - def save_colored_mask(semantic_mask, output_path, colormap): height, width = semantic_mask.shape color_mask = np.zeros((height, width, 3), dtype=np.uint8) @@ -89,7 +83,6 @@ def save_colored_mask(semantic_mask, output_path, colormap): os.makedirs(os.path.dirname(output_path), exist_ok=True) cv2.imwrite(output_path, color_mask[:, :, ::-1]) # OpenCV BGR - def update_detection_data_annotations( data_list: Dict[str, DataEntry], object_ann: List[ObjectAnn], @@ -103,8 +96,10 @@ def update_detection_data_annotations( save_semantic_segmentation: bool = False, ) -> None: - # Instance (Objects) + # Instance (Objects) for ann in object_ann: + if ann.sample_data_token not in data_list: + continue class_name = class_mappings.get(categories[ann.category_token], None) if class_name not in allowed_classes: continue @@ -117,9 +112,9 @@ def update_detection_data_annotations( flat_polygons = [] for cnt in contours: - if len(cnt) >= 3: + if len(cnt) >= 3: poly = cnt.reshape(-1, 2).tolist() # [[x1,y1], [x2,y2], ...] - poly_flat = [coord for point in poly for coord in point] + poly_flat = [coord for point in poly for coord in point] flat_polygons.append(poly_flat) instance = Instance( @@ -134,10 +129,10 @@ def update_detection_data_annotations( if save_semantic_segmentation: for ann in surface_ann: class_name = class_mappings.get(categories[ann.category_token], None) - + if class_name not in allowed_classes: continue - + bbox_label = allowed_classes.index(class_name) binary = ann.mask.decode().astype(np.uint8) @@ -151,10 +146,10 @@ def update_detection_data_annotations( flat_polygons.append(poly_flat) surface_instance = Instance( - bbox=ann.bbox if hasattr(ann, "bbox") else [0, 0, 0, 0], + bbox=ann.bbox if hasattr(ann, 'bbox') else [0,0,0,0], bbox_label=bbox_label, mask=flat_polygons, - extra_anns=[], + extra_anns=[] ) if ann.sample_data_token in data_list: @@ -164,17 +159,15 @@ def update_detection_data_annotations( colormap = generate_colormap(len(allowed_classes) + 1) for key, entry in data_list.items(): gray_mask = convert_entry_instances_to_semantic(entry, allowed_classes) - - mask_file = f"{root_path}/masks/{(entry.img_path).split('/')[-5] +'_' + (entry.img_path).split('/')[-2] +'_' + (entry.img_path).split('/')[-1]}.png" + mask_file = f"{root_path}/semseg/masks/{(entry.img_path).split('/')[-4] +'_' + (entry.img_path).split('/')[-2] +'_' + (entry.img_path).split('/')[-1]}".replace('.jpg', '.png') os.makedirs(os.path.dirname(mask_file), exist_ok=True) cv2.imwrite(mask_file, gray_mask.astype(np.uint8)) entry.gt_semantic_seg = mask_file if save_colored_masks: - color_file = f"{root_path}/masks_color/{(entry.img_path).split('/')[-5] +'_' +(entry.img_path).split('/')[-2] +'_' + (entry.img_path).split('/')[-1]}.png" + color_file = f"{root_path}/semseg/masks_color/{(entry.img_path).split('/')[-5] +'_' +(entry.img_path).split('/')[-2] +'_' + (entry.img_path).split('/')[-1]}".replace('.jpg', '.png') save_colored_mask(gray_mask, color_file, colormap) - def get_scene_root_dir_path( root_path: str, dataset_version: str, @@ -291,23 +284,24 @@ def main() -> None: t4_dataset_id = scene_id.strip() t4_dataset_version_id = None - if t4_dataset_version_id and os.path.exists( - osp.join(args.root_path, t4_dataset_id, t4_dataset_version_id) - ): + if t4_dataset_version_id and os.path.exists(osp.join(args.root_path, t4_dataset_id, t4_dataset_version_id)): scene_root_dir_path = osp.join(args.root_path, t4_dataset_id, t4_dataset_version_id) elif os.path.exists(osp.join(args.root_path, dataset_version, t4_dataset_id)): print( f"Warning: {t4_dataset_id} has no t4_dataset_version_id or the specified version is missing. " "Using the available version on disk." ) - + scene_root_dir_path = get_scene_root_dir_path(args.root_path, dataset_version, t4_dataset_id) else: - raise ValueError(f"{t4_dataset_id} does not exist.") + raise ValueError( + f"{t4_dataset_id} does not exist." + ) t4 = Tier4( data_root=scene_root_dir_path, verbose=False, + version="annotation", ) data_list: Dict[str, DataEntry] = {} From 41bcf4dad0e9e5fa9f2914b455df6d99d51c0528 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 19 Dec 2025 02:40:15 +0000 Subject: [PATCH 6/6] ci(pre-commit): autofix --- tools/detection2d/create_data_t4dataset.py | 50 ++++++++++++++-------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/tools/detection2d/create_data_t4dataset.py b/tools/detection2d/create_data_t4dataset.py index 16f51a4ca..cb351bdfd 100644 --- a/tools/detection2d/create_data_t4dataset.py +++ b/tools/detection2d/create_data_t4dataset.py @@ -6,9 +6,9 @@ from dataclasses import dataclass, field from typing import Dict, List, Optional +import cv2 import mmengine import numpy as np -import cv2 import yaml from mmengine.config import Config from mmengine.logging import print_log @@ -23,6 +23,7 @@ class Instance: mask: List[List[int]] = field(default_factory=list) extra_anns: List[str] = field(default_factory=list) + @dataclass class DataEntry: img_path: str @@ -30,19 +31,22 @@ class DataEntry: height: int instances: List[Instance] = field(default_factory=list) surfaces: List[Instance] = field(default_factory=list) - gt_semantic_seg: Optional[str] = None + gt_semantic_seg: Optional[str] = None + @dataclass class DetectionData: metainfo: Dict[str, str] data_list: List[DataEntry] = field(default_factory=list) + def save_semantic_mask_png(semantic_mask, output_path): os.makedirs(os.path.dirname(output_path), exist_ok=True) cv2.imwrite(output_path, semantic_mask.astype(np.uint8)) + def convert_entry_instances_to_semantic(entry, allowed_classes): height, width = entry.height, entry.width # Initialize with ignore index (usually 255) @@ -52,7 +56,7 @@ def convert_entry_instances_to_semantic(entry, allowed_classes): for surf in entry.surfaces: cls_id = surf.bbox_label for poly_flat in surf.mask: - if len(poly_flat) < 6: + if len(poly_flat) < 6: continue poly = np.array(poly_flat, dtype=np.int32).reshape(-1, 2) cv2.fillPoly(semantic_mask, [poly], color=cls_id) @@ -61,18 +65,20 @@ def convert_entry_instances_to_semantic(entry, allowed_classes): for inst in entry.instances: cls_id = inst.bbox_label for poly_flat in inst.mask: - if len(poly_flat) < 6: + if len(poly_flat) < 6: continue poly = np.array(poly_flat, dtype=np.int32).reshape(-1, 2) cv2.fillPoly(semantic_mask, [poly], color=cls_id) - + return semantic_mask + def generate_colormap(num_classes): np.random.seed(42) colors = np.random.randint(0, 256, size=(num_classes, 3), dtype=np.uint8) return colors + def save_colored_mask(semantic_mask, output_path, colormap): height, width = semantic_mask.shape color_mask = np.zeros((height, width, 3), dtype=np.uint8) @@ -83,6 +89,7 @@ def save_colored_mask(semantic_mask, output_path, colormap): os.makedirs(os.path.dirname(output_path), exist_ok=True) cv2.imwrite(output_path, color_mask[:, :, ::-1]) # OpenCV BGR + def update_detection_data_annotations( data_list: Dict[str, DataEntry], object_ann: List[ObjectAnn], @@ -96,7 +103,7 @@ def update_detection_data_annotations( save_semantic_segmentation: bool = False, ) -> None: - # Instance (Objects) + # Instance (Objects) for ann in object_ann: if ann.sample_data_token not in data_list: continue @@ -112,9 +119,9 @@ def update_detection_data_annotations( flat_polygons = [] for cnt in contours: - if len(cnt) >= 3: + if len(cnt) >= 3: poly = cnt.reshape(-1, 2).tolist() # [[x1,y1], [x2,y2], ...] - poly_flat = [coord for point in poly for coord in point] + poly_flat = [coord for point in poly for coord in point] flat_polygons.append(poly_flat) instance = Instance( @@ -129,10 +136,10 @@ def update_detection_data_annotations( if save_semantic_segmentation: for ann in surface_ann: class_name = class_mappings.get(categories[ann.category_token], None) - + if class_name not in allowed_classes: continue - + bbox_label = allowed_classes.index(class_name) binary = ann.mask.decode().astype(np.uint8) @@ -146,10 +153,10 @@ def update_detection_data_annotations( flat_polygons.append(poly_flat) surface_instance = Instance( - bbox=ann.bbox if hasattr(ann, 'bbox') else [0,0,0,0], + bbox=ann.bbox if hasattr(ann, "bbox") else [0, 0, 0, 0], bbox_label=bbox_label, mask=flat_polygons, - extra_anns=[] + extra_anns=[], ) if ann.sample_data_token in data_list: @@ -159,15 +166,20 @@ def update_detection_data_annotations( colormap = generate_colormap(len(allowed_classes) + 1) for key, entry in data_list.items(): gray_mask = convert_entry_instances_to_semantic(entry, allowed_classes) - mask_file = f"{root_path}/semseg/masks/{(entry.img_path).split('/')[-4] +'_' + (entry.img_path).split('/')[-2] +'_' + (entry.img_path).split('/')[-1]}".replace('.jpg', '.png') + mask_file = f"{root_path}/semseg/masks/{(entry.img_path).split('/')[-4] +'_' + (entry.img_path).split('/')[-2] +'_' + (entry.img_path).split('/')[-1]}".replace( + ".jpg", ".png" + ) os.makedirs(os.path.dirname(mask_file), exist_ok=True) cv2.imwrite(mask_file, gray_mask.astype(np.uint8)) entry.gt_semantic_seg = mask_file if save_colored_masks: - color_file = f"{root_path}/semseg/masks_color/{(entry.img_path).split('/')[-5] +'_' +(entry.img_path).split('/')[-2] +'_' + (entry.img_path).split('/')[-1]}".replace('.jpg', '.png') + color_file = f"{root_path}/semseg/masks_color/{(entry.img_path).split('/')[-5] +'_' +(entry.img_path).split('/')[-2] +'_' + (entry.img_path).split('/')[-1]}".replace( + ".jpg", ".png" + ) save_colored_mask(gray_mask, color_file, colormap) + def get_scene_root_dir_path( root_path: str, dataset_version: str, @@ -284,19 +296,19 @@ def main() -> None: t4_dataset_id = scene_id.strip() t4_dataset_version_id = None - if t4_dataset_version_id and os.path.exists(osp.join(args.root_path, t4_dataset_id, t4_dataset_version_id)): + if t4_dataset_version_id and os.path.exists( + osp.join(args.root_path, t4_dataset_id, t4_dataset_version_id) + ): scene_root_dir_path = osp.join(args.root_path, t4_dataset_id, t4_dataset_version_id) elif os.path.exists(osp.join(args.root_path, dataset_version, t4_dataset_id)): print( f"Warning: {t4_dataset_id} has no t4_dataset_version_id or the specified version is missing. " "Using the available version on disk." ) - + scene_root_dir_path = get_scene_root_dir_path(args.root_path, dataset_version, t4_dataset_id) else: - raise ValueError( - f"{t4_dataset_id} does not exist." - ) + raise ValueError(f"{t4_dataset_id} does not exist.") t4 = Tier4( data_root=scene_root_dir_path,