megadetector 5.0.5__py3-none-any.whl → 5.0.7__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 megadetector might be problematic. Click here for more details.

Files changed (132) hide show
  1. api/batch_processing/data_preparation/manage_local_batch.py +302 -263
  2. api/batch_processing/data_preparation/manage_video_batch.py +81 -2
  3. api/batch_processing/postprocessing/add_max_conf.py +1 -0
  4. api/batch_processing/postprocessing/categorize_detections_by_size.py +50 -19
  5. api/batch_processing/postprocessing/compare_batch_results.py +110 -60
  6. api/batch_processing/postprocessing/load_api_results.py +56 -70
  7. api/batch_processing/postprocessing/md_to_coco.py +1 -1
  8. api/batch_processing/postprocessing/md_to_labelme.py +2 -1
  9. api/batch_processing/postprocessing/postprocess_batch_results.py +240 -81
  10. api/batch_processing/postprocessing/render_detection_confusion_matrix.py +625 -0
  11. api/batch_processing/postprocessing/repeat_detection_elimination/find_repeat_detections.py +71 -23
  12. api/batch_processing/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +1 -1
  13. api/batch_processing/postprocessing/repeat_detection_elimination/repeat_detections_core.py +227 -75
  14. api/batch_processing/postprocessing/subset_json_detector_output.py +132 -5
  15. api/batch_processing/postprocessing/top_folders_to_bottom.py +1 -1
  16. api/synchronous/api_core/animal_detection_api/detection/run_detector_batch.py +2 -2
  17. classification/prepare_classification_script.py +191 -191
  18. data_management/coco_to_yolo.py +68 -45
  19. data_management/databases/integrity_check_json_db.py +7 -5
  20. data_management/generate_crops_from_cct.py +3 -3
  21. data_management/get_image_sizes.py +8 -6
  22. data_management/importers/add_timestamps_to_icct.py +79 -0
  23. data_management/importers/animl_results_to_md_results.py +160 -0
  24. data_management/importers/auckland_doc_test_to_json.py +4 -4
  25. data_management/importers/auckland_doc_to_json.py +1 -1
  26. data_management/importers/awc_to_json.py +5 -5
  27. data_management/importers/bellevue_to_json.py +5 -5
  28. data_management/importers/carrizo_shrubfree_2018.py +5 -5
  29. data_management/importers/carrizo_trail_cam_2017.py +5 -5
  30. data_management/importers/cct_field_adjustments.py +2 -3
  31. data_management/importers/channel_islands_to_cct.py +4 -4
  32. data_management/importers/ena24_to_json.py +5 -5
  33. data_management/importers/helena_to_cct.py +10 -10
  34. data_management/importers/idaho-camera-traps.py +12 -12
  35. data_management/importers/idfg_iwildcam_lila_prep.py +8 -8
  36. data_management/importers/jb_csv_to_json.py +4 -4
  37. data_management/importers/missouri_to_json.py +1 -1
  38. data_management/importers/noaa_seals_2019.py +1 -1
  39. data_management/importers/pc_to_json.py +5 -5
  40. data_management/importers/prepare-noaa-fish-data-for-lila.py +4 -4
  41. data_management/importers/prepare_zsl_imerit.py +5 -5
  42. data_management/importers/rspb_to_json.py +4 -4
  43. data_management/importers/save_the_elephants_survey_A.py +5 -5
  44. data_management/importers/save_the_elephants_survey_B.py +6 -6
  45. data_management/importers/snapshot_safari_importer.py +9 -9
  46. data_management/importers/snapshot_serengeti_lila.py +9 -9
  47. data_management/importers/timelapse_csv_set_to_json.py +5 -7
  48. data_management/importers/ubc_to_json.py +4 -4
  49. data_management/importers/umn_to_json.py +4 -4
  50. data_management/importers/wellington_to_json.py +1 -1
  51. data_management/importers/wi_to_json.py +2 -2
  52. data_management/importers/zamba_results_to_md_results.py +181 -0
  53. data_management/labelme_to_coco.py +35 -7
  54. data_management/labelme_to_yolo.py +229 -0
  55. data_management/lila/add_locations_to_island_camera_traps.py +1 -1
  56. data_management/lila/add_locations_to_nacti.py +147 -0
  57. data_management/lila/create_lila_blank_set.py +474 -0
  58. data_management/lila/create_lila_test_set.py +2 -1
  59. data_management/lila/create_links_to_md_results_files.py +106 -0
  60. data_management/lila/download_lila_subset.py +46 -21
  61. data_management/lila/generate_lila_per_image_labels.py +23 -14
  62. data_management/lila/get_lila_annotation_counts.py +17 -11
  63. data_management/lila/lila_common.py +14 -11
  64. data_management/lila/test_lila_metadata_urls.py +116 -0
  65. data_management/ocr_tools.py +829 -0
  66. data_management/resize_coco_dataset.py +13 -11
  67. data_management/yolo_output_to_md_output.py +84 -12
  68. data_management/yolo_to_coco.py +38 -20
  69. detection/process_video.py +36 -14
  70. detection/pytorch_detector.py +23 -8
  71. detection/run_detector.py +76 -19
  72. detection/run_detector_batch.py +178 -63
  73. detection/run_inference_with_yolov5_val.py +326 -57
  74. detection/run_tiled_inference.py +153 -43
  75. detection/video_utils.py +34 -8
  76. md_utils/ct_utils.py +172 -1
  77. md_utils/md_tests.py +372 -51
  78. md_utils/path_utils.py +167 -39
  79. md_utils/process_utils.py +26 -7
  80. md_utils/split_locations_into_train_val.py +215 -0
  81. md_utils/string_utils.py +10 -0
  82. md_utils/url_utils.py +0 -2
  83. md_utils/write_html_image_list.py +9 -26
  84. md_visualization/plot_utils.py +12 -8
  85. md_visualization/visualization_utils.py +106 -7
  86. md_visualization/visualize_db.py +16 -8
  87. md_visualization/visualize_detector_output.py +208 -97
  88. {megadetector-5.0.5.dist-info → megadetector-5.0.7.dist-info}/METADATA +3 -6
  89. {megadetector-5.0.5.dist-info → megadetector-5.0.7.dist-info}/RECORD +98 -121
  90. {megadetector-5.0.5.dist-info → megadetector-5.0.7.dist-info}/WHEEL +1 -1
  91. taxonomy_mapping/map_lila_taxonomy_to_wi_taxonomy.py +1 -1
  92. taxonomy_mapping/map_new_lila_datasets.py +43 -39
  93. taxonomy_mapping/prepare_lila_taxonomy_release.py +5 -2
  94. taxonomy_mapping/preview_lila_taxonomy.py +27 -27
  95. taxonomy_mapping/species_lookup.py +33 -13
  96. taxonomy_mapping/taxonomy_csv_checker.py +7 -5
  97. api/synchronous/api_core/yolov5/detect.py +0 -252
  98. api/synchronous/api_core/yolov5/export.py +0 -607
  99. api/synchronous/api_core/yolov5/hubconf.py +0 -146
  100. api/synchronous/api_core/yolov5/models/__init__.py +0 -0
  101. api/synchronous/api_core/yolov5/models/common.py +0 -738
  102. api/synchronous/api_core/yolov5/models/experimental.py +0 -104
  103. api/synchronous/api_core/yolov5/models/tf.py +0 -574
  104. api/synchronous/api_core/yolov5/models/yolo.py +0 -338
  105. api/synchronous/api_core/yolov5/train.py +0 -670
  106. api/synchronous/api_core/yolov5/utils/__init__.py +0 -36
  107. api/synchronous/api_core/yolov5/utils/activations.py +0 -103
  108. api/synchronous/api_core/yolov5/utils/augmentations.py +0 -284
  109. api/synchronous/api_core/yolov5/utils/autoanchor.py +0 -170
  110. api/synchronous/api_core/yolov5/utils/autobatch.py +0 -66
  111. api/synchronous/api_core/yolov5/utils/aws/__init__.py +0 -0
  112. api/synchronous/api_core/yolov5/utils/aws/resume.py +0 -40
  113. api/synchronous/api_core/yolov5/utils/benchmarks.py +0 -148
  114. api/synchronous/api_core/yolov5/utils/callbacks.py +0 -71
  115. api/synchronous/api_core/yolov5/utils/dataloaders.py +0 -1087
  116. api/synchronous/api_core/yolov5/utils/downloads.py +0 -178
  117. api/synchronous/api_core/yolov5/utils/flask_rest_api/example_request.py +0 -19
  118. api/synchronous/api_core/yolov5/utils/flask_rest_api/restapi.py +0 -46
  119. api/synchronous/api_core/yolov5/utils/general.py +0 -1018
  120. api/synchronous/api_core/yolov5/utils/loggers/__init__.py +0 -187
  121. api/synchronous/api_core/yolov5/utils/loggers/wandb/__init__.py +0 -0
  122. api/synchronous/api_core/yolov5/utils/loggers/wandb/log_dataset.py +0 -27
  123. api/synchronous/api_core/yolov5/utils/loggers/wandb/sweep.py +0 -41
  124. api/synchronous/api_core/yolov5/utils/loggers/wandb/wandb_utils.py +0 -577
  125. api/synchronous/api_core/yolov5/utils/loss.py +0 -234
  126. api/synchronous/api_core/yolov5/utils/metrics.py +0 -355
  127. api/synchronous/api_core/yolov5/utils/plots.py +0 -489
  128. api/synchronous/api_core/yolov5/utils/torch_utils.py +0 -314
  129. api/synchronous/api_core/yolov5/val.py +0 -394
  130. md_utils/matlab_porting_tools.py +0 -97
  131. {megadetector-5.0.5.dist-info → megadetector-5.0.7.dist-info}/LICENSE +0 -0
  132. {megadetector-5.0.5.dist-info → megadetector-5.0.7.dist-info}/top_level.txt +0 -0
