bplusplus 1.1.0__py3-none-any.whl → 1.2.1__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.
Potentially problematic release.
This version of bplusplus might be problematic. Click here for more details.
- bplusplus/__init__.py +4 -2
- bplusplus/collect.py +72 -3
- bplusplus/hierarchical/test.py +670 -0
- bplusplus/hierarchical/train.py +676 -0
- bplusplus/prepare.py +236 -71
- bplusplus/resnet/test.py +473 -0
- bplusplus/resnet/train.py +329 -0
- bplusplus-1.2.1.dist-info/METADATA +252 -0
- bplusplus-1.2.1.dist-info/RECORD +12 -0
- bplusplus/yolov5detect/__init__.py +0 -1
- bplusplus/yolov5detect/detect.py +0 -444
- bplusplus/yolov5detect/export.py +0 -1530
- bplusplus/yolov5detect/insect.yaml +0 -8
- bplusplus/yolov5detect/models/__init__.py +0 -0
- bplusplus/yolov5detect/models/common.py +0 -1109
- bplusplus/yolov5detect/models/experimental.py +0 -130
- bplusplus/yolov5detect/models/hub/anchors.yaml +0 -56
- bplusplus/yolov5detect/models/hub/yolov3-spp.yaml +0 -52
- bplusplus/yolov5detect/models/hub/yolov3-tiny.yaml +0 -42
- bplusplus/yolov5detect/models/hub/yolov3.yaml +0 -52
- bplusplus/yolov5detect/models/hub/yolov5-bifpn.yaml +0 -49
- bplusplus/yolov5detect/models/hub/yolov5-fpn.yaml +0 -43
- bplusplus/yolov5detect/models/hub/yolov5-p2.yaml +0 -55
- bplusplus/yolov5detect/models/hub/yolov5-p34.yaml +0 -42
- bplusplus/yolov5detect/models/hub/yolov5-p6.yaml +0 -57
- bplusplus/yolov5detect/models/hub/yolov5-p7.yaml +0 -68
- bplusplus/yolov5detect/models/hub/yolov5-panet.yaml +0 -49
- bplusplus/yolov5detect/models/hub/yolov5l6.yaml +0 -61
- bplusplus/yolov5detect/models/hub/yolov5m6.yaml +0 -61
- bplusplus/yolov5detect/models/hub/yolov5n6.yaml +0 -61
- bplusplus/yolov5detect/models/hub/yolov5s-LeakyReLU.yaml +0 -50
- bplusplus/yolov5detect/models/hub/yolov5s-ghost.yaml +0 -49
- bplusplus/yolov5detect/models/hub/yolov5s-transformer.yaml +0 -49
- bplusplus/yolov5detect/models/hub/yolov5s6.yaml +0 -61
- bplusplus/yolov5detect/models/hub/yolov5x6.yaml +0 -61
- bplusplus/yolov5detect/models/segment/yolov5l-seg.yaml +0 -49
- bplusplus/yolov5detect/models/segment/yolov5m-seg.yaml +0 -49
- bplusplus/yolov5detect/models/segment/yolov5n-seg.yaml +0 -49
- bplusplus/yolov5detect/models/segment/yolov5s-seg.yaml +0 -49
- bplusplus/yolov5detect/models/segment/yolov5x-seg.yaml +0 -49
- bplusplus/yolov5detect/models/tf.py +0 -797
- bplusplus/yolov5detect/models/yolo.py +0 -495
- bplusplus/yolov5detect/models/yolov5l.yaml +0 -49
- bplusplus/yolov5detect/models/yolov5m.yaml +0 -49
- bplusplus/yolov5detect/models/yolov5n.yaml +0 -49
- bplusplus/yolov5detect/models/yolov5s.yaml +0 -49
- bplusplus/yolov5detect/models/yolov5x.yaml +0 -49
- bplusplus/yolov5detect/utils/__init__.py +0 -97
- bplusplus/yolov5detect/utils/activations.py +0 -134
- bplusplus/yolov5detect/utils/augmentations.py +0 -448
- bplusplus/yolov5detect/utils/autoanchor.py +0 -175
- bplusplus/yolov5detect/utils/autobatch.py +0 -70
- bplusplus/yolov5detect/utils/aws/__init__.py +0 -0
- bplusplus/yolov5detect/utils/aws/mime.sh +0 -26
- bplusplus/yolov5detect/utils/aws/resume.py +0 -41
- bplusplus/yolov5detect/utils/aws/userdata.sh +0 -27
- bplusplus/yolov5detect/utils/callbacks.py +0 -72
- bplusplus/yolov5detect/utils/dataloaders.py +0 -1385
- bplusplus/yolov5detect/utils/docker/Dockerfile +0 -73
- bplusplus/yolov5detect/utils/docker/Dockerfile-arm64 +0 -40
- bplusplus/yolov5detect/utils/docker/Dockerfile-cpu +0 -42
- bplusplus/yolov5detect/utils/downloads.py +0 -136
- bplusplus/yolov5detect/utils/flask_rest_api/README.md +0 -70
- bplusplus/yolov5detect/utils/flask_rest_api/example_request.py +0 -17
- bplusplus/yolov5detect/utils/flask_rest_api/restapi.py +0 -49
- bplusplus/yolov5detect/utils/general.py +0 -1294
- bplusplus/yolov5detect/utils/google_app_engine/Dockerfile +0 -25
- bplusplus/yolov5detect/utils/google_app_engine/additional_requirements.txt +0 -6
- bplusplus/yolov5detect/utils/google_app_engine/app.yaml +0 -16
- bplusplus/yolov5detect/utils/loggers/__init__.py +0 -476
- bplusplus/yolov5detect/utils/loggers/clearml/README.md +0 -222
- bplusplus/yolov5detect/utils/loggers/clearml/__init__.py +0 -0
- bplusplus/yolov5detect/utils/loggers/clearml/clearml_utils.py +0 -230
- bplusplus/yolov5detect/utils/loggers/clearml/hpo.py +0 -90
- bplusplus/yolov5detect/utils/loggers/comet/README.md +0 -250
- bplusplus/yolov5detect/utils/loggers/comet/__init__.py +0 -551
- bplusplus/yolov5detect/utils/loggers/comet/comet_utils.py +0 -151
- bplusplus/yolov5detect/utils/loggers/comet/hpo.py +0 -126
- bplusplus/yolov5detect/utils/loggers/comet/optimizer_config.json +0 -135
- bplusplus/yolov5detect/utils/loggers/wandb/__init__.py +0 -0
- bplusplus/yolov5detect/utils/loggers/wandb/wandb_utils.py +0 -210
- bplusplus/yolov5detect/utils/loss.py +0 -259
- bplusplus/yolov5detect/utils/metrics.py +0 -381
- bplusplus/yolov5detect/utils/plots.py +0 -517
- bplusplus/yolov5detect/utils/segment/__init__.py +0 -0
- bplusplus/yolov5detect/utils/segment/augmentations.py +0 -100
- bplusplus/yolov5detect/utils/segment/dataloaders.py +0 -366
- bplusplus/yolov5detect/utils/segment/general.py +0 -160
- bplusplus/yolov5detect/utils/segment/loss.py +0 -198
- bplusplus/yolov5detect/utils/segment/metrics.py +0 -225
- bplusplus/yolov5detect/utils/segment/plots.py +0 -152
- bplusplus/yolov5detect/utils/torch_utils.py +0 -482
- bplusplus/yolov5detect/utils/triton.py +0 -90
- bplusplus-1.1.0.dist-info/METADATA +0 -179
- bplusplus-1.1.0.dist-info/RECORD +0 -92
- {bplusplus-1.1.0.dist-info → bplusplus-1.2.1.dist-info}/LICENSE +0 -0
- {bplusplus-1.1.0.dist-info → bplusplus-1.2.1.dist-info}/WHEEL +0 -0
bplusplus/prepare.py
CHANGED
|
@@ -1,21 +1,40 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import random
|
|
3
|
-
from typing import Any, Optional
|
|
4
|
-
import requests
|
|
5
|
-
import tempfile
|
|
6
|
-
from .collect import Group, collect
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
from .yolov5detect.detect import run
|
|
9
3
|
import shutil
|
|
10
|
-
|
|
4
|
+
import tempfile
|
|
11
5
|
from collections import defaultdict
|
|
12
|
-
from
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Optional
|
|
8
|
+
|
|
13
9
|
import matplotlib.pyplot as plt
|
|
10
|
+
import numpy as np
|
|
14
11
|
import requests
|
|
15
|
-
|
|
12
|
+
import torch
|
|
16
13
|
import yaml
|
|
14
|
+
from PIL import Image, ImageDraw, ImageFont
|
|
15
|
+
from prettytable import PrettyTable
|
|
16
|
+
from torch import serialization
|
|
17
|
+
from torch.nn import Module, ModuleDict, ModuleList
|
|
18
|
+
from torch.nn.modules.activation import LeakyReLU, ReLU, SiLU
|
|
19
|
+
# Add more modules to prevent further errors
|
|
20
|
+
from torch.nn.modules.batchnorm import BatchNorm2d
|
|
21
|
+
from torch.nn.modules.container import Sequential
|
|
22
|
+
from torch.nn.modules.conv import Conv2d
|
|
23
|
+
from torch.nn.modules.dropout import Dropout
|
|
24
|
+
from torch.nn.modules.linear import Linear
|
|
25
|
+
from torch.nn.modules.pooling import MaxPool2d
|
|
26
|
+
from torch.nn.modules.upsampling import Upsample
|
|
27
|
+
from tqdm import tqdm
|
|
28
|
+
from ultralytics import YOLO
|
|
29
|
+
from ultralytics.nn.modules import SPPF, Bottleneck, C2f, Concat, Detect
|
|
30
|
+
from ultralytics.nn.modules.block import DFL
|
|
31
|
+
from ultralytics.nn.modules.conv import Conv
|
|
32
|
+
from ultralytics.nn.tasks import DetectionModel
|
|
17
33
|
|
|
18
|
-
|
|
34
|
+
from .collect import Group, collect
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def prepare(input_directory: str, output_directory: str, one_stage: bool = False, with_background: bool = False, size_filter: bool = False, sizes: list = None):
|
|
19
38
|
|
|
20
39
|
"""
|
|
21
40
|
Prepares the dataset for training by performing the following steps:
|
|
@@ -43,12 +62,8 @@ def prepare(input_directory: str, output_directory: str, with_background: bool =
|
|
|
43
62
|
|
|
44
63
|
temp_dir_path = Path(temp_dir)
|
|
45
64
|
images_path = temp_dir_path / "images"
|
|
46
|
-
inference_path = temp_dir_path / "inference"
|
|
47
|
-
labels_path = temp_dir_path / "labels"
|
|
48
65
|
|
|
49
66
|
images_path.mkdir(parents=True, exist_ok=True)
|
|
50
|
-
inference_path.mkdir(parents=True, exist_ok=True)
|
|
51
|
-
labels_path.mkdir(parents=True, exist_ok=True)
|
|
52
67
|
|
|
53
68
|
for folder_directory in input_directory.iterdir():
|
|
54
69
|
images_names = []
|
|
@@ -66,49 +81,222 @@ def prepare(input_directory: str, output_directory: str, with_background: bool =
|
|
|
66
81
|
__delete_corrupted_images(images_path)
|
|
67
82
|
|
|
68
83
|
current_dir = Path(__file__).resolve().parent
|
|
69
|
-
yaml_path = current_dir / 'yolov5detect' / 'insect.yaml'
|
|
70
|
-
weights_path = current_dir / 'yolov5detect' / 'acc94.pt'
|
|
71
84
|
|
|
72
|
-
|
|
85
|
+
weights_path = current_dir / 'small-generic.pt'
|
|
86
|
+
|
|
87
|
+
github_release_url = 'https://github.com/orlandocloss/TwoStageInsectDetection/releases/download/models/small-generic.pt'
|
|
73
88
|
|
|
74
89
|
if not weights_path.exists():
|
|
75
90
|
__download_file_from_github_release(github_release_url, weights_path)
|
|
76
91
|
|
|
77
|
-
|
|
92
|
+
# Add all required classes to safe globals
|
|
93
|
+
serialization.add_safe_globals([
|
|
94
|
+
DetectionModel, Sequential, Conv, Conv2d, BatchNorm2d,
|
|
95
|
+
SiLU, ReLU, LeakyReLU, MaxPool2d, Linear, Dropout, Upsample,
|
|
96
|
+
Module, ModuleList, ModuleDict,
|
|
97
|
+
Bottleneck, C2f, SPPF, Detect, Concat, DFL
|
|
98
|
+
])
|
|
99
|
+
|
|
100
|
+
model = YOLO(weights_path)
|
|
101
|
+
model.predict(images_path, conf=0.25, save=True, save_txt=True, project=temp_dir_path)
|
|
102
|
+
labels_path = temp_dir_path / "predict" / "labels"
|
|
103
|
+
|
|
104
|
+
if size_filter and len(sizes) <= 2:
|
|
105
|
+
filtered=filter_by_size(images_path, labels_path, sizes)
|
|
106
|
+
print(f"\nFiltered {len(list(images_path.glob('*.jpg')))} images by size out of {original_image_count} input images.\n NOTE: Some images may be filtered due to corruption or inaccurate labels.")
|
|
107
|
+
|
|
108
|
+
if one_stage:
|
|
109
|
+
|
|
110
|
+
__delete_orphaned_images_and_inferences(images_path, labels_path)
|
|
111
|
+
__delete_invalid_txt_files(images_path, labels_path)
|
|
112
|
+
class_idxs = update_labels(class_mapping, labels_path)
|
|
113
|
+
__split_data(class_mapping, temp_dir_path, output_directory)
|
|
78
114
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
115
|
+
# __save_class_idx_to_file(class_idxs, output_directory)
|
|
116
|
+
final_image_count = count_images_across_splits(output_directory)
|
|
117
|
+
print(f"\nOut of {original_image_count} input images, {final_image_count} are eligible for detection. \nThese are saved across train, test and valid split in {output_directory}.")
|
|
118
|
+
__generate_sample_images_with_detections(output_directory, class_idxs)
|
|
83
119
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
print(f"\nOut of {original_image_count} input images, {final_image_count} are eligible for detection. \nThese are saved across train, test and valid split in {output_directory}.")
|
|
87
|
-
__generate_sample_images_with_detections(output_directory, class_idxs)
|
|
120
|
+
if with_background:
|
|
121
|
+
print("\nCollecting and splitting background images.")
|
|
88
122
|
|
|
89
|
-
|
|
90
|
-
print("\nCollecting and splitting background images.")
|
|
123
|
+
bg_images=int(final_image_count*0.06)
|
|
91
124
|
|
|
92
|
-
|
|
125
|
+
search: dict[str, Any] = {
|
|
126
|
+
"scientificName": ["Plantae"]
|
|
127
|
+
}
|
|
93
128
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
129
|
+
collect(
|
|
130
|
+
group_by_key=Group.scientificName,
|
|
131
|
+
search_parameters=search,
|
|
132
|
+
images_per_group=bg_images,
|
|
133
|
+
output_directory=temp_dir_path,
|
|
134
|
+
num_threads=3
|
|
135
|
+
)
|
|
97
136
|
|
|
98
|
-
|
|
99
|
-
group_by_key=Group.scientificName,
|
|
100
|
-
search_parameters=search,
|
|
101
|
-
images_per_group=bg_images,
|
|
102
|
-
output_directory=temp_dir_path
|
|
103
|
-
)
|
|
137
|
+
__delete_corrupted_images(temp_dir_path / "Plantae")
|
|
104
138
|
|
|
105
|
-
|
|
139
|
+
__split_background_images(temp_dir_path / "Plantae", output_directory)
|
|
106
140
|
|
|
107
|
-
|
|
141
|
+
__count_classes_and_output_table(output_directory, class_idxs)
|
|
108
142
|
|
|
109
|
-
|
|
143
|
+
__make_yaml_file(output_directory, class_idxs)
|
|
144
|
+
else:
|
|
145
|
+
try:
|
|
146
|
+
sized_dir = temp_dir_path / "sized"
|
|
147
|
+
sized_dir.mkdir(parents=True, exist_ok=True)
|
|
148
|
+
__two_stage_update(class_mapping, filtered, sized_dir, images_path)
|
|
149
|
+
__classification_split(sized_dir, output_directory)
|
|
150
|
+
__count_classification_split(output_directory, class_mapping)
|
|
151
|
+
except:
|
|
152
|
+
__classification_split(images_path, output_directory)
|
|
153
|
+
__count_classification_split(output_directory, class_mapping)
|
|
154
|
+
|
|
155
|
+
def __count_classification_split(output_directory: str, class_mapping: dict):
|
|
156
|
+
"""
|
|
157
|
+
Counts the number of images in the train and valid splits for each class.
|
|
110
158
|
|
|
111
|
-
|
|
159
|
+
Args:
|
|
160
|
+
output_directory (str): Path to the output directory containing train and valid splits.
|
|
161
|
+
class_mapping (dict): Dictionary mapping class names to image file names.
|
|
162
|
+
"""
|
|
163
|
+
class_counts = {}
|
|
164
|
+
train_counts = {}
|
|
165
|
+
valid_counts = {}
|
|
166
|
+
|
|
167
|
+
for class_name in class_mapping.keys():
|
|
168
|
+
train_dir = output_directory / 'train' / class_name
|
|
169
|
+
valid_dir = output_directory / 'valid' / class_name
|
|
170
|
+
|
|
171
|
+
train_count = len(list(train_dir.glob("*.jpg"))) if train_dir.exists() else 0
|
|
172
|
+
valid_count = len(list(valid_dir.glob("*.jpg"))) if valid_dir.exists() else 0
|
|
173
|
+
total_count = train_count + valid_count
|
|
174
|
+
|
|
175
|
+
class_counts[class_name] = total_count
|
|
176
|
+
train_counts[class_name] = train_count
|
|
177
|
+
valid_counts[class_name] = valid_count
|
|
178
|
+
|
|
179
|
+
table = PrettyTable()
|
|
180
|
+
table.field_names = ["Class", "Train", "Valid", "Total"]
|
|
181
|
+
for class_name in class_mapping.keys():
|
|
182
|
+
table.add_row([
|
|
183
|
+
class_name,
|
|
184
|
+
train_counts[class_name],
|
|
185
|
+
valid_counts[class_name],
|
|
186
|
+
class_counts[class_name]
|
|
187
|
+
])
|
|
188
|
+
print(table)
|
|
189
|
+
print(f"Saved in {output_directory}")
|
|
190
|
+
|
|
191
|
+
def __classification_split(input_directory: str, output_directory: str):
|
|
192
|
+
"""
|
|
193
|
+
Splits the data into train and validation sets for classification tasks.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
input_directory (str): Path to the input directory containing subdirectories of class names.
|
|
197
|
+
output_directory (str): Path to the output directory where train and valid splits will be created.
|
|
198
|
+
"""
|
|
199
|
+
input_directory = Path(input_directory)
|
|
200
|
+
output_directory = Path(output_directory)
|
|
201
|
+
|
|
202
|
+
# Create train and valid directories
|
|
203
|
+
train_dir = output_directory / 'train'
|
|
204
|
+
valid_dir = output_directory / 'valid'
|
|
205
|
+
|
|
206
|
+
train_dir.mkdir(parents=True, exist_ok=True)
|
|
207
|
+
valid_dir.mkdir(parents=True, exist_ok=True)
|
|
208
|
+
|
|
209
|
+
# Process each class directory
|
|
210
|
+
for class_dir in input_directory.iterdir():
|
|
211
|
+
if not class_dir.is_dir():
|
|
212
|
+
continue
|
|
213
|
+
|
|
214
|
+
class_name = class_dir.name
|
|
215
|
+
print(f"Processing class: {class_name}")
|
|
216
|
+
|
|
217
|
+
# Create corresponding class directories in train and valid
|
|
218
|
+
(train_dir / class_name).mkdir(exist_ok=True)
|
|
219
|
+
(valid_dir / class_name).mkdir(exist_ok=True)
|
|
220
|
+
|
|
221
|
+
# Get all image files
|
|
222
|
+
image_files = list(class_dir.glob('*.jpg')) + list(class_dir.glob('*.jpeg')) + list(class_dir.glob('*.png'))
|
|
223
|
+
|
|
224
|
+
if not image_files:
|
|
225
|
+
print(f"Warning: No images found in {class_dir}")
|
|
226
|
+
continue
|
|
227
|
+
|
|
228
|
+
# Shuffle the files to ensure random distribution
|
|
229
|
+
np.random.shuffle(image_files)
|
|
230
|
+
|
|
231
|
+
# Split into train (90%) and valid (10%)
|
|
232
|
+
split_idx = int(len(image_files) * 0.9)
|
|
233
|
+
train_files = image_files[:split_idx]
|
|
234
|
+
valid_files = image_files[split_idx:]
|
|
235
|
+
|
|
236
|
+
# Copy files to respective directories
|
|
237
|
+
for img_file in train_files:
|
|
238
|
+
shutil.copy(img_file, train_dir / class_name / img_file.name)
|
|
239
|
+
|
|
240
|
+
for img_file in valid_files:
|
|
241
|
+
shutil.copy(img_file, valid_dir / class_name / img_file.name)
|
|
242
|
+
|
|
243
|
+
print(f" - {len(train_files)} images in train, {len(valid_files)} images in valid")
|
|
244
|
+
|
|
245
|
+
print(f"\nData split complete. Train and validation sets created in {output_directory}")
|
|
246
|
+
|
|
247
|
+
def filter_by_size(images_path: Path, labels_path: Path, sizes: list):
|
|
248
|
+
"""
|
|
249
|
+
Filters images by size and updates labels accordingly.
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
images_path (Path): The path to the directory containing images.
|
|
253
|
+
labels_path (Path): The path to the directory containing labels.
|
|
254
|
+
sizes (list): A list of sizes to filter by.
|
|
255
|
+
"""
|
|
256
|
+
size_map={
|
|
257
|
+
"small": [0, 0.15],
|
|
258
|
+
"medium": [0.15, 0.3],
|
|
259
|
+
"large": [0.3, 1],
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
filtered_images = []
|
|
263
|
+
for image_file in images_path.glob("*.jpg"):
|
|
264
|
+
label_file = labels_path / (image_file.stem + ".txt")
|
|
265
|
+
image_name = image_file.name
|
|
266
|
+
|
|
267
|
+
if label_file.exists():
|
|
268
|
+
with open(label_file, 'r') as file:
|
|
269
|
+
lines = file.readlines()
|
|
270
|
+
if len(lines) != 1:
|
|
271
|
+
continue
|
|
272
|
+
else:
|
|
273
|
+
parts = lines[0].split()
|
|
274
|
+
_, _, width, height = map(float, parts[1:])
|
|
275
|
+
for size in sizes:
|
|
276
|
+
if width < size_map[size][1] and width >= size_map[size][0] and height < size_map[size][1] and height >= size_map[size][0]:
|
|
277
|
+
filtered_images.append(image_name)
|
|
278
|
+
|
|
279
|
+
for image_file in images_path.glob("*.jpg"):
|
|
280
|
+
label_file = labels_path / (image_file.stem + ".txt")
|
|
281
|
+
image_name = image_file.name
|
|
282
|
+
if image_name not in filtered_images:
|
|
283
|
+
image_file.unlink()
|
|
284
|
+
try:
|
|
285
|
+
label_file.unlink()
|
|
286
|
+
except FileNotFoundError:
|
|
287
|
+
pass
|
|
288
|
+
return filtered_images
|
|
289
|
+
|
|
290
|
+
def __two_stage_update(class_mapping: dict, filtered_images: Path, output_directory: Path, images_path: Path):
|
|
291
|
+
"""
|
|
292
|
+
Prepares folders with class name containing filtered images.
|
|
293
|
+
"""
|
|
294
|
+
|
|
295
|
+
for class_name, images in class_mapping.items():
|
|
296
|
+
for image_name in images:
|
|
297
|
+
if image_name in filtered_images:
|
|
298
|
+
(output_directory / class_name).mkdir(parents=True, exist_ok=True)
|
|
299
|
+
shutil.copy(images_path / image_name, output_directory / class_name / image_name)
|
|
112
300
|
|
|
113
301
|
def __delete_corrupted_images(images_path: Path):
|
|
114
302
|
|
|
@@ -158,7 +346,7 @@ def __download_file_from_github_release(url, dest_path):
|
|
|
158
346
|
progress_bar.close()
|
|
159
347
|
raise Exception(f"Failed to download file from {url}")
|
|
160
348
|
|
|
161
|
-
def __delete_orphaned_images_and_inferences(images_path: Path,
|
|
349
|
+
def __delete_orphaned_images_and_inferences(images_path: Path, labels_path: Path):
|
|
162
350
|
|
|
163
351
|
"""
|
|
164
352
|
Deletes orphaned images and their corresponding inference files if they do not have a label file.
|
|
@@ -177,15 +365,10 @@ def __delete_orphaned_images_and_inferences(images_path: Path, inference_path: P
|
|
|
177
365
|
for txt_file in labels_path.glob("*.txt"):
|
|
178
366
|
image_file_jpg = images_path / (txt_file.stem + ".jpg")
|
|
179
367
|
image_file_jpeg = images_path / (txt_file.stem + ".jpeg")
|
|
180
|
-
inference_file_jpg = inference_path / (txt_file.stem + ".jpg")
|
|
181
|
-
inference_file_jpeg = inference_path / (txt_file.stem + ".jpeg")
|
|
182
368
|
|
|
183
369
|
if not (image_file_jpg.exists() or image_file_jpeg.exists()):
|
|
184
370
|
print(f"Deleting {txt_file.name} - No corresponding image file")
|
|
185
371
|
txt_file.unlink()
|
|
186
|
-
elif not (inference_file_jpg.exists() or inference_file_jpeg.exists()):
|
|
187
|
-
print(f"Deleting {txt_file.name} - No corresponding inference file")
|
|
188
|
-
txt_file.unlink()
|
|
189
372
|
|
|
190
373
|
label_stems = {txt_file.stem for txt_file in labels_path.glob("*.txt")}
|
|
191
374
|
image_files = list(images_path.glob("*.jpg")) + list(images_path.glob("*.jpeg"))
|
|
@@ -195,19 +378,9 @@ def __delete_orphaned_images_and_inferences(images_path: Path, inference_path: P
|
|
|
195
378
|
print(f"Deleting orphaned image: {image_file.name}")
|
|
196
379
|
image_file.unlink()
|
|
197
380
|
|
|
198
|
-
|
|
199
|
-
inference_file_jpeg = inference_path / (image_file.stem + ".jpeg")
|
|
381
|
+
print("Orphaned images files without corresponding labels have been deleted.")
|
|
200
382
|
|
|
201
|
-
|
|
202
|
-
inference_file_jpg.unlink()
|
|
203
|
-
print(f"Deleted corresponding inference file: {inference_file_jpg.name}")
|
|
204
|
-
elif inference_file_jpeg.exists():
|
|
205
|
-
inference_file_jpeg.unlink()
|
|
206
|
-
print(f"Deleted corresponding inference file: {inference_file_jpeg.name}")
|
|
207
|
-
|
|
208
|
-
print("Orphaned images and inference files without corresponding labels have been deleted.")
|
|
209
|
-
|
|
210
|
-
def __delete_invalid_txt_files(images_path: Path, inference_path: Path, labels_path: Path):
|
|
383
|
+
def __delete_invalid_txt_files(images_path: Path, labels_path: Path):
|
|
211
384
|
|
|
212
385
|
"""
|
|
213
386
|
Deletes invalid text files and their corresponding image and inference files.
|
|
@@ -232,8 +405,6 @@ def __delete_invalid_txt_files(images_path: Path, inference_path: Path, labels_p
|
|
|
232
405
|
|
|
233
406
|
image_file_jpg = images_path / (txt_file.stem + ".jpg")
|
|
234
407
|
image_file_jpeg = images_path / (txt_file.stem + ".jpeg")
|
|
235
|
-
inference_file_jpg = inference_path / (txt_file.stem + ".jpg")
|
|
236
|
-
inference_file_jpeg = inference_path / (txt_file.stem + ".jpeg")
|
|
237
408
|
|
|
238
409
|
if image_file_jpg.exists():
|
|
239
410
|
image_file_jpg.unlink()
|
|
@@ -242,14 +413,7 @@ def __delete_invalid_txt_files(images_path: Path, inference_path: Path, labels_p
|
|
|
242
413
|
image_file_jpeg.unlink()
|
|
243
414
|
print(f"Deleted corresponding image file: {image_file_jpeg.name}")
|
|
244
415
|
|
|
245
|
-
|
|
246
|
-
inference_file_jpg.unlink()
|
|
247
|
-
print(f"Deleted corresponding inference file: {inference_file_jpg.name}")
|
|
248
|
-
elif inference_file_jpeg.exists():
|
|
249
|
-
inference_file_jpeg.unlink()
|
|
250
|
-
print(f"Deleted corresponding inference file: {inference_file_jpeg.name}")
|
|
251
|
-
|
|
252
|
-
print("Invalid text files and their corresponding images and inference files have been deleted.")
|
|
416
|
+
print("Invalid text files and their corresponding images files have been deleted.")
|
|
253
417
|
|
|
254
418
|
|
|
255
419
|
def __split_data(class_mapping: dict, temp_dir_path: Path, output_directory: Path):
|
|
@@ -262,7 +426,7 @@ def __split_data(class_mapping: dict, temp_dir_path: Path, output_directory: Pat
|
|
|
262
426
|
output_directory (Path): The path to the output directory where the split data will be saved.
|
|
263
427
|
"""
|
|
264
428
|
images_dir = temp_dir_path / "images"
|
|
265
|
-
labels_dir = temp_dir_path / "labels"
|
|
429
|
+
labels_dir = temp_dir_path / "predict" / "labels"
|
|
266
430
|
|
|
267
431
|
def create_dirs(split):
|
|
268
432
|
(output_directory / split).mkdir(parents=True, exist_ok=True)
|
|
@@ -483,6 +647,7 @@ def __count_classes_and_output_table(output_directory: Path, class_idxs: dict):
|
|
|
483
647
|
table.add_row([class_name, class_index, train_count, test_count, valid_count, total])
|
|
484
648
|
|
|
485
649
|
print(table)
|
|
650
|
+
|
|
486
651
|
def update_labels(class_mapping: dict, labels_path: Path) -> dict:
|
|
487
652
|
"""
|
|
488
653
|
Updates the labels based on the class mapping.
|