rfcli 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
rfcli/__init__.py ADDED
File without changes
rfcli/config.py ADDED
@@ -0,0 +1,9 @@
1
+ import os
2
+ from dotenv import load_dotenv
3
+
4
+ load_dotenv()
5
+
6
+ ROBOFLOW_API_KEY = os.getenv("ROBOFLOW_API_KEY")
7
+
8
+ if not ROBOFLOW_API_KEY:
9
+ raise ValueError("❌ ROBOFLOW_API_KEY not found in .env")
rfcli/convert.py ADDED
@@ -0,0 +1,51 @@
1
+ import os
2
+ import json
3
+
4
+
5
+ def coco_to_yolo(json_path, dataset_path):
6
+
7
+ labels_dir = os.path.join(dataset_path, "labels")
8
+ os.makedirs(labels_dir, exist_ok=True)
9
+
10
+ with open(json_path) as f:
11
+ data = json.load(f)
12
+
13
+ images = {img["id"]: img for img in data["images"]}
14
+ annotations = data["annotations"]
15
+
16
+ categories = data["categories"]
17
+ cat_map = {cat["id"]: i for i, cat in enumerate(categories)}
18
+
19
+ ann_by_image = {}
20
+
21
+ for ann in annotations:
22
+ ann_by_image.setdefault(ann["image_id"], []).append(ann)
23
+
24
+ for img_id, img_data in images.items():
25
+
26
+ file_name = img_data["file_name"]
27
+ width = float(img_data["width"])
28
+ height = float(img_data["height"])
29
+
30
+ label_path = os.path.join(
31
+ labels_dir,
32
+ os.path.splitext(file_name)[0] + ".txt"
33
+ )
34
+
35
+ with open(label_path, "w") as f:
36
+
37
+ for ann in ann_by_image.get(img_id, []):
38
+
39
+ # 🔥 FIX: ensure numeric
40
+ x, y, w, h = map(float, ann["bbox"])
41
+
42
+ x_center = (x + w / 2) / width
43
+ y_center = (y + h / 2) / height
44
+ w /= width
45
+ h /= height
46
+
47
+ class_id = cat_map[ann["category_id"]]
48
+
49
+ f.write(f"{class_id} {x_center} {y_center} {w} {h}\n")
50
+
51
+ print("✅ COCO → YOLO conversion complete!")
rfcli/infer.py ADDED
File without changes
rfcli/main.py ADDED
@@ -0,0 +1,70 @@
1
+ import click
2
+ from rich import print
3
+ from rfcli.upload import upload_images, upload_dataset
4
+ from rfcli.version import create_version
5
+ from rfcli.train_local import train_local
6
+ from rfcli.predict import predict
7
+
8
+
9
+ @click.group()
10
+ def cli():
11
+ """Roboflow CLI - Dataset to Deployment"""
12
+ pass
13
+
14
+
15
+ # 🔹 Single image upload
16
+ @cli.command()
17
+ @click.option('--workspace', required=True)
18
+ @click.option('--project', required=True)
19
+ @click.option('--folder', required=True)
20
+ def upload(workspace, project, folder):
21
+ print("[bold blue]🚀 Uploading images...[/bold blue]")
22
+ upload_images(workspace, project, folder)
23
+
24
+
25
+ # 🔥 Dataset upload
26
+ @cli.command(name="upload-dataset")
27
+ @click.option('--workspace', required=True)
28
+ @click.option('--project', required=True)
29
+ @click.option('--path', required=True)
30
+ def upload_dataset_cmd(workspace, project, path):
31
+ print("[bold blue]🚀 Uploading dataset...[/bold blue]")
32
+ upload_dataset(workspace, project, path)
33
+
34
+
35
+ # 🔥 Create version
36
+ @cli.command(name="create-version")
37
+ @click.option('--workspace', required=True)
38
+ @click.option('--project', required=True)
39
+ @click.option('--name', required=True)
40
+ def create_version_cmd(workspace, project, name):
41
+ print("[bold blue]🚀 Creating dataset version...[/bold blue]")
42
+ create_version(workspace, project, name)
43
+
44
+ #train local
45
+ @cli.command(name="train-local")
46
+ @click.option('--workspace', required=True)
47
+ @click.option('--project', required=True)
48
+ @click.option('--version', required=True, type=int)
49
+ def train_local_cmd(workspace, project, version):
50
+ train_local(workspace, project, version)
51
+
52
+ #predict
53
+ @cli.command(name="predict")
54
+ @click.option('--image', required=True)
55
+ def predict_cmd(image):
56
+ predict(image)
57
+
58
+ @click.group()
59
+ @click.version_option("0.1.0")
60
+ def cli():
61
+ """Roboflow CLI - Dataset to Deployment"""
62
+ pass
63
+
64
+ @cli.command()
65
+ def hello():
66
+ print("[bold green]Roboflow CLI is working! 🚀[/bold green]")
67
+
68
+
69
+ if __name__ == "__main__":
70
+ cli()
rfcli/predict.py ADDED
@@ -0,0 +1,26 @@
1
+ from ultralytics import YOLO
2
+ import cv2
3
+ import os
4
+
5
+
6
+ def predict(image_path):
7
+
8
+ print("🚀 Running inference...")
9
+
10
+ model = YOLO("runs/detect/train/weights/best.pt")
11
+
12
+ results = model(image_path)
13
+
14
+ for r in results:
15
+ boxes = r.boxes
16
+
17
+ for box in boxes:
18
+ print(f"Detected class: {int(box.cls[0])}, Confidence: {float(box.conf[0]):.2f}")
19
+
20
+ # 🔥 Save output instead of showing
21
+ annotated = results[0].plot()
22
+
23
+ output_path = "prediction.jpg"
24
+ cv2.imwrite(output_path, annotated)
25
+
26
+ print(f"\n✅ Prediction saved at: {os.path.abspath(output_path)}")
rfcli/train.py ADDED
File without changes
rfcli/train_local.py ADDED
@@ -0,0 +1,28 @@
1
+ from roboflow import Roboflow
2
+ from ultralytics import YOLO
3
+ from rfcli.config import ROBOFLOW_API_KEY
4
+
5
+
6
+ def train_local(workspace, project, version):
7
+
8
+ print("🚀 Downloading dataset from Roboflow...")
9
+
10
+ rf = Roboflow(api_key=ROBOFLOW_API_KEY)
11
+ dataset = rf.workspace(workspace).project(project).version(version).download("yolov8")
12
+
13
+ print("✅ Dataset downloaded!")
14
+
15
+ print("🚀 Starting YOLO training...")
16
+
17
+ model = YOLO("yolov8n.pt")
18
+
19
+ model.train(
20
+ data=f"{dataset.location}/data.yaml",
21
+ epochs=50,
22
+ imgsz=640
23
+ )
24
+
25
+ print("✅ Training complete!")
26
+
27
+ print("📦 Best model saved at:")
28
+ print("runs/detect/train/weights/best.pt")
rfcli/upload.py ADDED
@@ -0,0 +1,147 @@
1
+ import os
2
+ from rich import print
3
+ from roboflow import Roboflow
4
+ from rfcli.config import ROBOFLOW_API_KEY
5
+ from rfcli.convert import coco_to_yolo
6
+
7
+
8
+ # -----------------------------
9
+ # 🔹 SINGLE IMAGE UPLOAD
10
+ # -----------------------------
11
+ def upload_images(workspace, project_name, folder_path):
12
+
13
+ rf = Roboflow(api_key=ROBOFLOW_API_KEY)
14
+ project = rf.workspace(workspace).project(project_name)
15
+
16
+ images = [f for f in os.listdir(folder_path) if f.lower().endswith((".jpg", ".jpeg", ".png"))]
17
+
18
+ print(f"[blue]Uploading {len(images)} images...[/blue]")
19
+
20
+ for i, img_name in enumerate(images):
21
+ try:
22
+ project.upload(os.path.join(folder_path, img_name))
23
+ if i % 50 == 0:
24
+ print(f"[green]{i} uploaded...[/green]")
25
+ except Exception as e:
26
+ print(f"[red]Failed {img_name}: {e}[/red]")
27
+
28
+
29
+ # -----------------------------
30
+ # 🔥 MAIN DATASET UPLOAD
31
+ # -----------------------------
32
+ def upload_dataset(workspace, project_name, dataset_path):
33
+
34
+ print("[blue]🚀 Initializing Roboflow SDK...[/blue]")
35
+
36
+ rf = Roboflow(api_key=ROBOFLOW_API_KEY)
37
+ project = rf.workspace(workspace).project(project_name)
38
+
39
+ if not os.path.exists(dataset_path):
40
+ print("[red]❌ Invalid dataset path[/red]")
41
+ return
42
+
43
+ # 🔥 CASE 1: SPLIT DATASET
44
+ splits = ["train", "valid", "test"]
45
+ split_exists = any(os.path.exists(os.path.join(dataset_path, s)) for s in splits)
46
+
47
+ if split_exists:
48
+ print("[cyan]📂 Detected SPLIT dataset[/cyan]")
49
+ _handle_split_dataset(project, dataset_path)
50
+ return
51
+
52
+ # 🔥 CASE 2: COCO JSON in root
53
+ json_files = [f for f in os.listdir(dataset_path) if f.endswith(".json")]
54
+
55
+ if json_files:
56
+ print("[cyan]📦 Detected COCO dataset[/cyan]")
57
+
58
+ json_path = os.path.join(dataset_path, json_files[0])
59
+ coco_to_yolo(json_path, dataset_path)
60
+
61
+ # 🔥 CASE 3: YOLO FLAT DATASET
62
+ print("[cyan]📁 Detected YOLO flat dataset[/cyan]")
63
+ _handle_flat_dataset(project, dataset_path)
64
+
65
+
66
+ # -----------------------------
67
+ # 📂 HANDLE SPLIT DATASET
68
+ # -----------------------------
69
+ def _handle_split_dataset(project, dataset_path):
70
+
71
+ splits = ["train", "valid", "test"]
72
+
73
+ success, fail = 0, 0
74
+
75
+ for split in splits:
76
+
77
+ split_path = os.path.join(dataset_path, split)
78
+
79
+ if not os.path.exists(split_path):
80
+ continue
81
+
82
+ print(f"\n[bold cyan]Processing {split}...[/bold cyan]")
83
+
84
+ # Convert COCO if exists
85
+ json_files = [f for f in os.listdir(split_path) if f.endswith(".json")]
86
+ if json_files:
87
+ coco_to_yolo(os.path.join(split_path, json_files[0]), split_path)
88
+
89
+ labels_dir = os.path.join(split_path, "labels")
90
+
91
+ images = [f for f in os.listdir(split_path) if f.lower().endswith((".jpg", ".jpeg", ".png"))]
92
+
93
+ for i, img_name in enumerate(images):
94
+
95
+ img_path = os.path.join(split_path, img_name)
96
+ label_path = os.path.join(labels_dir, os.path.splitext(img_name)[0] + ".txt")
97
+
98
+ try:
99
+ if os.path.exists(label_path):
100
+ project.upload(img_path, annotation_path=label_path, split=split)
101
+ else:
102
+ project.upload(img_path, split=split)
103
+
104
+ success += 1
105
+
106
+ if success % 10 == 0:
107
+ print(f"[green]{success} uploaded...[/green]")
108
+
109
+ except Exception as e:
110
+ print(f"[red]Failed {img_name}: {e}[/red]")
111
+ fail += 1
112
+
113
+ print(f"\n[bold]Done → Success: {success}, Failed: {fail}[/bold]")
114
+
115
+
116
+ # -----------------------------
117
+ # 📁 HANDLE FLAT DATASET
118
+ # -----------------------------
119
+ def _handle_flat_dataset(project, dataset_path):
120
+
121
+ images = [f for f in os.listdir(dataset_path) if f.lower().endswith((".jpg", ".jpeg", ".png"))]
122
+
123
+ labels_dir = os.path.join(dataset_path, "labels")
124
+
125
+ success, fail = 0, 0
126
+
127
+ for i, img_name in enumerate(images):
128
+
129
+ img_path = os.path.join(dataset_path, img_name)
130
+ label_path = os.path.join(labels_dir, os.path.splitext(img_name)[0] + ".txt")
131
+
132
+ try:
133
+ if os.path.exists(label_path):
134
+ project.upload(img_path, annotation_path=label_path, split="train")
135
+ else:
136
+ project.upload(img_path)
137
+
138
+ success += 1
139
+
140
+ if success % 100 == 0:
141
+ print(f"[green]{success} uploaded...[/green]")
142
+
143
+ except Exception as e:
144
+ print(f"[red]Failed {img_name}: {e}[/red]")
145
+ fail += 1
146
+
147
+ print(f"\n[bold]Done → Success: {success}, Failed: {fail}[/bold]")
rfcli/utils.py ADDED
File without changes
rfcli/version.py ADDED
@@ -0,0 +1,47 @@
1
+ from rich import print
2
+ from roboflow import Roboflow
3
+ from rfcli.config import ROBOFLOW_API_KEY
4
+
5
+
6
+ def create_version(workspace, project_name, version_name):
7
+
8
+ print("[blue]🚀 Creating dataset version with split...[/blue]")
9
+
10
+ try:
11
+ rf = Roboflow(api_key=ROBOFLOW_API_KEY)
12
+ project = rf.workspace(workspace).project(project_name)
13
+
14
+ # 🔥 Create version with splits
15
+ version = project.generate_version(
16
+ {
17
+ "name": version_name,
18
+
19
+ # ✅ SPLIT CONFIG
20
+ "splits": {
21
+ "train": 70,
22
+ "valid": 20,
23
+ "test": 10
24
+ },
25
+
26
+ # ✅ PREPROCESSING
27
+ "preprocessing": {
28
+ "resize": {
29
+ "width": 640,
30
+ "height": 640,
31
+ "format": "Stretch to"
32
+ }
33
+ },
34
+
35
+ # (optional)
36
+ "augmentation": {}
37
+ }
38
+ )
39
+
40
+ print("[green]✅ Version created successfully![/green]")
41
+ print(f"[bold]Version Name:[/bold] {version_name}")
42
+
43
+ print("\n[cyan]📊 Version Info:[/cyan]")
44
+ print(version)
45
+
46
+ except Exception as e:
47
+ print(f"[red]❌ Failed to create version: {str(e)}[/red]")
@@ -0,0 +1,18 @@
1
+ Metadata-Version: 2.4
2
+ Name: rfcli
3
+ Version: 0.1.0
4
+ Summary: Dataset-to-Deployment CLI for Object Detection
5
+ Author: Your Name
6
+ Requires-Python: >=3.8
7
+ Requires-Dist: click
8
+ Requires-Dist: rich
9
+ Requires-Dist: roboflow
10
+ Requires-Dist: ultralytics
11
+ Requires-Dist: fastapi
12
+ Requires-Dist: uvicorn
13
+ Requires-Dist: python-multipart
14
+ Requires-Dist: opencv-python
15
+ Dynamic: author
16
+ Dynamic: requires-dist
17
+ Dynamic: requires-python
18
+ Dynamic: summary
@@ -0,0 +1,16 @@
1
+ rfcli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ rfcli/config.py,sha256=HIGrW9zdsi_eSsWQtQX-VILHbaB2i6n8FtCZ9iB_KCs,202
3
+ rfcli/convert.py,sha256=ERR2FzInpfBNeLreYXxWDVIHoEA8v62dWrH-35TLkhI,1407
4
+ rfcli/infer.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ rfcli/main.py,sha256=8DSjLVCF1QsoHnaNfOOmu7oYQyi0jupuFNb2H8Mbiqw,2021
6
+ rfcli/predict.py,sha256=KIVOB1k0EOHpxGMpetU-YVmrd4ECeayPYClZsisSXOo,623
7
+ rfcli/train.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ rfcli/train_local.py,sha256=rnLiHBkX6pACmz_P7Q_X67WoPOpmezE9ZYFhw0Z3oAo,713
9
+ rfcli/upload.py,sha256=-DDbvPmf6ElpK9CmYtitvzl61VuWI7Kzbf5F3G9LxMw,4757
10
+ rfcli/utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ rfcli/version.py,sha256=iG6ER0lHXo3MYKkTQzKV02VoDk6wsuoX7MqLaRg5tqQ,1369
12
+ rfcli-0.1.0.dist-info/METADATA,sha256=BKOPzb9c8u50NJTTFitVpkARzt8ST5HzNV7tccyo-LU,444
13
+ rfcli-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
14
+ rfcli-0.1.0.dist-info/entry_points.txt,sha256=0Chj2OlbTO3opMPJxQnu48Za9FEj5pqaa14wCs6DWq0,41
15
+ rfcli-0.1.0.dist-info/top_level.txt,sha256=kUvqUMu0dwIFSNVQTJxO142ucSH2mXty47hD4LM8U-I,6
16
+ rfcli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ rfcli = rfcli.main:cli
@@ -0,0 +1 @@
1
+ rfcli