wuhp commited on
Commit
ee953c5
·
verified ·
1 Parent(s): 84c4ee0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +67 -67
app.py CHANGED
@@ -9,28 +9,31 @@ import cv2
9
  import numpy as np
10
  from PIL import Image
11
  import gradio as gr
12
- from roboflow import Roboflow
13
 
14
 
15
  def parse_roboflow_url(url: str):
 
 
 
16
  parsed = urlparse(url)
17
  parts = parsed.path.strip('/').split('/')
18
- workspace = parts[0]
19
- project = parts[1]
20
  try:
21
- version = int(parts[-1])
22
  except ValueError:
23
- version = int(parts[-2])
24
- return workspace, project, version
25
 
26
 
27
  def convert_seg_to_bbox(api_key: str, dataset_url: str, split_ratios=(0.8, 0.1, 0.1)):
28
  # --- download segmentation export
29
- rf = Roboflow(api_key=api_key)
30
- ws, proj_name, ver = parse_roboflow_url(dataset_url)
31
- version_obj = rf.workspace(ws).project(proj_name).version(ver)
32
- dataset = version_obj.download("coco-segmentation")
33
- root = dataset.location
34
 
35
  # --- find the COCO JSON
36
  ann_file = None
@@ -42,39 +45,38 @@ def convert_seg_to_bbox(api_key: str, dataset_url: str, split_ratios=(0.8, 0.1,
42
  if ann_file:
43
  break
44
  if not ann_file:
45
- raise FileNotFoundError(f"No JSON annotations under {root}")
46
 
47
  coco = json.load(open(ann_file, 'r'))
48
  images_info = {img['id']: img for img in coco['images']}
49
  cat_ids = sorted(c['id'] for c in coco.get('categories', []))
50
  id_to_index = {cid: idx for idx, cid in enumerate(cat_ids)}
51
 
52
- # --- prepare flat_images + flat_labels
53
  out_root = tempfile.mkdtemp(prefix="yolov8_")
54
  flat_img = os.path.join(out_root, "flat_images")
55
  flat_lbl = os.path.join(out_root, "flat_labels")
56
  os.makedirs(flat_img, exist_ok=True)
57
  os.makedirs(flat_lbl, exist_ok=True)
58
 
59
- # --- convert each segmentation → YOLO bbox line
60
  annos = {}
61
  for anno in coco['annotations']:
62
  img_id = anno['image_id']
63
  poly = anno['segmentation'][0]
64
  xs, ys = poly[0::2], poly[1::2]
65
- x_min, x_max = min(xs), max(xs)
66
- y_min, y_max = min(ys), max(ys)
67
- w, h = x_max - x_min, y_max - y_min
68
- cx, cy = x_min + w/2, y_min + h/2
69
 
70
- iw, ih = images_info[img_id]['width'], images_info[img_id]['height']
71
- line = (
72
  f"{id_to_index[anno['category_id']]} "
73
  f"{cx/iw:.6f} {cy/ih:.6f} {w/iw:.6f} {h/ih:.6f}"
74
  )
75
  annos.setdefault(img_id, []).append(line)
76
 
77
- # --- map each file_name to its actual path on disk
78
  name_to_id = {img['file_name']: img['id'] for img in coco['images']}
79
  file_paths = {}
80
  for dp, _, files in os.walk(root):
@@ -82,26 +84,22 @@ def convert_seg_to_bbox(api_key: str, dataset_url: str, split_ratios=(0.8, 0.1,
82
  if f in name_to_id:
83
  file_paths[f] = os.path.join(dp, f)
84
 
85
- # --- copy images + write flat_labels
86
  for fname, img_id in name_to_id.items():
87
- src_path = file_paths.get(fname)
88
- if not src_path:
89
- # skip if we couldn't find this image under root
90
  continue
91
- shutil.copy(src_path, os.path.join(flat_img, fname))
92
  with open(os.path.join(flat_lbl, fname.rsplit('.',1)[0] + ".txt"), 'w') as lf:
93
  lf.write("\n".join(annos.get(img_id, [])))
94
 
95
- # --- split into train/valid/test
96
- all_files = sorted(
97
- f for f in os.listdir(flat_img)
98
- if f.lower().endswith(('.jpg','.png','.jpeg'))
99
- )
100
  random.shuffle(all_files)
101
  n = len(all_files)
102
  n_train = max(1, int(n * split_ratios[0]))
103
  n_valid = max(1, int(n * split_ratios[1]))
104
- # ensure at least 1 left for test
105
  n_valid = min(n_valid, n - n_train - 1)
106
 
107
  splits = {
@@ -110,28 +108,23 @@ def convert_seg_to_bbox(api_key: str, dataset_url: str, split_ratios=(0.8, 0.1,
110
  "test": all_files[n_train+n_valid:]
111
  }
112
 
113
- # --- build Roboflowfriendly folder structure
114
  for split, files in splits.items():
115
- out_img_dir = os.path.join(out_root, "images", split)
116
- out_lbl_dir = os.path.join(out_root, "labels", split)
117
- os.makedirs(out_img_dir, exist_ok=True)
118
- os.makedirs(out_lbl_dir, exist_ok=True)
119
  for fn in files:
120
- shutil.move(
121
- os.path.join(flat_img, fn),
122
- os.path.join(out_img_dir, fn)
123
- )
124
- lbl_fn = fn.rsplit('.',1)[0] + ".txt"
125
- shutil.move(
126
- os.path.join(flat_lbl, lbl_fn),
127
- os.path.join(out_lbl_dir, lbl_fn)
128
- )
129
 
130
- # --- clean up the flat dirs
131
  shutil.rmtree(flat_img)
132
  shutil.rmtree(flat_lbl)
133
 
134
- # --- prepare a few before/after visuals
135
  before, after = [], []
136
  sample = random.sample(list(name_to_id.keys()), min(5, len(name_to_id)))
137
  for fname in sample:
@@ -159,32 +152,37 @@ def convert_seg_to_bbox(api_key: str, dataset_url: str, split_ratios=(0.8, 0.1,
159
  before.append(Image.fromarray(seg_vis))
160
  after.append(Image.fromarray(box_vis))
161
 
162
- project_slug = f"{proj_name}-detection"
163
- return before, after, out_root, project_slug
164
 
165
 
166
  def upload_and_train_detection(
167
  api_key: str,
 
168
  project_slug: str,
169
  dataset_path: str,
170
  project_license: str = "MIT",
171
  project_type: str = "object-detection"
172
  ):
173
  rf = Roboflow(api_key=api_key)
174
- ws = rf.workspace()
175
 
176
- # getorcreate your detection project
177
  try:
178
  proj = ws.project(project_slug)
179
- except:
180
- proj = ws.create_project(
181
- project_slug,
182
- annotation=project_type,
183
- project_type=project_type,
184
- project_license=project_license
185
- )
 
 
 
 
186
 
187
- # upload the folder with proper train/valid/test
188
  ws.upload_dataset(
189
  dataset_path,
190
  project_slug,
@@ -192,34 +190,36 @@ def upload_and_train_detection(
192
  project_type=project_type
193
  )
194
 
195
- # create a new version & queue training
196
  version_num = proj.generate_version(settings={
197
  "augmentation": {},
198
  "preprocessing": {},
199
  })
200
  proj.version(str(version_num)).train()
201
 
202
- # return the hosted endpoint URL
203
  m = proj.version(str(version_num)).model
204
  return f"{m['base_url']}{m['id']}?api_key={api_key}"
205
 
206
 
207
  # --- Gradio UI ---
208
  with gr.Blocks() as app:
209
- gr.Markdown("## 🔄 Seg→BBox + AutoUpload/Train")
210
 
211
  api_input = gr.Textbox(label="Roboflow API Key", type="password")
212
  url_input = gr.Textbox(label="Segmentation Dataset URL")
213
  run_btn = gr.Button("Convert to BBoxes")
214
  before_g = gr.Gallery(columns=5, label="Before")
215
  after_g = gr.Gallery(columns=5, label="After")
216
- ds_state = gr.Textbox(visible=False)
217
- slug_state = gr.Textbox(visible=False)
 
 
 
218
 
219
  run_btn.click(
220
  convert_seg_to_bbox,
221
  inputs=[api_input, url_input],
222
- outputs=[before_g, after_g, ds_state, slug_state]
223
  )
224
 
225
  gr.Markdown("## 🚀 Upload & Train Detection Model")
@@ -228,7 +228,7 @@ with gr.Blocks() as app:
228
 
229
  train_btn.click(
230
  upload_and_train_detection,
231
- inputs=[api_input, slug_state, ds_state],
232
  outputs=[url_out]
233
  )
234
 
 
9
  import numpy as np
10
  from PIL import Image
11
  import gradio as gr
12
+ from roboflow import Roboflow, RoboflowError
13
 
14
 
15
  def parse_roboflow_url(url: str):
16
+ """
17
+ Extract (workspace, project slug, version) from any Roboflow URL.
18
+ """
19
  parsed = urlparse(url)
20
  parts = parsed.path.strip('/').split('/')
21
+ ws = parts[0]
22
+ proj = parts[1]
23
  try:
24
+ ver = int(parts[-1])
25
  except ValueError:
26
+ ver = int(parts[-2])
27
+ return ws, proj, ver
28
 
29
 
30
  def convert_seg_to_bbox(api_key: str, dataset_url: str, split_ratios=(0.8, 0.1, 0.1)):
31
  # --- download segmentation export
32
+ rf = Roboflow(api_key=api_key)
33
+ workspace, proj_name, ver = parse_roboflow_url(dataset_url)
34
+ version_obj = rf.workspace(workspace).project(proj_name).version(ver)
35
+ dataset = version_obj.download("coco-segmentation")
36
+ root = dataset.location
37
 
38
  # --- find the COCO JSON
39
  ann_file = None
 
45
  if ann_file:
46
  break
47
  if not ann_file:
48
+ raise FileNotFoundError(f"No JSON annotations found under {root}")
49
 
50
  coco = json.load(open(ann_file, 'r'))
51
  images_info = {img['id']: img for img in coco['images']}
52
  cat_ids = sorted(c['id'] for c in coco.get('categories', []))
53
  id_to_index = {cid: idx for idx, cid in enumerate(cat_ids)}
54
 
55
+ # --- flatten + convert to YOLO bboxes
56
  out_root = tempfile.mkdtemp(prefix="yolov8_")
57
  flat_img = os.path.join(out_root, "flat_images")
58
  flat_lbl = os.path.join(out_root, "flat_labels")
59
  os.makedirs(flat_img, exist_ok=True)
60
  os.makedirs(flat_lbl, exist_ok=True)
61
 
 
62
  annos = {}
63
  for anno in coco['annotations']:
64
  img_id = anno['image_id']
65
  poly = anno['segmentation'][0]
66
  xs, ys = poly[0::2], poly[1::2]
67
+ xmin, xmax = min(xs), max(xs)
68
+ ymin, ymax = min(ys), max(ys)
69
+ w, h = xmax - xmin, ymax - ymin
70
+ cx, cy = xmin + w/2, ymin + h/2
71
 
72
+ iw, ih = images_info[img_id]['width'], images_info[img_id]['height']
73
+ line = (
74
  f"{id_to_index[anno['category_id']]} "
75
  f"{cx/iw:.6f} {cy/ih:.6f} {w/iw:.6f} {h/ih:.6f}"
76
  )
77
  annos.setdefault(img_id, []).append(line)
78
 
79
+ # --- map filenames to their disk paths
80
  name_to_id = {img['file_name']: img['id'] for img in coco['images']}
81
  file_paths = {}
82
  for dp, _, files in os.walk(root):
 
84
  if f in name_to_id:
85
  file_paths[f] = os.path.join(dp, f)
86
 
87
+ # --- copy images and write YOLO .txt labels
88
  for fname, img_id in name_to_id.items():
89
+ src = file_paths.get(fname)
90
+ if not src:
 
91
  continue
92
+ shutil.copy(src, os.path.join(flat_img, fname))
93
  with open(os.path.join(flat_lbl, fname.rsplit('.',1)[0] + ".txt"), 'w') as lf:
94
  lf.write("\n".join(annos.get(img_id, [])))
95
 
96
+ # --- split into train/val/test
97
+ all_files = sorted(f for f in os.listdir(flat_img)
98
+ if f.lower().endswith(('.jpg','.png','.jpeg')))
 
 
99
  random.shuffle(all_files)
100
  n = len(all_files)
101
  n_train = max(1, int(n * split_ratios[0]))
102
  n_valid = max(1, int(n * split_ratios[1]))
 
103
  n_valid = min(n_valid, n - n_train - 1)
104
 
105
  splits = {
 
108
  "test": all_files[n_train+n_valid:]
109
  }
110
 
111
+ # --- arrange into Roboflowfriendly folder tree
112
  for split, files in splits.items():
113
+ idir = os.path.join(out_root, "images", split)
114
+ ldir = os.path.join(out_root, "labels", split)
115
+ os.makedirs(idir, exist_ok=True)
116
+ os.makedirs(ldir, exist_ok=True)
117
  for fn in files:
118
+ shutil.move(os.path.join(flat_img, fn),
119
+ os.path.join(idir, fn))
120
+ lbl = fn.rsplit('.',1)[0] + ".txt"
121
+ shutil.move(os.path.join(flat_lbl, lbl),
122
+ os.path.join(ldir, lbl))
 
 
 
 
123
 
 
124
  shutil.rmtree(flat_img)
125
  shutil.rmtree(flat_lbl)
126
 
127
+ # --- make a few before/after visual samples
128
  before, after = [], []
129
  sample = random.sample(list(name_to_id.keys()), min(5, len(name_to_id)))
130
  for fname in sample:
 
152
  before.append(Image.fromarray(seg_vis))
153
  after.append(Image.fromarray(box_vis))
154
 
155
+ # return samples + local folder + the two slugs we need downstream
156
+ return before, after, out_root, proj_name + "-detection", workspace
157
 
158
 
159
  def upload_and_train_detection(
160
  api_key: str,
161
+ workspace: str,
162
  project_slug: str,
163
  dataset_path: str,
164
  project_license: str = "MIT",
165
  project_type: str = "object-detection"
166
  ):
167
  rf = Roboflow(api_key=api_key)
168
+ ws = rf.workspace(workspace)
169
 
170
+ # --- getorcreate project
171
  try:
172
  proj = ws.project(project_slug)
173
+ except RoboflowError as e:
174
+ # only create if truly “not found”
175
+ if "does not exist" in str(e):
176
+ proj = ws.create_project(
177
+ project_slug,
178
+ annotation=project_type,
179
+ project_type=project_type,
180
+ project_license=project_license
181
+ )
182
+ else:
183
+ raise
184
 
185
+ # --- upload the new train/val/test
186
  ws.upload_dataset(
187
  dataset_path,
188
  project_slug,
 
190
  project_type=project_type
191
  )
192
 
193
+ # --- spin up a new version and start training
194
  version_num = proj.generate_version(settings={
195
  "augmentation": {},
196
  "preprocessing": {},
197
  })
198
  proj.version(str(version_num)).train()
199
 
 
200
  m = proj.version(str(version_num)).model
201
  return f"{m['base_url']}{m['id']}?api_key={api_key}"
202
 
203
 
204
  # --- Gradio UI ---
205
  with gr.Blocks() as app:
206
+ gr.Markdown("## 🔄 Seg→BBox + AutoUpload/Train")
207
 
208
  api_input = gr.Textbox(label="Roboflow API Key", type="password")
209
  url_input = gr.Textbox(label="Segmentation Dataset URL")
210
  run_btn = gr.Button("Convert to BBoxes")
211
  before_g = gr.Gallery(columns=5, label="Before")
212
  after_g = gr.Gallery(columns=5, label="After")
213
+
214
+ # hidden states
215
+ ds_state = gr.Textbox(visible=False) # local dataset folder
216
+ slug_state = gr.Textbox(visible=False) # project‑slug e.g. "myproj-detection"
217
+ ws_state = gr.Textbox(visible=False) # workspace name
218
 
219
  run_btn.click(
220
  convert_seg_to_bbox,
221
  inputs=[api_input, url_input],
222
+ outputs=[before_g, after_g, ds_state, slug_state, ws_state]
223
  )
224
 
225
  gr.Markdown("## 🚀 Upload & Train Detection Model")
 
228
 
229
  train_btn.click(
230
  upload_and_train_detection,
231
+ inputs=[api_input, ws_state, slug_state, ds_state],
232
  outputs=[url_out]
233
  )
234