segtodetect / app.py
wuhp's picture
Update app.py
99c4ece verified
raw
history blame
6.27 kB
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()