File size: 6,273 Bytes
bc6cd18
 
 
 
 
 
43b1849
bc6cd18
 
43b1849
bc6cd18
 
 
 
43b1849
bc6cd18
 
 
 
 
 
 
 
 
 
 
 
43b1849
 
bc6cd18
43b1849
 
bc6cd18
0425447
 
 
228a29d
0425447
 
228a29d
 
 
 
 
6d361b8
7e2847c
6d361b8
7e2847c
 
 
 
99c4ece
6d361b8
43b1849
 
228a29d
99c4ece
6d361b8
99c4ece
 
 
 
 
 
 
 
 
 
 
6d361b8
43b1849
bc6cd18
 
 
 
43b1849
6d361b8
bc6cd18
 
43b1849
bc6cd18
 
 
 
 
0425447
bc6cd18
43b1849
bc6cd18
 
 
 
6d361b8
bc6cd18
 
6d361b8
 
43b1849
bc6cd18
43b1849
 
 
 
 
bc6cd18
 
99c4ece
6d361b8
7e2847c
 
 
0425447
6d361b8
228a29d
6d361b8
43b1849
6d361b8
43b1849
bc6cd18
 
6d361b8
 
bc6cd18
6d361b8
43b1849
 
 
bc6cd18
43b1849
 
6d361b8
 
 
 
bc6cd18
 
 
 
99c4ece
43b1849
 
bc6cd18
 
 
6d361b8
bc6cd18
43b1849
 
 
 
bc6cd18
43b1849
 
bc6cd18
43b1849
bc6cd18
 
43b1849
bc6cd18
 
7e2847c
 
 
6d361b8
 
7838f2e
 
6d361b8
7838f2e
 
 
bc6cd18
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
import os
import json
import random
import shutil
import tempfile
from urllib.parse import urlparse

import cv2
import numpy as np
from PIL import Image
import gradio as gr
from roboflow import Roboflow


def parse_roboflow_url(url: str):
    """Extract workspace, project name, and version from a Roboflow URL."""
    parsed = urlparse(url)
    parts = parsed.path.strip('/').split('/')
    workspace = parts[0]
    project = parts[1]
    try:
        version = int(parts[-1])
    except ValueError:
        version = int(parts[-2])
    return workspace, project, version


