From cb72761a3b97764432c121dcb0dfb75910f9be61 Mon Sep 17 00:00:00 2001 From: Burhan <62214284+Burhan-Q@users.noreply.github.com> Date: Mon, 22 Jan 2024 13:07:38 -0500 Subject: [PATCH] Bounding Box to OBB conversion (#7572) Co-authored-by: UltralyticsAssistant Co-authored-by: Glenn Jocher Co-authored-by: Laughing <61612323+Laughing-q@users.noreply.github.com> Co-authored-by: Laughing-q <1185102784@qq.com> --- ultralytics/data/converter.py | 61 +++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/ultralytics/data/converter.py b/ultralytics/data/converter.py index 1a875f3b..aa3c43e5 100644 --- a/ultralytics/data/converter.py +++ b/ultralytics/data/converter.py @@ -474,3 +474,64 @@ def merge_multi_segment(segments): nidx = abs(idx[1] - idx[0]) s.append(segments[i][nidx:]) return s + + +def yolo_bbox2segment(im_dir, save_dir=None, sam_model="sam_b.pt"): + """ + Converts existing object detection dataset (bounding boxes) to segmentation dataset or oriented bounding box (OBB) + in YOLO format. Generates segmentation data using SAM auto-annotator as needed. + + Args: + im_dir (str | Path): Path to image directory to convert. + save_dir (str | Path): Path to save the generated labels, labels will be saved + into `labels-segment` in the same directory level of `im_dir` if save_dir is None. Default: None. + sam_model (str): Segmentation model to use for intermediate segmentation data; optional. + + Notes: + The input directory structure assumed for dataset: + - im_dir + ├─ 001.jpg + ├─ .. + ├─ NNN.jpg + - labels + ├─ 001.txt + ├─ .. + ├─ NNN.txt + """ + from ultralytics.data import YOLODataset + from ultralytics.utils.ops import xywh2xyxy + from ultralytics.utils import LOGGER + from ultralytics import SAM + from tqdm import tqdm + + # NOTE: add placeholder to pass class index check + dataset = YOLODataset(im_dir, data=dict(names=list(range(1000)))) + if len(dataset.labels[0]["segments"]) > 0: # if it's segment data + LOGGER.info("Segmentation labels detected, no need to generate new ones!") + return + + LOGGER.info("Detection labels detected, generating segment labels by SAM model!") + sam_model = SAM(sam_model) + for l in tqdm(dataset.labels, total=len(dataset.labels), desc="Generating segment labels"): + h, w = l["shape"] + boxes = l["bboxes"] + boxes[:, [0, 2]] *= w + boxes[:, [1, 3]] *= h + im = cv2.imread(l["im_file"]) + sam_results = sam_model(im, bboxes=xywh2xyxy(boxes), verbose=False, save=False) + l["segments"] = sam_results[0].masks.xyn + + save_dir = Path(save_dir) if save_dir else Path(im_dir).parent / "labels-segment" + save_dir.mkdir(parents=True, exist_ok=True) + for l in dataset.labels: + texts = [] + lb_name = Path(l["im_file"]).with_suffix(".txt").name + txt_file = save_dir / lb_name + cls = l["cls"] + for i, s in enumerate(l["segments"]): + line = (int(cls[i]), *s.reshape(-1)) + texts.append(("%g " * len(line)).rstrip() % line) + if texts: + with open(txt_file, "a") as f: + f.writelines(text + "\n" for text in texts) + LOGGER.info(f"Generated segment labels saved in {save_dir}")