Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -25,11 +25,11 @@ def parse_roboflow_url(url: str):
|
|
25 |
return workspace, project, version
|
26 |
|
27 |
|
28 |
-
def convert_seg_to_bbox(api_key: str, dataset_url: str
|
29 |
"""
|
30 |
1) Download segmentation dataset from Roboflow
|
31 |
2) Convert each mask to its bounding box (YOLO format)
|
32 |
-
3)
|
33 |
4) Return before/after visuals plus (dataset_path, detection_slug)
|
34 |
"""
|
35 |
rf = Roboflow(api_key=api_key)
|
@@ -38,41 +38,48 @@ def convert_seg_to_bbox(api_key: str, dataset_url: str, split_ratios=(0.8, 0.1,
|
|
38 |
dataset = version_obj.download("coco-segmentation")
|
39 |
root = dataset.location
|
40 |
|
41 |
-
#
|
42 |
-
|
43 |
for dp, _, files in os.walk(root):
|
44 |
for f in files:
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
|
|
|
|
|
|
|
|
56 |
id_to_index = {cid: idx for idx, cid in enumerate(cat_ids)}
|
57 |
|
58 |
-
#
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
|
|
|
|
|
|
76 |
file_paths = {
|
77 |
f: os.path.join(dp, f)
|
78 |
for dp, _, files in os.walk(root)
|
@@ -80,57 +87,49 @@ def convert_seg_to_bbox(api_key: str, dataset_url: str, split_ratios=(0.8, 0.1,
|
|
80 |
if f in name_to_id
|
81 |
}
|
82 |
|
83 |
-
#
|
84 |
-
all_files = list(name_to_id.keys())
|
85 |
-
random.shuffle(all_files)
|
86 |
-
n = len(all_files)
|
87 |
-
n_train = max(1, int(n * split_ratios[0]))
|
88 |
-
n_valid = max(1, int(n * split_ratios[1]))
|
89 |
-
n_valid = min(n_valid, n - n_train - 1)
|
90 |
-
splits = {
|
91 |
-
"train": all_files[:n_train],
|
92 |
-
"valid": all_files[n_train:n_train+n_valid],
|
93 |
-
"test": all_files[n_train+n_valid:]
|
94 |
-
}
|
95 |
-
|
96 |
-
# create Roboflow‐style dataset folder:
|
97 |
-
# out_root/
|
98 |
-
# train/images, train/labels,
|
99 |
-
# valid/images, valid/labels,
|
100 |
-
# test/images, test/labels
|
101 |
out_root = tempfile.mkdtemp(prefix="yolov8_")
|
102 |
-
for split
|
|
|
103 |
img_dir = os.path.join(out_root, split, "images")
|
104 |
lbl_dir = os.path.join(out_root, split, "labels")
|
105 |
os.makedirs(img_dir, exist_ok=True)
|
106 |
os.makedirs(lbl_dir, exist_ok=True)
|
107 |
-
for
|
108 |
-
|
109 |
shutil.copy(file_paths[fname], os.path.join(img_dir, fname))
|
110 |
-
|
111 |
-
|
112 |
-
with open(os.path.join(lbl_dir, fname.rsplit('.',1)[0] + ".txt"), 'w') as f:
|
113 |
-
f.write(txt)
|
114 |
|
115 |
-
#
|
116 |
before, after = [], []
|
117 |
-
|
118 |
-
|
|
|
|
|
119 |
img = cv2.cvtColor(cv2.imread(file_paths[fname]), cv2.COLOR_BGR2RGB)
|
|
|
|
|
120 |
seg_vis = img.copy()
|
121 |
-
for
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
|
|
|
|
|
|
|
|
126 |
box_vis = img.copy()
|
127 |
-
for line in
|
128 |
_, cxn, cyn, wnorm, hnorm = map(float, line.split())
|
129 |
-
iw
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
|
|
|
|
134 |
before.append(Image.fromarray(seg_vis))
|
135 |
after.append(Image.fromarray(box_vis))
|
136 |
|
@@ -146,7 +145,7 @@ def upload_and_train_detection(
|
|
146 |
project_type: str = "object-detection"
|
147 |
):
|
148 |
"""
|
149 |
-
Uploads the converted dataset (with
|
150 |
creates or fetches a detection project, and kicks off training.
|
151 |
Returns the hosted model URL.
|
152 |
"""
|
@@ -214,7 +213,7 @@ def upload_and_train_detection(
|
|
214 |
|
215 |
# --- Gradio UI ---
|
216 |
with gr.Blocks() as app:
|
217 |
-
gr.Markdown("## 🔄 Seg→BBox + Auto
|
218 |
|
219 |
api_input = gr.Textbox(label="Roboflow API Key", type="password")
|
220 |
url_input = gr.Textbox(label="Segmentation Dataset URL")
|
|
|
25 |
return workspace, project, version
|
26 |
|
27 |
|
28 |
+
def convert_seg_to_bbox(api_key: str, dataset_url: str):
|
29 |
"""
|
30 |
1) Download segmentation dataset from Roboflow
|
31 |
2) Convert each mask to its bounding box (YOLO format)
|
32 |
+
3) Preserve original train/valid/test splits
|
33 |
4) Return before/after visuals plus (dataset_path, detection_slug)
|
34 |
"""
|
35 |
rf = Roboflow(api_key=api_key)
|
|
|
38 |
dataset = version_obj.download("coco-segmentation")
|
39 |
root = dataset.location
|
40 |
|
41 |
+
# 1) Locate all three split JSON files
|
42 |
+
json_files = {}
|
43 |
for dp, _, files in os.walk(root):
|
44 |
for f in files:
|
45 |
+
lf = f.lower()
|
46 |
+
if not lf.endswith('.json'):
|
47 |
+
continue
|
48 |
+
if 'train' in lf:
|
49 |
+
json_files['train'] = os.path.join(dp, f)
|
50 |
+
elif 'valid' in lf or 'val' in lf:
|
51 |
+
json_files['valid'] = os.path.join(dp, f)
|
52 |
+
elif 'test' in lf:
|
53 |
+
json_files['test'] = os.path.join(dp, f)
|
54 |
+
if any(k not in json_files for k in ('train', 'valid', 'test')):
|
55 |
+
raise RuntimeError(f"Missing one of train/valid/test JSONs: {json_files}")
|
56 |
+
|
57 |
+
# 2) Build category → index mapping from the train split
|
58 |
+
train_coco = json.load(open(json_files['train'], 'r'))
|
59 |
+
cat_ids = sorted(c['id'] for c in train_coco.get('categories', []))
|
60 |
id_to_index = {cid: idx for idx, cid in enumerate(cat_ids)}
|
61 |
|
62 |
+
# 3) Aggregate ALL image info & annotations into global dicts
|
63 |
+
global_images_info = {}
|
64 |
+
global_annos = {}
|
65 |
+
for split, jf in json_files.items():
|
66 |
+
coco = json.load(open(jf, 'r'))
|
67 |
+
for img in coco['images']:
|
68 |
+
global_images_info[img['id']] = img
|
69 |
+
for anno in coco['annotations']:
|
70 |
+
xs = anno['segmentation'][0][0::2]
|
71 |
+
ys = anno['segmentation'][0][1::2]
|
72 |
+
xmin, xmax = min(xs), max(xs)
|
73 |
+
ymin, ymax = min(ys), max(ys)
|
74 |
+
w, h = xmax - xmin, ymax - ymin
|
75 |
+
cx, cy = xmin + w/2, ymin + h/2
|
76 |
+
iw = global_images_info[anno['image_id']]['width']
|
77 |
+
ih = global_images_info[anno['image_id']]['height']
|
78 |
+
line = f"{id_to_index[anno['category_id']]} {cx/iw:.6f} {cy/ih:.6f} {w/iw:.6f} {h/ih:.6f}"
|
79 |
+
global_annos.setdefault(anno['image_id'], []).append(line)
|
80 |
+
|
81 |
+
# 4) Build a quick map of filename → full path
|
82 |
+
name_to_id = {img['file_name']: img['id'] for img in global_images_info.values()}
|
83 |
file_paths = {
|
84 |
f: os.path.join(dp, f)
|
85 |
for dp, _, files in os.walk(root)
|
|
|
87 |
if f in name_to_id
|
88 |
}
|
89 |
|
90 |
+
# 5) Copy images & write YOLO .txt labels, preserving original splits
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
out_root = tempfile.mkdtemp(prefix="yolov8_")
|
92 |
+
for split in ('train', 'valid', 'test'):
|
93 |
+
coco = json.load(open(json_files[split], 'r'))
|
94 |
img_dir = os.path.join(out_root, split, "images")
|
95 |
lbl_dir = os.path.join(out_root, split, "labels")
|
96 |
os.makedirs(img_dir, exist_ok=True)
|
97 |
os.makedirs(lbl_dir, exist_ok=True)
|
98 |
+
for img in coco['images']:
|
99 |
+
fname = img['file_name']
|
100 |
shutil.copy(file_paths[fname], os.path.join(img_dir, fname))
|
101 |
+
with open(os.path.join(lbl_dir, fname.rsplit('.', 1)[0] + ".txt"), 'w') as f:
|
102 |
+
f.write("\n".join(global_annos.get(img['id'], [])))
|
|
|
|
|
103 |
|
104 |
+
# 6) Prepare a few before/after examples (random sample across all splits)
|
105 |
before, after = [], []
|
106 |
+
all_ids = list(global_images_info.keys())
|
107 |
+
sample_ids = random.sample(all_ids, min(5, len(all_ids)))
|
108 |
+
for img_id in sample_ids:
|
109 |
+
fname = global_images_info[img_id]['file_name']
|
110 |
img = cv2.cvtColor(cv2.imread(file_paths[fname]), cv2.COLOR_BGR2RGB)
|
111 |
+
|
112 |
+
# draw segmentation outlines
|
113 |
seg_vis = img.copy()
|
114 |
+
for jf in json_files.values():
|
115 |
+
coco = json.load(open(jf, 'r'))
|
116 |
+
for anno in coco['annotations']:
|
117 |
+
if anno['image_id'] != img_id:
|
118 |
+
continue
|
119 |
+
pts = np.array(anno['segmentation'][0], np.int32).reshape(-1, 2)
|
120 |
+
cv2.polylines(seg_vis, [pts], True, (255, 0, 0), 2)
|
121 |
+
|
122 |
+
# draw bounding boxes
|
123 |
box_vis = img.copy()
|
124 |
+
for line in global_annos.get(img_id, []):
|
125 |
_, cxn, cyn, wnorm, hnorm = map(float, line.split())
|
126 |
+
iw = global_images_info[img_id]['width']
|
127 |
+
ih = global_images_info[img_id]['height']
|
128 |
+
w0, h0 = int(wnorm * iw), int(hnorm * ih)
|
129 |
+
x0 = int(cxn * iw - w0 / 2)
|
130 |
+
y0 = int(cyn * ih - h0 / 2)
|
131 |
+
cv2.rectangle(box_vis, (x0, y0), (x0 + w0, y0 + h0), (0, 255, 0), 2)
|
132 |
+
|
133 |
before.append(Image.fromarray(seg_vis))
|
134 |
after.append(Image.fromarray(box_vis))
|
135 |
|
|
|
145 |
project_type: str = "object-detection"
|
146 |
):
|
147 |
"""
|
148 |
+
Uploads the converted dataset (with preserved splits) to Roboflow,
|
149 |
creates or fetches a detection project, and kicks off training.
|
150 |
Returns the hosted model URL.
|
151 |
"""
|
|
|
213 |
|
214 |
# --- Gradio UI ---
|
215 |
with gr.Blocks() as app:
|
216 |
+
gr.Markdown("## 🔄 Seg→BBox + Auto-Upload/Train")
|
217 |
|
218 |
api_input = gr.Textbox(label="Roboflow API Key", type="password")
|
219 |
url_input = gr.Textbox(label="Segmentation Dataset URL")
|