ultralytics 8.3.85__py3-none-any.whl → 8.3.87__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.
- tests/test_solutions.py +21 -2
- ultralytics/__init__.py +1 -1
- ultralytics/cfg/__init__.py +17 -25
- ultralytics/cfg/datasets/Argoverse.yaml +15 -13
- ultralytics/cfg/datasets/GlobalWheat2020.yaml +24 -10
- ultralytics/cfg/datasets/ImageNet.yaml +1 -1
- ultralytics/cfg/datasets/Objects365.yaml +21 -21
- ultralytics/cfg/datasets/SKU-110K.yaml +11 -11
- ultralytics/cfg/datasets/VOC.yaml +34 -28
- ultralytics/cfg/datasets/VisDrone.yaml +19 -15
- ultralytics/cfg/datasets/coco-pose.yaml +11 -8
- ultralytics/cfg/datasets/coco.yaml +11 -8
- ultralytics/cfg/datasets/lvis.yaml +12 -8
- ultralytics/cfg/datasets/open-images-v7.yaml +25 -20
- ultralytics/cfg/datasets/xView.yaml +28 -26
- ultralytics/cfg/models/11/yolo11-cls-resnet18.yaml +1 -1
- ultralytics/cfg/models/11/yolo11-cls.yaml +6 -6
- ultralytics/data/annotator.py +1 -1
- ultralytics/data/base.py +1 -1
- ultralytics/data/converter.py +6 -6
- ultralytics/data/loaders.py +1 -1
- ultralytics/data/split_dota.py +2 -2
- ultralytics/data/utils.py +4 -4
- ultralytics/engine/exporter.py +3 -3
- ultralytics/engine/results.py +77 -42
- ultralytics/engine/trainer.py +12 -6
- ultralytics/engine/tuner.py +4 -3
- ultralytics/engine/validator.py +1 -1
- ultralytics/models/yolo/obb/val.py +2 -2
- ultralytics/nn/autobackend.py +3 -2
- ultralytics/nn/tasks.py +1 -1
- ultralytics/solutions/parking_management.py +19 -4
- ultralytics/utils/__init__.py +3 -4
- ultralytics/utils/benchmarks.py +5 -5
- ultralytics/utils/callbacks/comet.py +37 -5
- ultralytics/utils/loss.py +1 -1
- {ultralytics-8.3.85.dist-info → ultralytics-8.3.87.dist-info}/METADATA +8 -8
- {ultralytics-8.3.85.dist-info → ultralytics-8.3.87.dist-info}/RECORD +42 -42
- {ultralytics-8.3.85.dist-info → ultralytics-8.3.87.dist-info}/WHEEL +1 -1
- {ultralytics-8.3.85.dist-info → ultralytics-8.3.87.dist-info}/LICENSE +0 -0
- {ultralytics-8.3.85.dist-info → ultralytics-8.3.87.dist-info}/entry_points.txt +0 -0
- {ultralytics-8.3.85.dist-info → ultralytics-8.3.87.dist-info}/top_level.txt +0 -0
tests/test_solutions.py
CHANGED
@@ -8,8 +8,11 @@ from ultralytics import YOLO, solutions
|
|
8
8
|
from ultralytics.utils import ASSETS_URL, WEIGHTS_DIR
|
9
9
|
from ultralytics.utils.downloads import safe_download
|
10
10
|
|
11
|
-
DEMO_VIDEO = "solutions_ci_demo.mp4"
|
12
|
-
POSE_VIDEO = "solution_ci_pose_demo.mp4"
|
11
|
+
DEMO_VIDEO = "solutions_ci_demo.mp4" # for all the solutions, except workout and parking
|
12
|
+
POSE_VIDEO = "solution_ci_pose_demo.mp4" # only for workouts monitoring solution
|
13
|
+
PARKING_VIDEO = "solution_ci_parking_demo.mp4" # only for parking management solution
|
14
|
+
PARKING_AREAS_JSON = "solution_ci_parking_areas.json" # only for parking management solution
|
15
|
+
PARKING_MODEL = "solutions_ci_parking_model.pt" # only for parking management solution
|
13
16
|
|
14
17
|
|
15
18
|
@pytest.mark.slow
|
@@ -62,6 +65,22 @@ def test_major_solutions():
|
|
62
65
|
_ = gym.monitor(im0)
|
63
66
|
cap.release()
|
64
67
|
|
68
|
+
# Test parking management
|
69
|
+
safe_download(url=f"{ASSETS_URL}/{PARKING_VIDEO}", dir=TMP)
|
70
|
+
safe_download(url=f"{ASSETS_URL}/{PARKING_AREAS_JSON}", dir=TMP)
|
71
|
+
safe_download(url=f"{ASSETS_URL}/{PARKING_MODEL}", dir=TMP)
|
72
|
+
cap = cv2.VideoCapture(str(TMP / PARKING_VIDEO))
|
73
|
+
assert cap.isOpened(), "Error reading video file"
|
74
|
+
parkingmanager = solutions.ParkingManagement(
|
75
|
+
json_file=str(TMP / PARKING_AREAS_JSON), model=str(TMP / PARKING_MODEL), show=False
|
76
|
+
)
|
77
|
+
while cap.isOpened():
|
78
|
+
success, im0 = cap.read()
|
79
|
+
if not success:
|
80
|
+
break
|
81
|
+
_ = parkingmanager.process_data(im0)
|
82
|
+
cap.release()
|
83
|
+
|
65
84
|
|
66
85
|
@pytest.mark.slow
|
67
86
|
def test_instance_segmentation():
|
ultralytics/__init__.py
CHANGED
ultralytics/cfg/__init__.py
CHANGED
@@ -656,7 +656,7 @@ def handle_yolo_solutions(args: List[str]) -> None:
|
|
656
656
|
- For 'analytics' solution, frame numbers are tracked for generating analytical graphs
|
657
657
|
- Video processing can be interrupted by pressing 'q'
|
658
658
|
- Processes video frames sequentially and saves output in .avi format
|
659
|
-
- If no source is specified, downloads and uses a default sample video
|
659
|
+
- If no source is specified, downloads and uses a default sample video
|
660
660
|
- The inference solution will be launched using the 'streamlit run' command.
|
661
661
|
- The Streamlit app file is located in the Ultralytics package directory.
|
662
662
|
"""
|
@@ -677,21 +677,19 @@ def handle_yolo_solutions(args: List[str]) -> None:
|
|
677
677
|
check_dict_alignment(full_args_dict, overrides) # dict alignment
|
678
678
|
|
679
679
|
# Get solution name
|
680
|
-
if args
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
680
|
+
if args[0] == "help":
|
681
|
+
LOGGER.info(SOLUTIONS_HELP_MSG)
|
682
|
+
return # Early return for 'help' case
|
683
|
+
elif args[0] in SOLUTION_MAP:
|
684
|
+
solution_name = args.pop(0) # Extract the solution name directly
|
685
685
|
else:
|
686
686
|
LOGGER.warning(
|
687
|
-
f"
|
687
|
+
f"❌ '{args[0]}' is not a valid solution. 💡 Defaulting to 'count'.\n"
|
688
|
+
f"🚀 Available solutions: {', '.join(list(SOLUTION_MAP.keys())[:-1])}\n"
|
688
689
|
)
|
689
|
-
|
690
|
-
|
691
|
-
if args and args[0] == "help": # Add check for return if user call `yolo solutions help`
|
692
|
-
return
|
690
|
+
solution_name = "count" # Default for invalid solution
|
693
691
|
|
694
|
-
if
|
692
|
+
if solution_name == "inference":
|
695
693
|
checks.check_requirements("streamlit>=1.29.0")
|
696
694
|
LOGGER.info("💡 Loading Ultralytics live inference app...")
|
697
695
|
subprocess.run(
|
@@ -705,10 +703,9 @@ def handle_yolo_solutions(args: List[str]) -> None:
|
|
705
703
|
]
|
706
704
|
)
|
707
705
|
else:
|
708
|
-
|
709
|
-
|
710
|
-
from ultralytics import solutions # import ultralytics solutions
|
706
|
+
from ultralytics import solutions
|
711
707
|
|
708
|
+
cls, method = SOLUTION_MAP[solution_name] # solution class name, method name and default source
|
712
709
|
solution = getattr(solutions, cls)(IS_CLI=True, **overrides) # get solution class i.e ObjectCounter
|
713
710
|
process = getattr(
|
714
711
|
solution, method
|
@@ -717,17 +714,12 @@ def handle_yolo_solutions(args: List[str]) -> None:
|
|
717
714
|
cap = cv2.VideoCapture(solution.CFG["source"]) # read the video file
|
718
715
|
|
719
716
|
# extract width, height and fps of the video file, create save directory and initialize video writer
|
720
|
-
import os # for directory creation
|
721
|
-
from pathlib import Path
|
722
|
-
|
723
|
-
from ultralytics.utils.files import increment_path # for output directory path update
|
724
|
-
|
725
717
|
w, h, fps = (int(cap.get(x)) for x in (cv2.CAP_PROP_FRAME_WIDTH, cv2.CAP_PROP_FRAME_HEIGHT, cv2.CAP_PROP_FPS))
|
726
|
-
if
|
718
|
+
if solution_name == "analytics": # analytical graphs follow fixed shape for output i.e w=1920, h=1080
|
727
719
|
w, h = 1920, 1080
|
728
|
-
save_dir =
|
729
|
-
save_dir.mkdir(parents=True
|
730
|
-
vw = cv2.VideoWriter(
|
720
|
+
save_dir = get_save_dir(SimpleNamespace(project="runs/solutions", name="exp", exist_ok=False))
|
721
|
+
save_dir.mkdir(parents=True) # create the output directory i.e. runs/solutions/exp
|
722
|
+
vw = cv2.VideoWriter(str(save_dir / f"{solution_name}.avi"), cv2.VideoWriter_fourcc(*"mp4v"), fps, (w, h))
|
731
723
|
|
732
724
|
try: # Process video frames
|
733
725
|
f_n = 0 # frame number, required for analytical graphs
|
@@ -735,7 +727,7 @@ def handle_yolo_solutions(args: List[str]) -> None:
|
|
735
727
|
success, frame = cap.read()
|
736
728
|
if not success:
|
737
729
|
break
|
738
|
-
frame = process(frame, f_n := f_n + 1) if
|
730
|
+
frame = process(frame, f_n := f_n + 1) if solution_name == "analytics" else process(frame)
|
739
731
|
vw.write(frame)
|
740
732
|
if cv2.waitKey(1) & 0xFF == ord("q"):
|
741
733
|
break
|
@@ -28,26 +28,28 @@ names:
|
|
28
28
|
# Download script/URL (optional) ---------------------------------------------------------------------------------------
|
29
29
|
download: |
|
30
30
|
import json
|
31
|
+
from pathlib import Path
|
32
|
+
|
31
33
|
from tqdm import tqdm
|
32
34
|
from ultralytics.utils.downloads import download
|
33
|
-
from pathlib import Path
|
34
35
|
|
35
36
|
def argoverse2yolo(set):
|
37
|
+
"""Convert Argoverse dataset annotations to YOLO format for object detection tasks."""
|
36
38
|
labels = {}
|
37
39
|
a = json.load(open(set, "rb"))
|
38
|
-
for annot in tqdm(a[
|
39
|
-
img_id = annot[
|
40
|
-
img_name = a[
|
41
|
-
img_label_name = f
|
40
|
+
for annot in tqdm(a["annotations"], desc=f"Converting {set} to YOLOv5 format..."):
|
41
|
+
img_id = annot["image_id"]
|
42
|
+
img_name = a["images"][img_id]["name"]
|
43
|
+
img_label_name = f"{img_name[:-3]}txt"
|
42
44
|
|
43
|
-
cls = annot[
|
44
|
-
x_center, y_center, width, height = annot[
|
45
|
+
cls = annot["category_id"] # instance class id
|
46
|
+
x_center, y_center, width, height = annot["bbox"]
|
45
47
|
x_center = (x_center + width / 2) / 1920.0 # offset and scale
|
46
48
|
y_center = (y_center + height / 2) / 1200.0 # offset and scale
|
47
49
|
width /= 1920.0 # scale
|
48
50
|
height /= 1200.0 # scale
|
49
51
|
|
50
|
-
img_dir = set.parents[2] /
|
52
|
+
img_dir = set.parents[2] / "Argoverse-1.1" / "labels" / a["seq_dirs"][a["images"][annot["image_id"]]["sid"]]
|
51
53
|
if not img_dir.exists():
|
52
54
|
img_dir.mkdir(parents=True, exist_ok=True)
|
53
55
|
|
@@ -57,19 +59,19 @@ download: |
|
|
57
59
|
labels[k].append(f"{cls} {x_center} {y_center} {width} {height}\n")
|
58
60
|
|
59
61
|
for k in labels:
|
60
|
-
with open(k, "w") as f:
|
62
|
+
with open(k, "w", encoding="utf-8") as f:
|
61
63
|
f.writelines(labels[k])
|
62
64
|
|
63
65
|
|
64
66
|
# Download 'https://argoverse-hd.s3.us-east-2.amazonaws.com/Argoverse-HD-Full.zip' (deprecated S3 link)
|
65
|
-
dir = Path(yaml[
|
66
|
-
urls = [
|
67
|
+
dir = Path(yaml["path"]) # dataset root dir
|
68
|
+
urls = ["https://drive.google.com/file/d/1st9qW3BeIwQsnR0t8mRpvbsSWIo16ACi/view?usp=drive_link"]
|
67
69
|
print("\n\nWARNING: Argoverse dataset MUST be downloaded manually, autodownload will NOT work.")
|
68
70
|
print(f"WARNING: Manually download Argoverse dataset '{urls[0]}' to '{dir}' and re-run your command.\n\n")
|
69
71
|
# download(urls, dir=dir)
|
70
72
|
|
71
73
|
# Convert
|
72
|
-
annotations_dir =
|
73
|
-
(dir /
|
74
|
+
annotations_dir = "Argoverse-HD/annotations/"
|
75
|
+
(dir / "Argoverse-1.1" / "tracking").rename(dir / "Argoverse-1.1" / "images") # rename 'tracking' to 'images'
|
74
76
|
for d in "train.json", "val.json":
|
75
77
|
argoverse2yolo(dir / annotations_dir / d) # convert Argoverse annotations to YOLO labels
|
@@ -32,23 +32,37 @@ names:
|
|
32
32
|
|
33
33
|
# Download script/URL (optional) ---------------------------------------------------------------------------------------
|
34
34
|
download: |
|
35
|
-
from ultralytics.utils.downloads import download
|
36
35
|
from pathlib import Path
|
37
36
|
|
37
|
+
from ultralytics.utils.downloads import download
|
38
|
+
|
38
39
|
# Download
|
39
|
-
dir = Path(yaml[
|
40
|
-
urls = [
|
41
|
-
|
40
|
+
dir = Path(yaml["path"]) # dataset root dir
|
41
|
+
urls = [
|
42
|
+
"https://zenodo.org/record/4298502/files/global-wheat-codalab-official.zip",
|
43
|
+
"https://github.com/ultralytics/assets/releases/download/v0.0.0/GlobalWheat2020_labels.zip",
|
44
|
+
]
|
42
45
|
download(urls, dir=dir)
|
43
46
|
|
44
47
|
# Make Directories
|
45
|
-
for p in
|
48
|
+
for p in "annotations", "images", "labels":
|
46
49
|
(dir / p).mkdir(parents=True, exist_ok=True)
|
47
50
|
|
48
51
|
# Move
|
49
|
-
for p in
|
50
|
-
|
51
|
-
|
52
|
-
|
52
|
+
for p in (
|
53
|
+
"arvalis_1",
|
54
|
+
"arvalis_2",
|
55
|
+
"arvalis_3",
|
56
|
+
"ethz_1",
|
57
|
+
"rres_1",
|
58
|
+
"inrae_1",
|
59
|
+
"usask_1",
|
60
|
+
"utokyo_1",
|
61
|
+
"utokyo_2",
|
62
|
+
"nau_1",
|
63
|
+
"uq_1",
|
64
|
+
):
|
65
|
+
(dir / "global-wheat-codalab-official" / p).rename(dir / "images" / p) # move to /images
|
66
|
+
f = (dir / "global-wheat-codalab-official" / p).with_suffix(".json") # json file
|
53
67
|
if f.exists():
|
54
|
-
f.rename((dir /
|
68
|
+
f.rename((dir / "annotations" / p).with_suffix(".json")) # move to /annotations
|
@@ -384,58 +384,58 @@ names:
|
|
384
384
|
|
385
385
|
# Download script/URL (optional) ---------------------------------------------------------------------------------------
|
386
386
|
download: |
|
387
|
+
from pathlib import Path
|
388
|
+
|
389
|
+
import numpy as np
|
387
390
|
from tqdm import tqdm
|
388
391
|
|
389
392
|
from ultralytics.utils.checks import check_requirements
|
390
393
|
from ultralytics.utils.downloads import download
|
391
394
|
from ultralytics.utils.ops import xyxy2xywhn
|
392
395
|
|
393
|
-
|
394
|
-
from pathlib import Path
|
395
|
-
|
396
|
-
check_requirements(('pycocotools>=2.0',))
|
396
|
+
check_requirements(("pycocotools>=2.0",))
|
397
397
|
from pycocotools.coco import COCO
|
398
398
|
|
399
399
|
# Make Directories
|
400
|
-
dir = Path(yaml[
|
401
|
-
for p in
|
400
|
+
dir = Path(yaml["path"]) # dataset root dir
|
401
|
+
for p in "images", "labels":
|
402
402
|
(dir / p).mkdir(parents=True, exist_ok=True)
|
403
|
-
for q in
|
403
|
+
for q in "train", "val":
|
404
404
|
(dir / p / q).mkdir(parents=True, exist_ok=True)
|
405
405
|
|
406
406
|
# Train, Val Splits
|
407
|
-
for split, patches in [(
|
407
|
+
for split, patches in [("train", 50 + 1), ("val", 43 + 1)]:
|
408
408
|
print(f"Processing {split} in {patches} patches ...")
|
409
|
-
images, labels = dir /
|
409
|
+
images, labels = dir / "images" / split, dir / "labels" / split
|
410
410
|
|
411
411
|
# Download
|
412
412
|
url = f"https://dorc.ks3-cn-beijing.ksyun.com/data-set/2020Objects365%E6%95%B0%E6%8D%AE%E9%9B%86/{split}/"
|
413
|
-
if split ==
|
414
|
-
download([f
|
415
|
-
download([f
|
416
|
-
elif split ==
|
417
|
-
download([f
|
418
|
-
download([f
|
419
|
-
download([f
|
413
|
+
if split == "train":
|
414
|
+
download([f"{url}zhiyuan_objv2_{split}.tar.gz"], dir=dir) # annotations json
|
415
|
+
download([f"{url}patch{i}.tar.gz" for i in range(patches)], dir=images, curl=True, threads=8)
|
416
|
+
elif split == "val":
|
417
|
+
download([f"{url}zhiyuan_objv2_{split}.json"], dir=dir) # annotations json
|
418
|
+
download([f"{url}images/v1/patch{i}.tar.gz" for i in range(15 + 1)], dir=images, curl=True, threads=8)
|
419
|
+
download([f"{url}images/v2/patch{i}.tar.gz" for i in range(16, patches)], dir=images, curl=True, threads=8)
|
420
420
|
|
421
421
|
# Move
|
422
|
-
for f in tqdm(images.rglob(
|
422
|
+
for f in tqdm(images.rglob("*.jpg"), desc=f"Moving {split} images"):
|
423
423
|
f.rename(images / f.name) # move to /images/{split}
|
424
424
|
|
425
425
|
# Labels
|
426
|
-
coco = COCO(dir / f
|
426
|
+
coco = COCO(dir / f"zhiyuan_objv2_{split}.json")
|
427
427
|
names = [x["name"] for x in coco.loadCats(coco.getCatIds())]
|
428
428
|
for cid, cat in enumerate(names):
|
429
429
|
catIds = coco.getCatIds(catNms=[cat])
|
430
430
|
imgIds = coco.getImgIds(catIds=catIds)
|
431
|
-
for im in tqdm(coco.loadImgs(imgIds), desc=f
|
431
|
+
for im in tqdm(coco.loadImgs(imgIds), desc=f"Class {cid + 1}/{len(names)} {cat}"):
|
432
432
|
width, height = im["width"], im["height"]
|
433
433
|
path = Path(im["file_name"]) # image filename
|
434
434
|
try:
|
435
|
-
with open(labels / path.with_suffix(
|
435
|
+
with open(labels / path.with_suffix(".txt").name, "a", encoding="utf-8") as file:
|
436
436
|
annIds = coco.getAnnIds(imgIds=im["id"], catIds=catIds, iscrowd=None)
|
437
437
|
for a in coco.loadAnns(annIds):
|
438
|
-
x, y, w, h = a[
|
438
|
+
x, y, w, h = a["bbox"] # bounding box in xywh (xy top-left corner)
|
439
439
|
xyxy = np.array([x, y, x + w, y + h])[None] # pixels(1,4)
|
440
440
|
x, y, w, h = xyxy2xywhn(xyxy, w=width, h=height, clip=True)[0] # normalized and clipped
|
441
441
|
file.write(f"{cid} {x:.5f} {y:.5f} {w:.5f} {h:.5f}\n")
|
@@ -31,27 +31,27 @@ download: |
|
|
31
31
|
from ultralytics.utils.ops import xyxy2xywh
|
32
32
|
|
33
33
|
# Download
|
34
|
-
dir = Path(yaml[
|
34
|
+
dir = Path(yaml["path"]) # dataset root dir
|
35
35
|
parent = Path(dir.parent) # download dir
|
36
|
-
urls = [
|
36
|
+
urls = ["http://trax-geometry.s3.amazonaws.com/cvpr_challenge/SKU110K_fixed.tar.gz"]
|
37
37
|
download(urls, dir=parent)
|
38
38
|
|
39
39
|
# Rename directories
|
40
40
|
if dir.exists():
|
41
41
|
shutil.rmtree(dir)
|
42
|
-
(parent /
|
43
|
-
(dir /
|
42
|
+
(parent / "SKU110K_fixed").rename(dir) # rename dir
|
43
|
+
(dir / "labels").mkdir(parents=True, exist_ok=True) # create labels dir
|
44
44
|
|
45
45
|
# Convert labels
|
46
|
-
names =
|
47
|
-
for d in
|
48
|
-
x = pd.read_csv(dir /
|
46
|
+
names = "image", "x1", "y1", "x2", "y2", "class", "image_width", "image_height" # column names
|
47
|
+
for d in "annotations_train.csv", "annotations_val.csv", "annotations_test.csv":
|
48
|
+
x = pd.read_csv(dir / "annotations" / d, names=names).values # annotations
|
49
49
|
images, unique_images = x[:, 0], np.unique(x[:, 0])
|
50
|
-
with open((dir / d).with_suffix(
|
51
|
-
f.writelines(f
|
52
|
-
for im in tqdm(unique_images, desc=f
|
50
|
+
with open((dir / d).with_suffix(".txt").__str__().replace("annotations_", ""), "w", encoding="utf-8") as f:
|
51
|
+
f.writelines(f"./images/{s}\n" for s in unique_images)
|
52
|
+
for im in tqdm(unique_images, desc=f"Converting {dir / d}"):
|
53
53
|
cls = 0 # single-class dataset
|
54
|
-
with open((dir /
|
54
|
+
with open((dir / "labels" / im).with_suffix(".txt"), "a", encoding="utf-8") as f:
|
55
55
|
for r in x[images == im]:
|
56
56
|
w, h = r[6], r[7] # image width, height
|
57
57
|
xywh = xyxy2xywh(np.array([[r[1] / w, r[2] / h, r[3] / w, r[4] / h]]))[0] # instance
|
@@ -46,55 +46,61 @@ names:
|
|
46
46
|
# Download script/URL (optional) ---------------------------------------------------------------------------------------
|
47
47
|
download: |
|
48
48
|
import xml.etree.ElementTree as ET
|
49
|
+
from pathlib import Path
|
49
50
|
|
50
51
|
from tqdm import tqdm
|
52
|
+
|
51
53
|
from ultralytics.utils.downloads import download
|
52
|
-
|
54
|
+
|
53
55
|
|
54
56
|
def convert_label(path, lb_path, year, image_id):
|
57
|
+
"""Converts XML annotations from VOC format to YOLO format by extracting bounding boxes and class IDs."""
|
58
|
+
|
55
59
|
def convert_box(size, box):
|
56
|
-
dw, dh = 1. / size[0], 1. / size[1]
|
60
|
+
dw, dh = 1.0 / size[0], 1.0 / size[1]
|
57
61
|
x, y, w, h = (box[0] + box[1]) / 2.0 - 1, (box[2] + box[3]) / 2.0 - 1, box[1] - box[0], box[3] - box[2]
|
58
62
|
return x * dw, y * dh, w * dw, h * dh
|
59
63
|
|
60
|
-
in_file = open(path / f
|
61
|
-
out_file = open(lb_path,
|
64
|
+
in_file = open(path / f"VOC{year}/Annotations/{image_id}.xml")
|
65
|
+
out_file = open(lb_path, "w")
|
62
66
|
tree = ET.parse(in_file)
|
63
67
|
root = tree.getroot()
|
64
|
-
size = root.find(
|
65
|
-
w = int(size.find(
|
66
|
-
h = int(size.find(
|
68
|
+
size = root.find("size")
|
69
|
+
w = int(size.find("width").text)
|
70
|
+
h = int(size.find("height").text)
|
67
71
|
|
68
|
-
names = list(yaml[
|
69
|
-
for obj in root.iter(
|
70
|
-
cls = obj.find(
|
71
|
-
if cls in names and int(obj.find(
|
72
|
-
xmlbox = obj.find(
|
73
|
-
bb = convert_box((w, h), [float(xmlbox.find(x).text) for x in (
|
72
|
+
names = list(yaml["names"].values()) # names list
|
73
|
+
for obj in root.iter("object"):
|
74
|
+
cls = obj.find("name").text
|
75
|
+
if cls in names and int(obj.find("difficult").text) != 1:
|
76
|
+
xmlbox = obj.find("bndbox")
|
77
|
+
bb = convert_box((w, h), [float(xmlbox.find(x).text) for x in ("xmin", "xmax", "ymin", "ymax")])
|
74
78
|
cls_id = names.index(cls) # class id
|
75
|
-
out_file.write(" ".join(str(a) for a in (cls_id, *bb)) +
|
79
|
+
out_file.write(" ".join(str(a) for a in (cls_id, *bb)) + "\n")
|
76
80
|
|
77
81
|
|
78
82
|
# Download
|
79
|
-
dir = Path(yaml[
|
80
|
-
url =
|
81
|
-
urls = [
|
82
|
-
|
83
|
-
|
84
|
-
|
83
|
+
dir = Path(yaml["path"]) # dataset root dir
|
84
|
+
url = "https://github.com/ultralytics/assets/releases/download/v0.0.0/"
|
85
|
+
urls = [
|
86
|
+
f"{url}VOCtrainval_06-Nov-2007.zip", # 446MB, 5012 images
|
87
|
+
f"{url}VOCtest_06-Nov-2007.zip", # 438MB, 4953 images
|
88
|
+
f"{url}VOCtrainval_11-May-2012.zip", # 1.95GB, 17126 images
|
89
|
+
]
|
90
|
+
download(urls, dir=dir / "images", curl=True, threads=3, exist_ok=True) # download and unzip over existing (required)
|
85
91
|
|
86
92
|
# Convert
|
87
|
-
path = dir /
|
88
|
-
for year, image_set in (
|
89
|
-
imgs_path = dir /
|
90
|
-
lbs_path = dir /
|
93
|
+
path = dir / "images/VOCdevkit"
|
94
|
+
for year, image_set in ("2012", "train"), ("2012", "val"), ("2007", "train"), ("2007", "val"), ("2007", "test"):
|
95
|
+
imgs_path = dir / "images" / f"{image_set}{year}"
|
96
|
+
lbs_path = dir / "labels" / f"{image_set}{year}"
|
91
97
|
imgs_path.mkdir(exist_ok=True, parents=True)
|
92
98
|
lbs_path.mkdir(exist_ok=True, parents=True)
|
93
99
|
|
94
|
-
with open(path / f
|
100
|
+
with open(path / f"VOC{year}/ImageSets/Main/{image_set}.txt") as f:
|
95
101
|
image_ids = f.read().strip().split()
|
96
|
-
for id in tqdm(image_ids, desc=f
|
97
|
-
f = path / f
|
98
|
-
lb_path = (lbs_path / f.name).with_suffix(
|
102
|
+
for id in tqdm(image_ids, desc=f"{image_set}{year}"):
|
103
|
+
f = path / f"VOC{year}/JPEGImages/{id}.jpg" # old img path
|
104
|
+
lb_path = (lbs_path / f.name).with_suffix(".txt") # new label path
|
99
105
|
f.rename(imgs_path / f.name) # move image
|
100
106
|
convert_label(path, lb_path, year, id) # convert labels to YOLO format
|
@@ -34,40 +34,44 @@ download: |
|
|
34
34
|
|
35
35
|
from ultralytics.utils.downloads import download
|
36
36
|
|
37
|
+
|
37
38
|
def visdrone2yolo(dir):
|
39
|
+
"""Convert VisDrone annotations to YOLO format, creating label files with normalized bounding box coordinates."""
|
38
40
|
from PIL import Image
|
39
41
|
from tqdm import tqdm
|
40
42
|
|
41
43
|
def convert_box(size, box):
|
42
44
|
# Convert VisDrone box to YOLO xywh box
|
43
|
-
dw = 1. / size[0]
|
44
|
-
dh = 1. / size[1]
|
45
|
+
dw = 1.0 / size[0]
|
46
|
+
dh = 1.0 / size[1]
|
45
47
|
return (box[0] + box[2] / 2) * dw, (box[1] + box[3] / 2) * dh, box[2] * dw, box[3] * dh
|
46
48
|
|
47
|
-
(dir /
|
48
|
-
pbar = tqdm((dir /
|
49
|
+
(dir / "labels").mkdir(parents=True, exist_ok=True) # make labels directory
|
50
|
+
pbar = tqdm((dir / "annotations").glob("*.txt"), desc=f"Converting {dir}")
|
49
51
|
for f in pbar:
|
50
|
-
img_size = Image.open((dir /
|
52
|
+
img_size = Image.open((dir / "images" / f.name).with_suffix(".jpg")).size
|
51
53
|
lines = []
|
52
|
-
with open(f,
|
53
|
-
for row in [x.split(
|
54
|
-
if row[4] ==
|
54
|
+
with open(f, encoding="utf-8") as file: # read annotation.txt
|
55
|
+
for row in [x.split(",") for x in file.read().strip().splitlines()]:
|
56
|
+
if row[4] == "0": # VisDrone 'ignored regions' class 0
|
55
57
|
continue
|
56
58
|
cls = int(row[5]) - 1
|
57
59
|
box = convert_box(img_size, tuple(map(int, row[:4])))
|
58
60
|
lines.append(f"{cls} {' '.join(f'{x:.6f}' for x in box)}\n")
|
59
|
-
with open(str(f).replace(f
|
61
|
+
with open(str(f).replace(f"{os.sep}annotations{os.sep}", f"{os.sep}labels{os.sep}"), "w", encoding="utf-8") as fl:
|
60
62
|
fl.writelines(lines) # write label.txt
|
61
63
|
|
62
64
|
|
63
65
|
# Download
|
64
|
-
dir = Path(yaml[
|
65
|
-
urls = [
|
66
|
-
|
67
|
-
|
68
|
-
|
66
|
+
dir = Path(yaml["path"]) # dataset root dir
|
67
|
+
urls = [
|
68
|
+
"https://github.com/ultralytics/assets/releases/download/v0.0.0/VisDrone2019-DET-train.zip",
|
69
|
+
"https://github.com/ultralytics/assets/releases/download/v0.0.0/VisDrone2019-DET-val.zip",
|
70
|
+
"https://github.com/ultralytics/assets/releases/download/v0.0.0/VisDrone2019-DET-test-dev.zip",
|
71
|
+
"https://github.com/ultralytics/assets/releases/download/v0.0.0/VisDrone2019-DET-test-challenge.zip",
|
72
|
+
]
|
69
73
|
download(urls, dir=dir, curl=True, threads=4)
|
70
74
|
|
71
75
|
# Convert
|
72
|
-
for d in
|
76
|
+
for d in "VisDrone2019-DET-train", "VisDrone2019-DET-val", "VisDrone2019-DET-test-dev":
|
73
77
|
visdrone2yolo(dir / d) # convert VisDrone annotations to YOLO labels
|
@@ -24,16 +24,19 @@ names:
|
|
24
24
|
|
25
25
|
# Download script/URL (optional)
|
26
26
|
download: |
|
27
|
-
from ultralytics.utils.downloads import download
|
28
27
|
from pathlib import Path
|
29
28
|
|
29
|
+
from ultralytics.utils.downloads import download
|
30
|
+
|
30
31
|
# Download labels
|
31
|
-
dir = Path(yaml[
|
32
|
-
url =
|
33
|
-
urls = [url
|
32
|
+
dir = Path(yaml["path"]) # dataset root dir
|
33
|
+
url = "https://github.com/ultralytics/assets/releases/download/v0.0.0/"
|
34
|
+
urls = [f"{url}coco2017labels-pose.zip"]
|
34
35
|
download(urls, dir=dir.parent)
|
35
36
|
# Download data
|
36
|
-
urls = [
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
urls = [
|
38
|
+
"http://images.cocodataset.org/zips/train2017.zip", # 19G, 118k images
|
39
|
+
"http://images.cocodataset.org/zips/val2017.zip", # 1G, 5k images
|
40
|
+
"http://images.cocodataset.org/zips/test2017.zip", # 7G, 41k images (optional)
|
41
|
+
]
|
42
|
+
download(urls, dir=dir / "images", threads=3)
|
@@ -99,17 +99,20 @@ names:
|
|
99
99
|
|
100
100
|
# Download script/URL (optional)
|
101
101
|
download: |
|
102
|
-
from ultralytics.utils.downloads import download
|
103
102
|
from pathlib import Path
|
104
103
|
|
104
|
+
from ultralytics.utils.downloads import download
|
105
|
+
|
105
106
|
# Download labels
|
106
107
|
segments = True # segment or box labels
|
107
|
-
dir = Path(yaml[
|
108
|
-
url =
|
109
|
-
urls = [url + (
|
108
|
+
dir = Path(yaml["path"]) # dataset root dir
|
109
|
+
url = "https://github.com/ultralytics/assets/releases/download/v0.0.0/"
|
110
|
+
urls = [url + ("coco2017labels-segments.zip" if segments else "coco2017labels.zip")] # labels
|
110
111
|
download(urls, dir=dir.parent)
|
111
112
|
# Download data
|
112
|
-
urls = [
|
113
|
-
|
114
|
-
|
115
|
-
|
113
|
+
urls = [
|
114
|
+
"http://images.cocodataset.org/zips/train2017.zip", # 19G, 118k images
|
115
|
+
"http://images.cocodataset.org/zips/val2017.zip", # 1G, 5k images
|
116
|
+
"http://images.cocodataset.org/zips/test2017.zip", # 7G, 41k images (optional)
|
117
|
+
]
|
118
|
+
download(urls, dir=dir / "images", threads=3)
|
@@ -1221,16 +1221,20 @@ names:
|
|
1221
1221
|
|
1222
1222
|
# Download script/URL (optional)
|
1223
1223
|
download: |
|
1224
|
-
from ultralytics.utils.downloads import download
|
1225
1224
|
from pathlib import Path
|
1226
1225
|
|
1226
|
+
from ultralytics.utils.downloads import download
|
1227
|
+
|
1227
1228
|
# Download labels
|
1228
|
-
dir = Path(yaml[
|
1229
|
-
url =
|
1230
|
-
urls = [url
|
1229
|
+
dir = Path(yaml["path"]) # dataset root dir
|
1230
|
+
url = "https://github.com/ultralytics/assets/releases/download/v0.0.0/"
|
1231
|
+
urls = [f"{url}lvis-labels-segments.zip"]
|
1231
1232
|
download(urls, dir=dir.parent)
|
1233
|
+
|
1232
1234
|
# Download data
|
1233
|
-
urls = [
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1235
|
+
urls = [
|
1236
|
+
"http://images.cocodataset.org/zips/train2017.zip", # 19G, 118k images
|
1237
|
+
"http://images.cocodataset.org/zips/val2017.zip", # 1G, 5k images
|
1238
|
+
"http://images.cocodataset.org/zips/test2017.zip", # 7G, 41k images (optional)
|
1239
|
+
]
|
1240
|
+
download(urls, dir=dir / "images", threads=3)
|