Initial commit: Wood knot detection model and GUI
This commit is contained in:
128
convert_to_label_studio.py
Normal file
128
convert_to_label_studio.py
Normal file
@ -0,0 +1,128 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from PIL import Image
|
||||
|
||||
|
||||
def coco_to_label_studio(
|
||||
image_paths: list[Path],
|
||||
coco_predictions_path: Path,
|
||||
category_name: str = "knot",
|
||||
) -> list[dict]:
|
||||
"""Convert COCO predictions to Label Studio import format with pre-annotations."""
|
||||
|
||||
# Load COCO predictions
|
||||
with coco_predictions_path.open("r", encoding="utf-8") as f:
|
||||
coco_data = json.load(f)
|
||||
|
||||
# Build image_id -> annotations mapping
|
||||
annotations_by_image = {}
|
||||
for ann in coco_data.get("annotations", []):
|
||||
img_id = ann["image_id"]
|
||||
if img_id not in annotations_by_image:
|
||||
annotations_by_image[img_id] = []
|
||||
annotations_by_image[img_id].append(ann)
|
||||
|
||||
# Build image_id -> image info mapping
|
||||
image_info = {img["id"]: img for img in coco_data.get("images", [])}
|
||||
|
||||
# Convert to Label Studio format
|
||||
ls_tasks = []
|
||||
for img_id, img_data in image_info.items():
|
||||
file_name = img_data["file_name"]
|
||||
width = img_data["width"]
|
||||
height = img_data["height"]
|
||||
|
||||
# Find the actual file path
|
||||
matching_paths = [p for p in image_paths if p.name == file_name]
|
||||
if not matching_paths:
|
||||
continue
|
||||
|
||||
img_path = matching_paths[0]
|
||||
|
||||
# Build predictions (pre-annotations)
|
||||
predictions = []
|
||||
for ann in annotations_by_image.get(img_id, []):
|
||||
# COCO bbox: [x_min, y_min, width, height]
|
||||
x, y, w, h = ann["bbox"]
|
||||
|
||||
# Label Studio uses percentages
|
||||
x_percent = (x / width) * 100
|
||||
y_percent = (y / height) * 100
|
||||
w_percent = (w / width) * 100
|
||||
h_percent = (h / height) * 100
|
||||
|
||||
predictions.append({
|
||||
"value": {
|
||||
"x": x_percent,
|
||||
"y": y_percent,
|
||||
"width": w_percent,
|
||||
"height": h_percent,
|
||||
"rectanglelabels": [category_name]
|
||||
},
|
||||
"from_name": "label",
|
||||
"to_name": "image",
|
||||
"type": "rectanglelabels",
|
||||
"score": ann.get("score", 1.0)
|
||||
})
|
||||
|
||||
# Create Label Studio task with predictions
|
||||
task = {
|
||||
"data": {
|
||||
"image": f"/data/local-files/?d={img_path.absolute()}"
|
||||
},
|
||||
"predictions": [{
|
||||
"result": predictions,
|
||||
"model_version": "rfdetr-auto-label"
|
||||
}]
|
||||
}
|
||||
|
||||
ls_tasks.append(task)
|
||||
|
||||
return ls_tasks
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Convert COCO predictions to Label Studio format for review."
|
||||
)
|
||||
parser.add_argument("--coco-json", type=Path, required=True, help="COCO predictions JSON from auto_label_images.py")
|
||||
parser.add_argument("--images-dir", type=Path, required=True, help="Directory with the images")
|
||||
parser.add_argument("--output-json", type=Path, required=True, help="Output Label Studio tasks JSON")
|
||||
parser.add_argument("--category-name", default="knot", help="Label name in Label Studio")
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.coco_json.exists():
|
||||
raise SystemExit(f"COCO JSON not found: {args.coco_json}")
|
||||
if not args.images_dir.exists():
|
||||
raise SystemExit(f"Images dir not found: {args.images_dir}")
|
||||
|
||||
# Collect all images
|
||||
image_paths = list(args.images_dir.glob("*.jpg")) + list(args.images_dir.glob("*.png"))
|
||||
if not image_paths:
|
||||
raise SystemExit(f"No images found in {args.images_dir}")
|
||||
|
||||
# Convert
|
||||
ls_tasks = coco_to_label_studio(image_paths, args.coco_json, args.category_name)
|
||||
|
||||
# Write Label Studio import format
|
||||
with args.output_json.open("w", encoding="utf-8") as f:
|
||||
json.dump(ls_tasks, f, indent=2)
|
||||
|
||||
print(f"✓ Converted {len(ls_tasks)} tasks with predictions")
|
||||
print(f"✓ Output: {args.output_json}")
|
||||
print(f"\nImport into Label Studio:")
|
||||
print(f" 1. Open your project")
|
||||
print(f" 2. Settings → Storage → Add Local Storage")
|
||||
print(f" Absolute path: {args.images_dir.absolute()}")
|
||||
print(f" 3. Import → Upload Files → select {args.output_json.name}")
|
||||
print(f" 4. Start reviewing predictions!")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user