wuhp commited on
Commit
8193bd7
·
verified ·
1 Parent(s): 5bacccd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +73 -74
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, split_ratios=(0.8, 0.1, 0.1)):
29
  """
30
  1) Download segmentation dataset from Roboflow
31
  2) Convert each mask to its bounding box (YOLO format)
32
- 3) Split into train/valid/test
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
- # find the COCO JSON
42
- ann_file = None
43
  for dp, _, files in os.walk(root):
44
  for f in files:
45
- if f.lower().endswith('.json'):
46
- ann_file = os.path.join(dp, f)
47
- break
48
- if ann_file:
49
- break
50
- if not ann_file:
51
- raise FileNotFoundError(f"No JSON found under {root}")
52
-
53
- coco = json.load(open(ann_file, 'r'))
54
- images_info = {img['id']: img for img in coco['images']}
55
- cat_ids = sorted(c['id'] for c in coco.get('categories', []))
 
 
 
 
56
  id_to_index = {cid: idx for idx, cid in enumerate(cat_ids)}
57
 
58
- # build YOLO bboxes
59
- annos = {}
60
- for anno in coco['annotations']:
61
- img_id = anno['image_id']
62
- xs, ys = anno['segmentation'][0][0::2], anno['segmentation'][0][1::2]
63
- xmin, xmax = min(xs), max(xs)
64
- ymin, ymax = min(ys), max(ys)
65
- w, h = xmax - xmin, ymax - ymin
66
- cx, cy = xmin + w/2, ymin + h/2
67
- iw, ih = images_info[img_id]['width'], images_info[img_id]['height']
68
- line = (
69
- f"{id_to_index[anno['category_id']]} "
70
- f"{cx/iw:.6f} {cy/ih:.6f} {w/iw:.6f} {h/ih:.6f}"
71
- )
72
- annos.setdefault(img_id, []).append(line)
73
-
74
- # gather all filenames and paths
75
- name_to_id = {img['file_name']: img['id'] for img in coco['images']}
 
 
 
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
- # split filenames
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, files in splits.items():
 
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 fname in files:
108
- # copy image
109
  shutil.copy(file_paths[fname], os.path.join(img_dir, fname))
110
- # write label
111
- txt = "\n".join(annos.get(name_to_id[fname], []))
112
- with open(os.path.join(lbl_dir, fname.rsplit('.',1)[0] + ".txt"), 'w') as f:
113
- f.write(txt)
114
 
115
- # prepare a few before/after examples
116
  before, after = [], []
117
- sample = random.sample(all_files, min(5, len(all_files)))
118
- for fname in sample:
 
 
119
  img = cv2.cvtColor(cv2.imread(file_paths[fname]), cv2.COLOR_BGR2RGB)
 
 
120
  seg_vis = img.copy()
121
- for anno in coco['annotations']:
122
- if anno['image_id'] != name_to_id[fname]:
123
- continue
124
- pts = np.array(anno['segmentation'][0], np.int32).reshape(-1,2)
125
- cv2.polylines(seg_vis, [pts], True, (255,0,0), 2)
 
 
 
 
126
  box_vis = img.copy()
127
- for line in annos.get(name_to_id[fname], []):
128
  _, cxn, cyn, wnorm, hnorm = map(float, line.split())
129
- iw, ih = images_info[name_to_id[fname]]['width'], images_info[name_to_id[fname]]['height']
130
- w0, h0 = int(wnorm*iw), int(hnorm*ih)
131
- x0 = int(cxn*iw - w0/2)
132
- y0 = int(cyn*ih - h0/2)
133
- cv2.rectangle(box_vis, (x0,y0), (x0+w0,y0+h0), (0,255,0), 2)
 
 
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 train/valid/test splits) to Roboflow,
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 + AutoUpload/Train")
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")