@@ -1,489 +0,0 @@
1
- # YOLOv5 🚀 by Ultralytics, GPL-3.0 license
2
- """
3
- Plotting utils
4
- """
5
-
6
- import math
7
- import os
8
- from copy import copy
9
- from pathlib import Path
10
- from urllib.error import URLError
11
-
12
- import cv2
13
- import matplotlib
14
- import matplotlib.pyplot as plt
15
- import numpy as np
16
- import pandas as pd
17
- import seaborn as sn
18
- import torch
19
- from PIL import Image, ImageDraw, ImageFont
20
-
21
- from utils.general import (CONFIG_DIR, FONT, LOGGER, Timeout, check_font, check_requirements, clip_coords,
22
- increment_path, is_ascii, threaded, try_except, xywh2xyxy, xyxy2xywh)
23
- from utils.metrics import fitness
24
-
25
- # Settings
26
- RANK = int(os.getenv('RANK', -1))
27
- matplotlib.rc('font', **{'size': 11})
28
- matplotlib.use('Agg') # for writing to files only
29
-
30
-
31
- class Colors:
32
- # Ultralytics color palette https://ultralytics.com/
33
- def __init__(self):
34
- # hex = matplotlib.colors.TABLEAU_COLORS.values()
35
- hexs = ('FF3838', 'FF9D97', 'FF701F', 'FFB21D', 'CFD231', '48F90A', '92CC17', '3DDB86', '1A9334', '00D4BB',
36
- '2C99A8', '00C2FF', '344593', '6473FF', '0018EC', '8438FF', '520085', 'CB38FF', 'FF95C8', 'FF37C7')
37
- self.palette = [self.hex2rgb(f'#{c}') for c in hexs]
38
- self.n = len(self.palette)
39
-
40
- def __call__(self, i, bgr=False):
41
- c = self.palette[int(i) % self.n]
42
- return (c[2], c[1], c[0]) if bgr else c
43
-
44
- @staticmethod
45
- def hex2rgb(h): # rgb order (PIL)
46
- return tuple(int(h[1 + i:1 + i + 2], 16) for i in (0, 2, 4))
47
-
48
-
49
- colors = Colors() # create instance for 'from utils.plots import colors'
50
-
51
-
52
- def check_pil_font(font=FONT, size=10):
53
- # Return a PIL TrueType Font, downloading to CONFIG_DIR if necessary
54
- font = Path(font)
55
- font = font if font.exists() else (CONFIG_DIR / font.name)
56
- try:
57
- return ImageFont.truetype(str(font) if font.exists() else font.name, size)
58
- except Exception: # download if missing
59
- try:
60
- check_font(font)
61
- return ImageFont.truetype(str(font), size)
62
- except TypeError:
63
- check_requirements('Pillow>=8.4.0') # known issue https://github.com/ultralytics/yolov5/issues/5374
64
- except URLError: # not online
65
- return ImageFont.load_default()
66
-
67
-
68
- class Annotator:
69
- # YOLOv5 Annotator for train/val mosaics and jpgs and detect/hub inference annotations
70
- def __init__(self, im, line_width=None, font_size=None, font='Arial.ttf', pil=False, example='abc'):
71
- assert im.data.contiguous, 'Image not contiguous. Apply np.ascontiguousarray(im) to Annotator() input images.'
72
- non_ascii = not is_ascii(example) # non-latin labels, i.e. asian, arabic, cyrillic
73
- self.pil = pil or non_ascii
74
- if self.pil: # use PIL
75
- self.im = im if isinstance(im, Image.Image) else Image.fromarray(im)
76
- self.draw = ImageDraw.Draw(self.im)
77
- self.font = check_pil_font(font='Arial.Unicode.ttf' if non_ascii else font,
78
- size=font_size or max(round(sum(self.im.size) / 2 * 0.035), 12))
79
- else: # use cv2
80
- self.im = im
81
- self.lw = line_width or max(round(sum(im.shape) / 2 * 0.003), 2) # line width
82
-
83
- def box_label(self, box, label='', color=(128, 128, 128), txt_color=(255, 255, 255)):
84
- # Add one xyxy box to image with label
85
- if self.pil or not is_ascii(label):
86
- self.draw.rectangle(box, width=self.lw, outline=color) # box
87
- if label:
88
- w, h = self.font.getsize(label) # text width, height
89
- outside = box[1] - h >= 0 # label fits outside box
90
- self.draw.rectangle(
91
- (box[0], box[1] - h if outside else box[1], box[0] + w + 1,
92
- box[1] + 1 if outside else box[1] + h + 1),
93
- fill=color,
94
- )
95
- # self.draw.text((box[0], box[1]), label, fill=txt_color, font=self.font, anchor='ls') # for PIL>8.0
96
- self.draw.text((box[0], box[1] - h if outside else box[1]), label, fill=txt_color, font=self.font)
97
- else: # cv2
98
- p1, p2 = (int(box[0]), int(box[1])), (int(box[2]), int(box[3]))
99
- cv2.rectangle(self.im, p1, p2, color, thickness=self.lw, lineType=cv2.LINE_AA)
100
- if label:
101
- tf = max(self.lw - 1, 1) # font thickness
102
- w, h = cv2.getTextSize(label, 0, fontScale=self.lw / 3, thickness=tf)[0] # text width, height
103
- outside = p1[1] - h >= 3
104
- p2 = p1[0] + w, p1[1] - h - 3 if outside else p1[1] + h + 3
105
- cv2.rectangle(self.im, p1, p2, color, -1, cv2.LINE_AA) # filled
106
- cv2.putText(self.im,
107
- label, (p1[0], p1[1] - 2 if outside else p1[1] + h + 2),
108
- 0,
109
- self.lw / 3,
110
- txt_color,
111
- thickness=tf,
112
- lineType=cv2.LINE_AA)
113
-
114
- def rectangle(self, xy, fill=None, outline=None, width=1):
115
- # Add rectangle to image (PIL-only)
116
- self.draw.rectangle(xy, fill, outline, width)
117
-
118
- def text(self, xy, text, txt_color=(255, 255, 255)):
119
- # Add text to image (PIL-only)
120
- w, h = self.font.getsize(text) # text width, height
121
- self.draw.text((xy[0], xy[1] - h + 1), text, fill=txt_color, font=self.font)
122
-
123
- def result(self):
124
- # Return annotated image as array
125
- return np.asarray(self.im)
126
-
127
-
128
- def feature_visualization(x, module_type, stage, n=32, save_dir=Path('runs/detect/exp')):
129
- """
130
- x: Features to be visualized
131
- module_type: Module type
132
- stage: Module stage within model
133
- n: Maximum number of feature maps to plot
134
- save_dir: Directory to save results
135
- """
136
- if 'Detect' not in module_type:
137
- batch, channels, height, width = x.shape # batch, channels, height, width
138
- if height > 1 and width > 1:
139
- f = save_dir / f"stage{stage}_{module_type.split('.')[-1]}_features.png" # filename
140
-
141
- blocks = torch.chunk(x[0].cpu(), channels, dim=0) # select batch index 0, block by channels
142
- n = min(n, channels) # number of plots
143
- fig, ax = plt.subplots(math.ceil(n / 8), 8, tight_layout=True) # 8 rows x n/8 cols
144
- ax = ax.ravel()
145
- plt.subplots_adjust(wspace=0.05, hspace=0.05)
146
- for i in range(n):
147
- ax[i].imshow(blocks[i].squeeze()) # cmap='gray'
148
- ax[i].axis('off')
149
-
150
- LOGGER.info(f'Saving {f}... ({n}/{channels})')
151
- plt.savefig(f, dpi=300, bbox_inches='tight')
152
- plt.close()
153
- np.save(str(f.with_suffix('.npy')), x[0].cpu().numpy()) # npy save
154
-
155
-
156
- def hist2d(x, y, n=100):
157
- # 2d histogram used in labels.png and evolve.png
158
- xedges, yedges = np.linspace(x.min(), x.max(), n), np.linspace(y.min(), y.max(), n)
159
- hist, xedges, yedges = np.histogram2d(x, y, (xedges, yedges))
160
- xidx = np.clip(np.digitize(x, xedges) - 1, 0, hist.shape[0] - 1)
161
- yidx = np.clip(np.digitize(y, yedges) - 1, 0, hist.shape[1] - 1)
162
- return np.log(hist[xidx, yidx])
163
-
164
-
165
- def butter_lowpass_filtfilt(data, cutoff=1500, fs=50000, order=5):
166
- from scipy.signal import butter, filtfilt
167
-
168
- # https://stackoverflow.com/questions/28536191/how-to-filter-smooth-with-scipy-numpy
169
- def butter_lowpass(cutoff, fs, order):
170
- nyq = 0.5 * fs
171
- normal_cutoff = cutoff / nyq
172
- return butter(order, normal_cutoff, btype='low', analog=False)
173
-
174
- b, a = butter_lowpass(cutoff, fs, order=order)
175
- return filtfilt(b, a, data) # forward-backward filter
176
-
177
-
178
- def output_to_target(output):
179
- # Convert model output to target format [batch_id, class_id, x, y, w, h, conf]
180
- targets = []
181
- for i, o in enumerate(output):
182
- for *box, conf, cls in o.cpu().numpy():
183
- targets.append([i, cls, *list(*xyxy2xywh(np.array(box)[None])), conf])
184
- return np.array(targets)
185
-
186
-
187
- @threaded
188
- def plot_images(images, targets, paths=None, fname='images.jpg', names=None, max_size=1920, max_subplots=16):
189
- # Plot image grid with labels
190
- if isinstance(images, torch.Tensor):
191
- images = images.cpu().float().numpy()
192
- if isinstance(targets, torch.Tensor):
193
- targets = targets.cpu().numpy()
194
- if np.max(images[0]) <= 1:
195
- images *= 255 # de-normalise (optional)
196
- bs, _, h, w = images.shape # batch size, _, height, width
197
- bs = min(bs, max_subplots) # limit plot images
198
- ns = np.ceil(bs ** 0.5) # number of subplots (square)
199
-
200
- # Build Image
201
- mosaic = np.full((int(ns * h), int(ns * w), 3), 255, dtype=np.uint8) # init
202
- for i, im in enumerate(images):
203
- if i == max_subplots: # if last batch has fewer images than we expect
204
- break
205
- x, y = int(w * (i // ns)), int(h * (i % ns)) # block origin
206
- im = im.transpose(1, 2, 0)
207
- mosaic[y:y + h, x:x + w, :] = im
208
-
209
- # Resize (optional)
210
- scale = max_size / ns / max(h, w)
211
- if scale < 1:
212
- h = math.ceil(scale * h)
213
- w = math.ceil(scale * w)
214
- mosaic = cv2.resize(mosaic, tuple(int(x * ns) for x in (w, h)))
215
-
216
- # Annotate
217
- fs = int((h + w) * ns * 0.01) # font size
218
- annotator = Annotator(mosaic, line_width=round(fs / 10), font_size=fs, pil=True, example=names)
219
- for i in range(i + 1):
220
- x, y = int(w * (i // ns)), int(h * (i % ns)) # block origin
221
- annotator.rectangle([x, y, x + w, y + h], None, (255, 255, 255), width=2) # borders
222
- if paths:
223
- annotator.text((x + 5, y + 5 + h), text=Path(paths[i]).name[:40], txt_color=(220, 220, 220)) # filenames
224
- if len(targets) > 0:
225
- ti = targets[targets[:, 0] == i] # image targets
226
- boxes = xywh2xyxy(ti[:, 2:6]).T
227
- classes = ti[:, 1].astype('int')
228
- labels = ti.shape[1] == 6 # labels if no conf column
229
- conf = None if labels else ti[:, 6] # check for confidence presence (label vs pred)
230
-
231
- if boxes.shape[1]:
232
- if boxes.max() <= 1.01: # if normalized with tolerance 0.01
233
- boxes[[0, 2]] *= w # scale to pixels
234
- boxes[[1, 3]] *= h
235
- elif scale < 1: # absolute coords need scale if image scales
236
- boxes *= scale
237
- boxes[[0, 2]] += x
238
- boxes[[1, 3]] += y
239
- for j, box in enumerate(boxes.T.tolist()):
240
- cls = classes[j]
241
- color = colors(cls)
242
- cls = names[cls] if names else cls
243
- if labels or conf[j] > 0.25: # 0.25 conf thresh
244
- label = f'{cls}' if labels else f'{cls} {conf[j]:.1f}'
245
- annotator.box_label(box, label, color=color)
246
- annotator.im.save(fname) # save
247
-
248
-
249
- def plot_lr_scheduler(optimizer, scheduler, epochs=300, save_dir=''):
250
- # Plot LR simulating training for full epochs
251
- optimizer, scheduler = copy(optimizer), copy(scheduler) # do not modify originals
252
- y = []
253
- for _ in range(epochs):
254
- scheduler.step()
255
- y.append(optimizer.param_groups[0]['lr'])
256
- plt.plot(y, '.-', label='LR')
257
- plt.xlabel('epoch')
258
- plt.ylabel('LR')
259
- plt.grid()
260
- plt.xlim(0, epochs)
261
- plt.ylim(0)
262
- plt.savefig(Path(save_dir) / 'LR.png', dpi=200)
263
- plt.close()
264
-
265
-
266
- def plot_val_txt(): # from utils.plots import *; plot_val()
267
- # Plot val.txt histograms
268
- x = np.loadtxt('val.txt', dtype=np.float32)
269
- box = xyxy2xywh(x[:, :4])
270
- cx, cy = box[:, 0], box[:, 1]
271
-
272
- fig, ax = plt.subplots(1, 1, figsize=(6, 6), tight_layout=True)
273
- ax.hist2d(cx, cy, bins=600, cmax=10, cmin=0)
274
- ax.set_aspect('equal')
275
- plt.savefig('hist2d.png', dpi=300)
276
-
277
- fig, ax = plt.subplots(1, 2, figsize=(12, 6), tight_layout=True)
278
- ax[0].hist(cx, bins=600)
279
- ax[1].hist(cy, bins=600)
280
- plt.savefig('hist1d.png', dpi=200)
281
-
282
-
283
- def plot_targets_txt(): # from utils.plots import *; plot_targets_txt()
284
- # Plot targets.txt histograms
285
- x = np.loadtxt('targets.txt', dtype=np.float32).T
286
- s = ['x targets', 'y targets', 'width targets', 'height targets']
287
- fig, ax = plt.subplots(2, 2, figsize=(8, 8), tight_layout=True)
288
- ax = ax.ravel()
289
- for i in range(4):
290
- ax[i].hist(x[i], bins=100, label=f'{x[i].mean():.3g} +/- {x[i].std():.3g}')
291
- ax[i].legend()
292
- ax[i].set_title(s[i])
293
- plt.savefig('targets.jpg', dpi=200)
294
-
295
-
296
- def plot_val_study(file='', dir='', x=None): # from utils.plots import *; plot_val_study()
297
- # Plot file=study.txt generated by val.py (or plot all study*.txt in dir)
298
- save_dir = Path(file).parent if file else Path(dir)
299
- plot2 = False # plot additional results
300
- if plot2:
301
- ax = plt.subplots(2, 4, figsize=(10, 6), tight_layout=True)[1].ravel()
302
-
303
- fig2, ax2 = plt.subplots(1, 1, figsize=(8, 4), tight_layout=True)
304
- # for f in [save_dir / f'study_coco_{x}.txt' for x in ['yolov5n6', 'yolov5s6', 'yolov5m6', 'yolov5l6', 'yolov5x6']]:
305
- for f in sorted(save_dir.glob('study*.txt')):
306
- y = np.loadtxt(f, dtype=np.float32, usecols=[0, 1, 2, 3, 7, 8, 9], ndmin=2).T
307
- x = np.arange(y.shape[1]) if x is None else np.array(x)
308
- if plot2:
309
- s = ['P', 'R', 'mAP@.5', 'mAP@.5:.95', 't_preprocess (ms/img)', 't_inference (ms/img)', 't_NMS (ms/img)']
310
- for i in range(7):
311
- ax[i].plot(x, y[i], '.-', linewidth=2, markersize=8)
312
- ax[i].set_title(s[i])
313
-
314
- j = y[3].argmax() + 1
315
- ax2.plot(y[5, 1:j],
316
- y[3, 1:j] * 1E2,
317
- '.-',
318
- linewidth=2,
319
- markersize=8,
320
- label=f.stem.replace('study_coco_', '').replace('yolo', 'YOLO'))
321
-
322
- ax2.plot(1E3 / np.array([209, 140, 97, 58, 35, 18]), [34.6, 40.5, 43.0, 47.5, 49.7, 51.5],
323
- 'k.-',
324
- linewidth=2,
325
- markersize=8,
326
- alpha=.25,
327
- label='EfficientDet')
328
-
329
- ax2.grid(alpha=0.2)
330
- ax2.set_yticks(np.arange(20, 60, 5))
331
- ax2.set_xlim(0, 57)
332
- ax2.set_ylim(25, 55)
333
- ax2.set_xlabel('GPU Speed (ms/img)')
334
- ax2.set_ylabel('COCO AP val')
335
- ax2.legend(loc='lower right')
336
- f = save_dir / 'study.png'
337
- print(f'Saving {f}...')
338
- plt.savefig(f, dpi=300)
339
-
340
-
341
- @try_except # known issue https://github.com/ultralytics/yolov5/issues/5395
342
- @Timeout(30) # known issue https://github.com/ultralytics/yolov5/issues/5611
343
- def plot_labels(labels, names=(), save_dir=Path('')):
344
- # plot dataset labels
345
- LOGGER.info(f"Plotting labels to {save_dir / 'labels.jpg'}... ")
346
- c, b = labels[:, 0], labels[:, 1:].transpose() # classes, boxes
347
- nc = int(c.max() + 1) # number of classes
348
- x = pd.DataFrame(b.transpose(), columns=['x', 'y', 'width', 'height'])
349
-
350
- # seaborn correlogram
351
- sn.pairplot(x, corner=True, diag_kind='auto', kind='hist', diag_kws=dict(bins=50), plot_kws=dict(pmax=0.9))
352
- plt.savefig(save_dir / 'labels_correlogram.jpg', dpi=200)
353
- plt.close()
354
-
355
- # matplotlib labels
356
- matplotlib.use('svg') # faster
357
- ax = plt.subplots(2, 2, figsize=(8, 8), tight_layout=True)[1].ravel()
358
- y = ax[0].hist(c, bins=np.linspace(0, nc, nc + 1) - 0.5, rwidth=0.8)
359
- try: # color histogram bars by class
360
- [y[2].patches[i].set_color([x / 255 for x in colors(i)]) for i in range(nc)] # known issue #3195
361
- except Exception:
362
- pass
363
- ax[0].set_ylabel('instances')
364
- if 0 < len(names) < 30:
365
- ax[0].set_xticks(range(len(names)))
366
- ax[0].set_xticklabels(names, rotation=90, fontsize=10)
367
- else:
368
- ax[0].set_xlabel('classes')
369
- sn.histplot(x, x='x', y='y', ax=ax[2], bins=50, pmax=0.9)
370
- sn.histplot(x, x='width', y='height', ax=ax[3], bins=50, pmax=0.9)
371
-
372
- # rectangles
373
- labels[:, 1:3] = 0.5 # center
374
- labels[:, 1:] = xywh2xyxy(labels[:, 1:]) * 2000
375
- img = Image.fromarray(np.ones((2000, 2000, 3), dtype=np.uint8) * 255)
376
- for cls, *box in labels[:1000]:
377
- ImageDraw.Draw(img).rectangle(box, width=1, outline=colors(cls)) # plot
378
- ax[1].imshow(img)
379
- ax[1].axis('off')
380
-
381
- for a in [0, 1, 2, 3]:
382
- for s in ['top', 'right', 'left', 'bottom']:
383
- ax[a].spines[s].set_visible(False)
384
-
385
- plt.savefig(save_dir / 'labels.jpg', dpi=200)
386
- matplotlib.use('Agg')
387
- plt.close()
388
-
389
-
390
- def plot_evolve(evolve_csv='path/to/evolve.csv'): # from utils.plots import *; plot_evolve()
391
- # Plot evolve.csv hyp evolution results
392
- evolve_csv = Path(evolve_csv)
393
- data = pd.read_csv(evolve_csv)
394
- keys = [x.strip() for x in data.columns]
395
- x = data.values
396
- f = fitness(x)
397
- j = np.argmax(f) # max fitness index
398
- plt.figure(figsize=(10, 12), tight_layout=True)
399
- matplotlib.rc('font', **{'size': 8})
400
- print(f'Best results from row {j} of {evolve_csv}:')
401
- for i, k in enumerate(keys[7:]):
402
- v = x[:, 7 + i]
403
- mu = v[j] # best single result
404
- plt.subplot(6, 5, i + 1)
405
- plt.scatter(v, f, c=hist2d(v, f, 20), cmap='viridis', alpha=.8, edgecolors='none')
406
- plt.plot(mu, f.max(), 'k+', markersize=15)
407
- plt.title(f'{k} = {mu:.3g}', fontdict={'size': 9}) # limit to 40 characters
408
- if i % 5 != 0:
409
- plt.yticks([])
410
- print(f'{k:>15}: {mu:.3g}')
411
- f = evolve_csv.with_suffix('.png') # filename
412
- plt.savefig(f, dpi=200)
413
- plt.close()
414
- print(f'Saved {f}')
415
-
416
-
417
- def plot_results(file='path/to/results.csv', dir=''):
418
- # Plot training results.csv. Usage: from utils.plots import *; plot_results('path/to/results.csv')
419
- save_dir = Path(file).parent if file else Path(dir)
420
- fig, ax = plt.subplots(2, 5, figsize=(12, 6), tight_layout=True)
421
- ax = ax.ravel()
422
- files = list(save_dir.glob('results*.csv'))
423
- assert len(files), f'No results.csv files found in {save_dir.resolve()}, nothing to plot.'
424
- for f in files:
425
- try:
426
- data = pd.read_csv(f)
427
- s = [x.strip() for x in data.columns]
428
- x = data.values[:, 0]
429
- for i, j in enumerate([1, 2, 3, 4, 5, 8, 9, 10, 6, 7]):
430
- y = data.values[:, j].astype('float')
431
- # y[y == 0] = np.nan # don't show zero values
432
- ax[i].plot(x, y, marker='.', label=f.stem, linewidth=2, markersize=8)
433
- ax[i].set_title(s[j], fontsize=12)
434
- # if j in [8, 9, 10]: # share train and val loss y axes
435
- # ax[i].get_shared_y_axes().join(ax[i], ax[i - 5])
436
- except Exception as e:
437
- LOGGER.info(f'Warning: Plotting error for {f}: {e}')
438
- ax[1].legend()
439
- fig.savefig(save_dir / 'results.png', dpi=200)
440
- plt.close()
441
-
442
-
443
- def profile_idetection(start=0, stop=0, labels=(), save_dir=''):
444
- # Plot iDetection '*.txt' per-image logs. from utils.plots import *; profile_idetection()
445
- ax = plt.subplots(2, 4, figsize=(12, 6), tight_layout=True)[1].ravel()
446
- s = ['Images', 'Free Storage (GB)', 'RAM Usage (GB)', 'Battery', 'dt_raw (ms)', 'dt_smooth (ms)', 'real-world FPS']
447
- files = list(Path(save_dir).glob('frames*.txt'))
448
- for fi, f in enumerate(files):
449
- try:
450
- results = np.loadtxt(f, ndmin=2).T[:, 90:-30] # clip first and last rows
451
- n = results.shape[1] # number of rows
452
- x = np.arange(start, min(stop, n) if stop else n)
453
- results = results[:, x]
454
- t = (results[0] - results[0].min()) # set t0=0s
455
- results[0] = x
456
- for i, a in enumerate(ax):
457
- if i < len(results):
458
- label = labels[fi] if len(labels) else f.stem.replace('frames_', '')
459
- a.plot(t, results[i], marker='.', label=label, linewidth=1, markersize=5)
460
- a.set_title(s[i])
461
- a.set_xlabel('time (s)')
462
- # if fi == len(files) - 1:
463
- # a.set_ylim(bottom=0)
464
- for side in ['top', 'right']:
465
- a.spines[side].set_visible(False)
466
- else:
467
- a.remove()
468
- except Exception as e:
469
- print(f'Warning: Plotting error for {f}; {e}')
470
- ax[1].legend()
471
- plt.savefig(Path(save_dir) / 'idetection_profile.png', dpi=200)
472
-
473
-
474
- def save_one_box(xyxy, im, file=Path('im.jpg'), gain=1.02, pad=10, square=False, BGR=False, save=True):
475
- # Save image crop as {file} with crop size multiple {gain} and {pad} pixels. Save and/or return crop
476
- xyxy = torch.tensor(xyxy).view(-1, 4)
477
- b = xyxy2xywh(xyxy) # boxes
478
- if square:
479
- b[:, 2:] = b[:, 2:].max(1)[0].unsqueeze(1) # attempt rectangle to square
480
- b[:, 2:] = b[:, 2:] * gain + pad # box wh * gain + pad
481
- xyxy = xywh2xyxy(b).long()
482
- clip_coords(xyxy, im.shape)
483
- crop = im[int(xyxy[0, 1]):int(xyxy[0, 3]), int(xyxy[0, 0]):int(xyxy[0, 2]), ::(1 if BGR else -1)]
484
- if save:
485
- file.parent.mkdir(parents=True, exist_ok=True) # make directory
486
- f = str(increment_path(file).with_suffix('.jpg'))
487
- # cv2.imwrite(f, crop) # https://github.com/ultralytics/yolov5/issues/7007 chroma subsampling issue
488
- Image.fromarray(cv2.cvtColor(crop, cv2.COLOR_BGR2RGB)).save(f, quality=95, subsampling=0)
489
- return crop