def convert_seg_to_bbox(api_key: str, dataset_url: str):
    # 1) download the dataset
    rf = Roboflow(api_key=api_key)
    ws, proj, ver = parse_roboflow_url(dataset_url)
    version_obj = rf.workspace(ws).project(proj).version(ver)
    dataset = version_obj.download("coco-segmentation")
    root = dataset.location  # e.g. "/home/user/app/ds-2"

    # --- DEBUG: print out the downloaded directory tree ---
    print(f"\n=== Downloaded dataset root: {root} ===")
    for dirpath, dirnames, filenames in os.walk(root):
        print(f"\nDirectory: {dirpath}")
        for d in dirnames:
            print(f"  [DIR ] {d}")
        for f in filenames:
            print(f"  [FILE] {f}")
    print("=== end tree dump ===\n")

    # 2) search for any JSON file with "train" in its name
    ann_file = None
    for dirpath, _, filenames in os.walk(root):
        for fname in filenames:
            if 'train' in fname.lower() and fname.lower().endswith('.json'):
                ann_file = os.path.join(dirpath, fname)
                print(f"Found TRAIN annotation file: {ann_file}")
                break
        if ann_file:
            break

    # 2b) fallback: first .json anywhere
    if ann_file is None:
        for dirpath, _, filenames in os.walk(root):
            for fname in filenames:
                if fname.lower().endswith('.json'):
                    ann_file = os.path.join(dirpath, fname)
                    print(f"No TRAIN file—falling back to first JSON: {ann_file}")
                    break
            if ann_file:
                break

    if ann_file is None:
        raise FileNotFoundError(f"No JSON annotations found under {root}")

    # 3) load COCO annotations
    with open(ann_file, 'r') as f:
        coco = json.load(f)
    images_info = {img['id']: img for img in coco['images']}

    # 4) build category→index map
    cat_ids = sorted(c['id'] for c in coco.get('categories', []))
    id_to_index = {cid: idx for idx, cid in enumerate(cat_ids)}

    # 5) prepare YOLO output directories
    out_root = tempfile.mkdtemp(prefix="yolov8_")
    img_out = os.path.join(out_root, "images")
    lbl_out = os.path.join(out_root, "labels")
    os.makedirs(img_out, exist_ok=True)
    os.makedirs(lbl_out, exist_ok=True)
    print(f"Preparing YOLOv8 output in: {out_root}")

    # 6) convert each segmentation annotation to a YOLO bbox line
    annos = {}
    for anno in coco['annotations']:
        img_id = anno['image_id']
        poly = anno['segmentation'][0]
        xs, ys = poly[0::2], poly[1::2]
        x_min, x_max = min(xs), max(xs)
        y_min, y_max = min(ys), max(ys)
        w, h = x_max - x_min, y_max - y_min
        cx, cy = x_min + w/2, y_min + h/2

        info = images_info[img_id]
        iw, ih = info['width'], info['height']
        line = (
            f"{id_to_index[anno['category_id']]} "
            f"{cx/iw:.6f} {cy/ih:.6f} {w/iw:.6f} {h/ih:.6f}"
        )
        annos.setdefault(img_id, []).append(line)

    # 7) locate your image folder (first with any .jpg/.png)
    train_img_dir = None
    for dirpath, _, files in os.walk(root):
        if any(f.lower().endswith(('.jpg', '.png', '.jpeg')) for f in files):
            train_img_dir = dirpath
            print(f"Found image directory: {train_img_dir}")
            break

    if train_img_dir is None:
        raise FileNotFoundError(f"No image files found under {root}")

    # 8) copy images + write labels
    name_to_id = {img['file_name']: img['id'] for img in coco['images']}
    for fname, img_id in name_to_id.items():
        src = os.path.join(train_img_dir, fname)
        if not os.path.isfile(src):
            continue
        shutil.copy(src, os.path.join(img_out, fname))
        lbl_path = os.path.join(lbl_out, fname.rsplit('.', 1)[0] + ".txt")
        with open(lbl_path, 'w') as lf:
            lf.write("\n".join(annos.get(img_id, [])))

    # 9) build before/after galleries
    before, after = [], []
    sample = random.sample(list(name_to_id.keys()), min(5, len(name_to_id)))
    for fname in sample:
        src = os.path.join(train_img_dir, fname)
        img = cv2.cvtColor(cv2.imread(src), cv2.COLOR_BGR2RGB)

        seg_vis = img.copy()
        img_id = name_to_id[fname]
        for anno in coco['annotations']:
            if anno['image_id'] != img_id: continue
            pts = np.array(anno['segmentation'][0], dtype=np.int32).reshape(-1, 2)
            cv2.polylines(seg_vis, [pts], True, (255, 0, 0), 2)

        box_vis = img.copy()
        for line in annos.get(img_id, []):
            _, cxn, cyn, wnorm, hnorm = map(float, line.split())
            iw, ih = images_info[img_id]['width'], images_info[img_id]['height']
            w0, h0 = int(wnorm * iw), int(hnorm * ih)
            x0 = int(cxn * iw - w0/2)
            y0 = int(cyn * ih - h0/2)
            cv2.rectangle(box_vis, (x0, y0), (x0 + w0, y0 + h0), (0, 255, 0), 2)

        before.append(Image.fromarray(seg_vis))
        after.append(Image.fromarray(box_vis))

    return before, after


# --- Gradio app ---
with gr.Blocks() as app:
    gr.Markdown("# Segmentation → YOLOv8 Converter")
    api_input      = gr.Textbox(label="Roboflow API Key", type="password")
    url_input      = gr.Textbox(label="Roboflow Dataset URL (Segmentation)")
    run_btn        = gr.Button("Convert")
    before_gallery = gr.Gallery(label="Before (Segmentation)", columns=5, height="auto")
    after_gallery  = gr.Gallery(label="After (Bounding Boxes)", columns=5, height="auto")

    run_btn.click(
        fn=convert_seg_to_bbox,
        inputs=[api_input, url_input],
        outputs=[before_gallery, after_gallery]
    )

if __name__ == "__main__":
    app.launch()