Spaces:
Sleeping
Sleeping
import os | |
import json | |
import math | |
import random | |
import shutil | |
import tempfile | |
from urllib.parse import urlparse | |
from PIL import Image | |
import cv2 | |
import numpy as np | |
import gradio as gr | |
from roboflow import Roboflow | |
def parse_roboflow_url(url): | |
"""Extract workspace, project name, and version from a Roboflow URL.""" | |
parsed = urlparse(url) | |
parts = parsed.path.strip('/').split('/') | |
# Expect at least [workspace, project, ..., version] | |
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, dataset_url): | |
# Initialize Roboflow client | |
rf = Roboflow(api_key=api_key) | |
workspace, project_name, version = parse_roboflow_url(dataset_url) | |
project = rf.workspace(workspace).project(project_name) | |
version_obj = project.version(version) | |
# Download the segmentation dataset in COCO format | |
dataset = version_obj.download("coco-segmentation") | |
root = dataset.location # root of downloaded dataset | |
# Load COCO train annotations | |
ann_dir = os.path.join(root, "coco-annotations") | |
ann_file = os.path.join(ann_dir, "train.json") | |
with open(ann_file, 'r') as f: | |
coco = json.load(f) | |
images_info = {img['id']: img for img in coco['images']} | |
# Map original category IDs to contiguous YOLO class indices | |
categories = coco.get('categories', []) | |
cat_ids = sorted(cat['id'] for cat in categories) | |
id_to_index = {cid: idx for idx, cid in enumerate(cat_ids)} | |
# Prepare output directories for YOLOv8 dataset | |
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) | |
# Build YOLO annotation strings grouped by image | |
annos = {} | |
for anno in coco['annotations']: | |
img_id = anno['image_id'] | |
poly = anno['segmentation'][0] | |
xs = poly[0::2] | |
ys = poly[1::2] | |
x_min, x_max = min(xs), max(xs) | |
y_min, y_max = min(ys), max(ys) | |
width = x_max - x_min | |
height = y_max - y_min | |
cx = x_min + width / 2 | |
cy = y_min + height / 2 | |
info = images_info[img_id] | |
img_w, img_h = info['width'], info['height'] | |
cxn = cx / img_w | |
cyn = cy / img_h | |
wnorm = width / img_w | |
hnorm = height / img_h | |
cls_idx = id_to_index[anno['category_id']] | |
line = f"{cls_idx} {cxn:.6f} {cyn:.6f} {wnorm:.6f} {hnorm:.6f}" | |
annos.setdefault(img_id, []).append(line) | |
# Determine train images directory | |
train_img_dir = os.path.join(root, "train", "images") | |
if not os.path.isdir(train_img_dir): | |
train_img_dir = os.path.join(root, "train") | |
# Map filenames to image IDs | |
name_to_id = {img['file_name']: img['id'] for img in coco['images']} | |
# Copy images and write YOLO label files | |
for fname, img_id in name_to_id.items(): | |
src_img = os.path.join(train_img_dir, fname) | |
if not os.path.isfile(src_img): | |
continue | |
dst_img = os.path.join(img_out, fname) | |
shutil.copy(src_img, dst_img) | |
lbl_path = os.path.join(lbl_out, os.path.splitext(fname)[0] + ".txt") | |
with open(lbl_path, 'w') as lf: | |
for line in annos.get(img_id, []): | |
lf.write(line + '\n') | |
# Prepare before/after example galleries | |
before_imgs, after_imgs = [], [] | |
example_files = random.sample(list(name_to_id.keys()), min(5, len(name_to_id))) | |
for fname in example_files: | |
src_img = os.path.join(train_img_dir, fname) | |
img = cv2.cvtColor(cv2.imread(src_img), cv2.COLOR_BGR2RGB) | |
# Overlay segmentation polygons | |
seg_vis = img.copy() | |
img_id = name_to_id[fname] | |
for anno in coco['annotations']: | |
if anno['image_id'] != img_id: | |
continue | |
poly = anno['segmentation'][0] | |
pts = np.array(poly, dtype=np.int32).reshape(-1, 2) | |
cv2.polylines(seg_vis, [pts], True, (255, 0, 0), 2) | |
# Overlay bounding boxes | |
box_vis = img.copy() | |
for line in annos.get(img_id, []): | |
_, cxn, cyn, wnorm, hnorm = line.split() | |
cxn, cyn, wnorm, hnorm = map(float, (cxn, cyn, wnorm, hnorm)) | |
iw, ih = images_info[img_id]['width'], images_info[img_id]['height'] | |
w0 = int(wnorm * iw) | |
h0 = 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_imgs.append(Image.fromarray(seg_vis)) | |
after_imgs.append(Image.fromarray(box_vis)) | |
return before_imgs, after_imgs | |
# Build Gradio interface | |
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)").style(grid=[5], height="auto") | |
after_gallery = gr.Gallery(label="After (Bounding Boxes)").style(grid=[5], height="auto") | |
run_btn.click(convert_seg_to_bbox, inputs=[api_input, url_input], outputs=[before_gallery, after_gallery]) | |
if __name__ == "__main__": | |
app.launch() | |