Skip to content

Commit 4e30788

Browse files
authored
Merge pull request #386 from roboflow/tony/multilabel-upload
multilabel classification upload with the CLI 😎
2 parents 40e2241 + 52e9344 commit 4e30788

19 files changed

+4911
-5
lines changed

roboflow/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from roboflow.models import CLIPModel, GazeModel # noqa: F401
1616
from roboflow.util.general import write_line
1717

18-
__version__ = "1.1.65"
18+
__version__ = "1.1.66"
1919

2020

2121
def check_key(api_key, model, notebook, num_retries=0):

roboflow/core/workspace.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,8 @@ def _save_annotation(image_id, imagedesc):
364364
if isinstance(annotationdesc, dict):
365365
if annotationdesc.get("type") == "classification_folder":
366366
annotation_path = annotationdesc.get("classification_label")
367+
elif annotationdesc.get("type") == "classification_multilabel":
368+
annotation_path = json.dumps(annotationdesc.get("labels", []))
367369
elif annotationdesc.get("rawText"):
368370
annotation_path = annotationdesc
369371
elif annotationdesc.get("file"):

roboflow/util/folderparser.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,13 @@ def _filterIndividualAnnotations(image, annotation, format, imgRefMap, annotatio
194194
return _annotation
195195
else:
196196
return None
197+
elif format == "multilabel_csv":
198+
rows = [r for r in parsed["rows"] if r["file_name"] == image["name"]]
199+
if rows:
200+
labels = rows[0]["labels"]
201+
return {"type": "classification_multilabel", "labels": labels}
202+
else:
203+
return None
197204
elif format == "jsonl":
198205
jsonlLines = [json.dumps(line) for line in parsed if line["image"] == image["name"]]
199206
if jsonlLines:
@@ -218,8 +225,9 @@ def _loadAnnotations(folder, annotations):
218225
ann["parsed"] = _read_jsonl(f"{folder}{ann['file']}")
219226
ann["parsedType"] = "jsonl"
220227
elif extension == ".csv":
221-
ann["parsedType"] = "csv"
222-
ann["parsed"] = _parseAnnotationCSV(f"{folder}{ann['file']}")
228+
parsed = _parseAnnotationCSV(f"{folder}{ann['file']}")
229+
ann["parsed"] = parsed
230+
ann["parsedType"] = parsed.get("type", "csv")
223231
return annotations
224232

225233

@@ -241,10 +249,20 @@ def _parseAnnotationCSV(filename):
241249
# TODO: use a proper CSV library?
242250
with open(filename) as f:
243251
lines = f.readlines()
244-
headers = lines[0]
252+
headers = [h.strip() for h in lines[0].split(",")]
253+
# Multi-label classification csv typically named _classes.csv
254+
if os.path.basename(filename) == "_classes.csv":
255+
parsed_lines = []
256+
for line in lines[1:]:
257+
parts = [p.strip() for p in line.split(",")]
258+
file_name = parts[0]
259+
labels = [headers[i] for i, v in enumerate(parts[1:], start=1) if v == "1"]
260+
parsed_lines.append({"file_name": file_name, "labels": labels})
261+
return {"type": "multilabel_csv", "rows": parsed_lines, "headers": headers}
262+
header_line = lines[0]
245263
lines = [{"file_name": ld.split(",")[0].strip(), "line": ld} for ld in lines[1:]]
246264
return {
247-
"headers": headers,
265+
"headers": header_line,
248266
"lines": lines,
249267
}
250268

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Skin-Problem-MultiLabel > 2023-12-26 4:26pm
2+
https://universe.roboflow.com/parin-kittipongdaja-vwmn3/skin-problem-multilabel
3+
4+
Provided by a Roboflow user
5+
License: CC BY 4.0
35.5 KB
Loading
41.2 KB
Loading
Loading

tests/datasets/skinproblem-multilabel-classification/test/_classes.csv

Lines changed: 482 additions & 0 deletions
Large diffs are not rendered by default.
25.7 KB
Loading
44.4 KB
Loading

0 commit comments

Comments
 (0)