segment-everything 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.
Files changed (145) hide show
  1. segment_everything/__init__.py +5 -0
  2. segment_everything/augmentation/albumentations_helper.py +0 -0
  3. segment_everything/detect_and_segment.py +131 -0
  4. segment_everything/napari_helper.py +15 -0
  5. segment_everything/prompt_generator.py +188 -0
  6. segment_everything/py.typed +5 -0
  7. segment_everything/stacked_label_dataset.py +113 -0
  8. segment_everything/stacked_labels.py +428 -0
  9. segment_everything/vendored/PromptGuidedDecoder/Prompt_guided_Mask_Decoder.pt +0 -0
  10. segment_everything/vendored/__init__.py +5 -0
  11. segment_everything/vendored/dice.py +158 -0
  12. segment_everything/vendored/efficientvit/__init__.py +0 -0
  13. segment_everything/vendored/efficientvit/apps/__init__.py +0 -0
  14. segment_everything/vendored/efficientvit/apps/data_provider/__init__.py +7 -0
  15. segment_everything/vendored/efficientvit/apps/data_provider/augment/__init__.py +6 -0
  16. segment_everything/vendored/efficientvit/apps/data_provider/augment/bbox.py +30 -0
  17. segment_everything/vendored/efficientvit/apps/data_provider/augment/color_aug.py +78 -0
  18. segment_everything/vendored/efficientvit/apps/data_provider/base.py +254 -0
  19. segment_everything/vendored/efficientvit/apps/data_provider/random_resolution/__init__.py +6 -0
  20. segment_everything/vendored/efficientvit/apps/data_provider/random_resolution/_data_loader.py +1538 -0
  21. segment_everything/vendored/efficientvit/apps/data_provider/random_resolution/_data_worker.py +357 -0
  22. segment_everything/vendored/efficientvit/apps/data_provider/random_resolution/controller.py +100 -0
  23. segment_everything/vendored/efficientvit/apps/setup.py +150 -0
  24. segment_everything/vendored/efficientvit/apps/trainer/__init__.py +6 -0
  25. segment_everything/vendored/efficientvit/apps/trainer/base.py +318 -0
  26. segment_everything/vendored/efficientvit/apps/trainer/run_config.py +129 -0
  27. segment_everything/vendored/efficientvit/apps/utils/__init__.py +12 -0
  28. segment_everything/vendored/efficientvit/apps/utils/dist.py +32 -0
  29. segment_everything/vendored/efficientvit/apps/utils/ema.py +52 -0
  30. segment_everything/vendored/efficientvit/apps/utils/export.py +45 -0
  31. segment_everything/vendored/efficientvit/apps/utils/init.py +66 -0
  32. segment_everything/vendored/efficientvit/apps/utils/lr.py +52 -0
  33. segment_everything/vendored/efficientvit/apps/utils/metric.py +43 -0
  34. segment_everything/vendored/efficientvit/apps/utils/misc.py +101 -0
  35. segment_everything/vendored/efficientvit/apps/utils/opt.py +28 -0
  36. segment_everything/vendored/efficientvit/cls_model_zoo.py +79 -0
  37. segment_everything/vendored/efficientvit/clscore/__init__.py +0 -0
  38. segment_everything/vendored/efficientvit/clscore/data_provider/__init__.py +5 -0
  39. segment_everything/vendored/efficientvit/clscore/data_provider/imagenet.py +142 -0
  40. segment_everything/vendored/efficientvit/clscore/trainer/__init__.py +6 -0
  41. segment_everything/vendored/efficientvit/clscore/trainer/cls_run_config.py +18 -0
  42. segment_everything/vendored/efficientvit/clscore/trainer/cls_trainer.py +265 -0
  43. segment_everything/vendored/efficientvit/clscore/trainer/utils/__init__.py +7 -0
  44. segment_everything/vendored/efficientvit/clscore/trainer/utils/label_smooth.py +18 -0
  45. segment_everything/vendored/efficientvit/clscore/trainer/utils/metric.py +23 -0
  46. segment_everything/vendored/efficientvit/clscore/trainer/utils/mixup.py +67 -0
  47. segment_everything/vendored/efficientvit/models/__init__.py +0 -0
  48. segment_everything/vendored/efficientvit/models/efficientvit/__init__.py +8 -0
  49. segment_everything/vendored/efficientvit/models/efficientvit/backbone.py +380 -0
  50. segment_everything/vendored/efficientvit/models/efficientvit/cls.py +188 -0
  51. segment_everything/vendored/efficientvit/models/efficientvit/sam.py +181 -0
  52. segment_everything/vendored/efficientvit/models/efficientvit/seg.py +373 -0
  53. segment_everything/vendored/efficientvit/models/nn/__init__.py +8 -0
  54. segment_everything/vendored/efficientvit/models/nn/act.py +30 -0
  55. segment_everything/vendored/efficientvit/models/nn/drop.py +104 -0
  56. segment_everything/vendored/efficientvit/models/nn/norm.py +164 -0
  57. segment_everything/vendored/efficientvit/models/nn/ops.py +597 -0
  58. segment_everything/vendored/efficientvit/models/utils/__init__.py +7 -0
  59. segment_everything/vendored/efficientvit/models/utils/list.py +53 -0
  60. segment_everything/vendored/efficientvit/models/utils/network.py +73 -0
  61. segment_everything/vendored/efficientvit/models/utils/random.py +65 -0
  62. segment_everything/vendored/efficientvit/sam_model_zoo.py +45 -0
  63. segment_everything/vendored/efficientvit/seg_model_zoo.py +70 -0
  64. segment_everything/vendored/get_object_aware.py +26 -0
  65. segment_everything/vendored/mobilesamv2/__init__.py +16 -0
  66. segment_everything/vendored/mobilesamv2/automatic_mask_generator.py +415 -0
  67. segment_everything/vendored/mobilesamv2/build_sam.py +246 -0
  68. segment_everything/vendored/mobilesamv2/modeling/__init__.py +11 -0
  69. segment_everything/vendored/mobilesamv2/modeling/common.py +43 -0
  70. segment_everything/vendored/mobilesamv2/modeling/image_encoder.py +394 -0
  71. segment_everything/vendored/mobilesamv2/modeling/mask_decoder.py +213 -0
  72. segment_everything/vendored/mobilesamv2/modeling/prompt_encoder.py +217 -0
  73. segment_everything/vendored/mobilesamv2/modeling/sam.py +203 -0
  74. segment_everything/vendored/mobilesamv2/modeling/transformer.py +240 -0
  75. segment_everything/vendored/mobilesamv2/predictor.py +384 -0
  76. segment_everything/vendored/mobilesamv2/utils/__init__.py +5 -0
  77. segment_everything/vendored/mobilesamv2/utils/amg.py +347 -0
  78. segment_everything/vendored/mobilesamv2/utils/onnx.py +144 -0
  79. segment_everything/vendored/mobilesamv2/utils/transforms.py +103 -0
  80. segment_everything/vendored/object_detection/__init__.py +0 -0
  81. segment_everything/vendored/object_detection/ultralytics/__init__.py +5 -0
  82. segment_everything/vendored/object_detection/ultralytics/nn/__init__.py +9 -0
  83. segment_everything/vendored/object_detection/ultralytics/nn/autobackend.py +658 -0
  84. segment_everything/vendored/object_detection/ultralytics/nn/autoshape.py +397 -0
  85. segment_everything/vendored/object_detection/ultralytics/nn/modules/__init__.py +110 -0
  86. segment_everything/vendored/object_detection/ultralytics/nn/modules/block.py +304 -0
  87. segment_everything/vendored/object_detection/ultralytics/nn/modules/conv.py +297 -0
  88. segment_everything/vendored/object_detection/ultralytics/nn/modules/head.py +468 -0
  89. segment_everything/vendored/object_detection/ultralytics/nn/modules/transformer.py +378 -0
  90. segment_everything/vendored/object_detection/ultralytics/nn/modules/utils.py +78 -0
  91. segment_everything/vendored/object_detection/ultralytics/nn/tasks.py +1049 -0
  92. segment_everything/vendored/object_detection/ultralytics/prompt_mobilesamv2/__init__.py +6 -0
  93. segment_everything/vendored/object_detection/ultralytics/prompt_mobilesamv2/model.py +104 -0
  94. segment_everything/vendored/object_detection/ultralytics/prompt_mobilesamv2/predict.py +95 -0
  95. segment_everything/vendored/object_detection/ultralytics/yolo/__init__.py +5 -0
  96. segment_everything/vendored/object_detection/ultralytics/yolo/cfg/__init__.py +588 -0
  97. segment_everything/vendored/object_detection/ultralytics/yolo/cfg/default.yaml +117 -0
  98. segment_everything/vendored/object_detection/ultralytics/yolo/data/__init__.py +9 -0
  99. segment_everything/vendored/object_detection/ultralytics/yolo/data/annotator.py +53 -0
  100. segment_everything/vendored/object_detection/ultralytics/yolo/data/augment.py +899 -0
  101. segment_everything/vendored/object_detection/ultralytics/yolo/data/base.py +286 -0
  102. segment_everything/vendored/object_detection/ultralytics/yolo/data/build.py +213 -0
  103. segment_everything/vendored/object_detection/ultralytics/yolo/data/converter.py +358 -0
  104. segment_everything/vendored/object_detection/ultralytics/yolo/data/dataloaders/__init__.py +0 -0
  105. segment_everything/vendored/object_detection/ultralytics/yolo/data/dataloaders/stream_loaders.py +459 -0
  106. segment_everything/vendored/object_detection/ultralytics/yolo/data/dataset.py +274 -0
  107. segment_everything/vendored/object_detection/ultralytics/yolo/data/dataset_wrappers.py +53 -0
  108. segment_everything/vendored/object_detection/ultralytics/yolo/data/scripts/download_weights.sh +18 -0
  109. segment_everything/vendored/object_detection/ultralytics/yolo/data/scripts/get_coco.sh +60 -0
  110. segment_everything/vendored/object_detection/ultralytics/yolo/data/scripts/get_coco128.sh +17 -0
  111. segment_everything/vendored/object_detection/ultralytics/yolo/data/scripts/get_imagenet.sh +51 -0
  112. segment_everything/vendored/object_detection/ultralytics/yolo/data/utils.py +716 -0
  113. segment_everything/vendored/object_detection/ultralytics/yolo/engine/__init__.py +0 -0
  114. segment_everything/vendored/object_detection/ultralytics/yolo/engine/exporter.py +1214 -0
  115. segment_everything/vendored/object_detection/ultralytics/yolo/engine/model.py +641 -0
  116. segment_everything/vendored/object_detection/ultralytics/yolo/engine/predictor.py +461 -0
  117. segment_everything/vendored/object_detection/ultralytics/yolo/engine/results.py +741 -0
  118. segment_everything/vendored/object_detection/ultralytics/yolo/utils/__init__.py +893 -0
  119. segment_everything/vendored/object_detection/ultralytics/yolo/utils/autobatch.py +108 -0
  120. segment_everything/vendored/object_detection/ultralytics/yolo/utils/callbacks/__init__.py +5 -0
  121. segment_everything/vendored/object_detection/ultralytics/yolo/utils/callbacks/base.py +212 -0
  122. segment_everything/vendored/object_detection/ultralytics/yolo/utils/checks.py +547 -0
  123. segment_everything/vendored/object_detection/ultralytics/yolo/utils/dist.py +67 -0
  124. segment_everything/vendored/object_detection/ultralytics/yolo/utils/downloads.py +353 -0
  125. segment_everything/vendored/object_detection/ultralytics/yolo/utils/errors.py +12 -0
  126. segment_everything/vendored/object_detection/ultralytics/yolo/utils/files.py +100 -0
  127. segment_everything/vendored/object_detection/ultralytics/yolo/utils/instance.py +391 -0
  128. segment_everything/vendored/object_detection/ultralytics/yolo/utils/loss.py +579 -0
  129. segment_everything/vendored/object_detection/ultralytics/yolo/utils/metrics.py +1189 -0
  130. segment_everything/vendored/object_detection/ultralytics/yolo/utils/ops.py +870 -0
  131. segment_everything/vendored/object_detection/ultralytics/yolo/utils/patches.py +45 -0
  132. segment_everything/vendored/object_detection/ultralytics/yolo/utils/plotting.py +767 -0
  133. segment_everything/vendored/object_detection/ultralytics/yolo/utils/tal.py +276 -0
  134. segment_everything/vendored/object_detection/ultralytics/yolo/utils/torch_utils.py +684 -0
  135. segment_everything/vendored/object_detection/ultralytics/yolo/utils/tuner.py +54 -0
  136. segment_everything/vendored/object_detection/ultralytics/yolo/v8/__init__.py +5 -0
  137. segment_everything/vendored/object_detection/ultralytics/yolo/v8/detect/__init__.py +5 -0
  138. segment_everything/vendored/object_detection/ultralytics/yolo/v8/detect/predict.py +69 -0
  139. segment_everything/vendored/tinyvit/__init__.py +2 -0
  140. segment_everything/vendored/tinyvit/tiny_vit.py +867 -0
  141. segment_everything/weights_helper.py +124 -0
  142. segment_everything-0.1.0.dist-info/METADATA +53 -0
  143. segment_everything-0.1.0.dist-info/RECORD +145 -0
  144. segment_everything-0.1.0.dist-info/WHEEL +4 -0
  145. segment_everything-0.1.0.dist-info/licenses/LICENSE +28 -0
@@ -0,0 +1,767 @@
1
+ # Ultralytics YOLO 🚀, AGPL-3.0 license
2
+
3
+ import contextlib
4
+ import math
5
+ from pathlib import Path
6
+
7
+ import cv2
8
+ import matplotlib.pyplot as plt
9
+ import numpy as np
10
+ import torch
11
+ from PIL import Image, ImageDraw, ImageFont
12
+ from PIL import __version__ as pil_version
13
+ from scipy.ndimage import gaussian_filter1d
14
+
15
+ from ..utils import LOGGER, TryExcept, plt_settings, threaded
16
+
17
+ from .checks import check_font, check_version, is_ascii
18
+ from .files import increment_path
19
+ from .ops import clip_boxes, scale_image, xywh2xyxy, xyxy2xywh
20
+
21
+
22
+ class Colors:
23
+ # Ultralytics color palette https://ultralytics.com/
24
+ def __init__(self):
25
+ """Initialize colors as hex = matplotlib.colors.TABLEAU_COLORS.values()."""
26
+ hexs = (
27
+ "FF3838",
28
+ "FF9D97",
29
+ "FF701F",
30
+ "FFB21D",
31
+ "CFD231",
32
+ "48F90A",
33
+ "92CC17",
34
+ "3DDB86",
35
+ "1A9334",
36
+ "00D4BB",
37
+ "2C99A8",
38
+ "00C2FF",
39
+ "344593",
40
+ "6473FF",
41
+ "0018EC",
42
+ "8438FF",
43
+ "520085",
44
+ "CB38FF",
45
+ "FF95C8",
46
+ "FF37C7",
47
+ )
48
+ self.palette = [self.hex2rgb(f"#{c}") for c in hexs]
49
+ self.n = len(self.palette)
50
+ self.pose_palette = np.array(
51
+ [
52
+ [255, 128, 0],
53
+ [255, 153, 51],
54
+ [255, 178, 102],
55
+ [230, 230, 0],
56
+ [255, 153, 255],
57
+ [153, 204, 255],
58
+ [255, 102, 255],
59
+ [255, 51, 255],
60
+ [102, 178, 255],
61
+ [51, 153, 255],
62
+ [255, 153, 153],
63
+ [255, 102, 102],
64
+ [255, 51, 51],
65
+ [153, 255, 153],
66
+ [102, 255, 102],
67
+ [51, 255, 51],
68
+ [0, 255, 0],
69
+ [0, 0, 255],
70
+ [255, 0, 0],
71
+ [255, 255, 255],
72
+ ],
73
+ dtype=np.uint8,
74
+ )
75
+
76
+ def __call__(self, i, bgr=False):
77
+ """Converts hex color codes to rgb values."""
78
+ c = self.palette[int(i) % self.n]
79
+ return (c[2], c[1], c[0]) if bgr else c
80
+
81
+ @staticmethod
82
+ def hex2rgb(h): # rgb order (PIL)
83
+ return tuple(int(h[1 + i : 1 + i + 2], 16) for i in (0, 2, 4))
84
+
85
+
86
+ colors = Colors() # create instance for 'from utils.plots import colors'
87
+
88
+
89
+ class Annotator:
90
+ # YOLOv8 Annotator for train/val mosaics and jpgs and detect/hub inference annotations
91
+ def __init__(
92
+ self,
93
+ im,
94
+ line_width=None,
95
+ font_size=None,
96
+ font="Arial.ttf",
97
+ pil=False,
98
+ example="abc",
99
+ ):
100
+ """Initialize the Annotator class with image and line width along with color palette for keypoints and limbs."""
101
+ assert (
102
+ im.data.contiguous
103
+ ), "Image not contiguous. Apply np.ascontiguousarray(im) to Annotator() input images."
104
+ non_ascii = not is_ascii(
105
+ example
106
+ ) # non-latin labels, i.e. asian, arabic, cyrillic
107
+ self.pil = pil or non_ascii
108
+ if self.pil: # use PIL
109
+ self.im = (
110
+ im if isinstance(im, Image.Image) else Image.fromarray(im)
111
+ )
112
+ self.draw = ImageDraw.Draw(self.im)
113
+ try:
114
+ font = check_font("Arial.Unicode.ttf" if non_ascii else font)
115
+ size = font_size or max(
116
+ round(sum(self.im.size) / 2 * 0.035), 12
117
+ )
118
+ self.font = ImageFont.truetype(str(font), size)
119
+ except Exception:
120
+ self.font = ImageFont.load_default()
121
+ # Deprecation fix for w, h = getsize(string) -> _, _, w, h = getbox(string)
122
+ if check_version(pil_version, "9.2.0"):
123
+ self.font.getsize = lambda x: self.font.getbbox(x)[
124
+ 2:4
125
+ ] # text width, height
126
+ else: # use cv2
127
+ self.im = im
128
+ self.lw = line_width or max(
129
+ round(sum(im.shape) / 2 * 0.003), 2
130
+ ) # line width
131
+ # Pose
132
+ self.skeleton = [
133
+ [16, 14],
134
+ [14, 12],
135
+ [17, 15],
136
+ [15, 13],
137
+ [12, 13],
138
+ [6, 12],
139
+ [7, 13],
140
+ [6, 7],
141
+ [6, 8],
142
+ [7, 9],
143
+ [8, 10],
144
+ [9, 11],
145
+ [2, 3],
146
+ [1, 2],
147
+ [1, 3],
148
+ [2, 4],
149
+ [3, 5],
150
+ [4, 6],
151
+ [5, 7],
152
+ ]
153
+
154
+ self.limb_color = colors.pose_palette[
155
+ [9, 9, 9, 9, 7, 7, 7, 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 16, 16]
156
+ ]
157
+ self.kpt_color = colors.pose_palette[
158
+ [16, 16, 16, 16, 16, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9]
159
+ ]
160
+
161
+ def box_label(
162
+ self, box, label="", color=(128, 128, 128), txt_color=(255, 255, 255)
163
+ ):
164
+ """Add one xyxy box to image with label."""
165
+ if isinstance(box, torch.Tensor):
166
+ box = box.tolist()
167
+ if self.pil or not is_ascii(label):
168
+ self.draw.rectangle(box, width=self.lw, outline=color) # box
169
+ if label:
170
+ w, h = self.font.getsize(label) # text width, height
171
+ outside = box[1] - h >= 0 # label fits outside box
172
+ self.draw.rectangle(
173
+ (
174
+ box[0],
175
+ box[1] - h if outside else box[1],
176
+ box[0] + w + 1,
177
+ box[1] + 1 if outside else box[1] + h + 1,
178
+ ),
179
+ fill=color,
180
+ )
181
+ # self.draw.text((box[0], box[1]), label, fill=txt_color, font=self.font, anchor='ls') # for PIL>8.0
182
+ self.draw.text(
183
+ (box[0], box[1] - h if outside else box[1]),
184
+ label,
185
+ fill=txt_color,
186
+ font=self.font,
187
+ )
188
+ else: # cv2
189
+ p1, p2 = (int(box[0]), int(box[1])), (int(box[2]), int(box[3]))
190
+ cv2.rectangle(
191
+ self.im, p1, p2, color, thickness=self.lw, lineType=cv2.LINE_AA
192
+ )
193
+ if label:
194
+ tf = max(self.lw - 1, 1) # font thickness
195
+ w, h = cv2.getTextSize(
196
+ label, 0, fontScale=self.lw / 3, thickness=tf
197
+ )[
198
+ 0
199
+ ] # text width, height
200
+ outside = p1[1] - h >= 3
201
+ p2 = p1[0] + w, p1[1] - h - 3 if outside else p1[1] + h + 3
202
+ cv2.rectangle(
203
+ self.im, p1, p2, color, -1, cv2.LINE_AA
204
+ ) # filled
205
+ cv2.putText(
206
+ self.im,
207
+ label,
208
+ (p1[0], p1[1] - 2 if outside else p1[1] + h + 2),
209
+ 0,
210
+ self.lw / 3,
211
+ txt_color,
212
+ thickness=tf,
213
+ lineType=cv2.LINE_AA,
214
+ )
215
+
216
+ def masks(self, masks, colors, im_gpu, alpha=0.5, retina_masks=False):
217
+ """Plot masks at once.
218
+ Args:
219
+ masks (tensor): predicted masks on cuda, shape: [n, h, w]
220
+ colors (List[List[Int]]): colors for predicted masks, [[r, g, b] * n]
221
+ im_gpu (tensor): img is in cuda, shape: [3, h, w], range: [0, 1]
222
+ alpha (float): mask transparency: 0.0 fully transparent, 1.0 opaque
223
+ """
224
+ if self.pil:
225
+ # Convert to numpy first
226
+ self.im = np.asarray(self.im).copy()
227
+ if len(masks) == 0:
228
+ self.im[:] = (
229
+ im_gpu.permute(1, 2, 0).contiguous().cpu().numpy() * 255
230
+ )
231
+ if im_gpu.device != masks.device:
232
+ im_gpu = im_gpu.to(masks.device)
233
+ colors = (
234
+ torch.tensor(colors, device=masks.device, dtype=torch.float32)
235
+ / 255.0
236
+ ) # shape(n,3)
237
+ colors = colors[:, None, None] # shape(n,1,1,3)
238
+ masks = masks.unsqueeze(3) # shape(n,h,w,1)
239
+ masks_color = masks * (colors * alpha) # shape(n,h,w,3)
240
+
241
+ inv_alph_masks = (1 - masks * alpha).cumprod(0) # shape(n,h,w,1)
242
+ mcs = masks_color.max(dim=0).values # shape(n,h,w,3)
243
+
244
+ im_gpu = im_gpu.flip(dims=[0]) # flip channel
245
+ im_gpu = im_gpu.permute(1, 2, 0).contiguous() # shape(h,w,3)
246
+ im_gpu = im_gpu * inv_alph_masks[-1] + mcs
247
+ im_mask = im_gpu * 255
248
+ im_mask_np = im_mask.byte().cpu().numpy()
249
+ self.im[:] = (
250
+ im_mask_np
251
+ if retina_masks
252
+ else scale_image(im_mask_np, self.im.shape)
253
+ )
254
+ if self.pil:
255
+ # Convert im back to PIL and update draw
256
+ self.fromarray(self.im)
257
+
258
+ def kpts(self, kpts, shape=(640, 640), radius=5, kpt_line=True):
259
+ """Plot keypoints on the image.
260
+
261
+ Args:
262
+ kpts (tensor): Predicted keypoints with shape [17, 3]. Each keypoint has (x, y, confidence).
263
+ shape (tuple): Image shape as a tuple (h, w), where h is the height and w is the width.
264
+ radius (int, optional): Radius of the drawn keypoints. Default is 5.
265
+ kpt_line (bool, optional): If True, the function will draw lines connecting keypoints
266
+ for human pose. Default is True.
267
+
268
+ Note: `kpt_line=True` currently only supports human pose plotting.
269
+ """
270
+ if self.pil:
271
+ # Convert to numpy first
272
+ self.im = np.asarray(self.im).copy()
273
+ nkpt, ndim = kpts.shape
274
+ is_pose = nkpt == 17 and ndim == 3
275
+ kpt_line &= is_pose # `kpt_line=True` for now only supports human pose plotting
276
+ for i, k in enumerate(kpts):
277
+ color_k = (
278
+ [int(x) for x in self.kpt_color[i]] if is_pose else colors(i)
279
+ )
280
+ x_coord, y_coord = k[0], k[1]
281
+ if x_coord % shape[1] != 0 and y_coord % shape[0] != 0:
282
+ if len(k) == 3:
283
+ conf = k[2]
284
+ if conf < 0.5:
285
+ continue
286
+ cv2.circle(
287
+ self.im,
288
+ (int(x_coord), int(y_coord)),
289
+ radius,
290
+ color_k,
291
+ -1,
292
+ lineType=cv2.LINE_AA,
293
+ )
294
+
295
+ if kpt_line:
296
+ ndim = kpts.shape[-1]
297
+ for i, sk in enumerate(self.skeleton):
298
+ pos1 = (int(kpts[(sk[0] - 1), 0]), int(kpts[(sk[0] - 1), 1]))
299
+ pos2 = (int(kpts[(sk[1] - 1), 0]), int(kpts[(sk[1] - 1), 1]))
300
+ if ndim == 3:
301
+ conf1 = kpts[(sk[0] - 1), 2]
302
+ conf2 = kpts[(sk[1] - 1), 2]
303
+ if conf1 < 0.5 or conf2 < 0.5:
304
+ continue
305
+ if (
306
+ pos1[0] % shape[1] == 0
307
+ or pos1[1] % shape[0] == 0
308
+ or pos1[0] < 0
309
+ or pos1[1] < 0
310
+ ):
311
+ continue
312
+ if (
313
+ pos2[0] % shape[1] == 0
314
+ or pos2[1] % shape[0] == 0
315
+ or pos2[0] < 0
316
+ or pos2[1] < 0
317
+ ):
318
+ continue
319
+ cv2.line(
320
+ self.im,
321
+ pos1,
322
+ pos2,
323
+ [int(x) for x in self.limb_color[i]],
324
+ thickness=2,
325
+ lineType=cv2.LINE_AA,
326
+ )
327
+ if self.pil:
328
+ # Convert im back to PIL and update draw
329
+ self.fromarray(self.im)
330
+
331
+ def rectangle(self, xy, fill=None, outline=None, width=1):
332
+ """Add rectangle to image (PIL-only)."""
333
+ self.draw.rectangle(xy, fill, outline, width)
334
+
335
+ def text(
336
+ self,
337
+ xy,
338
+ text,
339
+ txt_color=(255, 255, 255),
340
+ anchor="top",
341
+ box_style=False,
342
+ ):
343
+ """Adds text to an image using PIL or cv2."""
344
+ if anchor == "bottom": # start y from font bottom
345
+ w, h = self.font.getsize(text) # text width, height
346
+ xy[1] += 1 - h
347
+ if self.pil:
348
+ if box_style:
349
+ w, h = self.font.getsize(text)
350
+ self.draw.rectangle(
351
+ (xy[0], xy[1], xy[0] + w + 1, xy[1] + h + 1),
352
+ fill=txt_color,
353
+ )
354
+ # Using `txt_color` for background and draw fg with white color
355
+ txt_color = (255, 255, 255)
356
+ self.draw.text(xy, text, fill=txt_color, font=self.font)
357
+ else:
358
+ if box_style:
359
+ tf = max(self.lw - 1, 1) # font thickness
360
+ w, h = cv2.getTextSize(
361
+ text, 0, fontScale=self.lw / 3, thickness=tf
362
+ )[
363
+ 0
364
+ ] # text width, height
365
+ outside = xy[1] - h >= 3
366
+ p2 = xy[0] + w, xy[1] - h - 3 if outside else xy[1] + h + 3
367
+ cv2.rectangle(
368
+ self.im, xy, p2, txt_color, -1, cv2.LINE_AA
369
+ ) # filled
370
+ # Using `txt_color` for background and draw fg with white color
371
+ txt_color = (255, 255, 255)
372
+ tf = max(self.lw - 1, 1) # font thickness
373
+ cv2.putText(
374
+ self.im,
375
+ text,
376
+ xy,
377
+ 0,
378
+ self.lw / 3,
379
+ txt_color,
380
+ thickness=tf,
381
+ lineType=cv2.LINE_AA,
382
+ )
383
+
384
+ def fromarray(self, im):
385
+ """Update self.im from a numpy array."""
386
+ self.im = im if isinstance(im, Image.Image) else Image.fromarray(im)
387
+ self.draw = ImageDraw.Draw(self.im)
388
+
389
+ def result(self):
390
+ """Return annotated image as array."""
391
+ return np.asarray(self.im)
392
+
393
+
394
+ @TryExcept() # known issue https://github.com/ultralytics/yolov5/issues/5395
395
+ @plt_settings()
396
+ def plot_labels(boxes, cls, names=(), save_dir=Path(""), on_plot=None):
397
+ """Save and plot image with no axis or spines."""
398
+ import pandas as pd
399
+ import seaborn as sn
400
+
401
+ # Plot dataset labels
402
+ LOGGER.info(f"Plotting labels to {save_dir / 'labels.jpg'}... ")
403
+ b = boxes.transpose() # classes, boxes
404
+ nc = int(cls.max() + 1) # number of classes
405
+ x = pd.DataFrame(b.transpose(), columns=["x", "y", "width", "height"])
406
+
407
+ # Seaborn correlogram
408
+ sn.pairplot(
409
+ x,
410
+ corner=True,
411
+ diag_kind="auto",
412
+ kind="hist",
413
+ diag_kws=dict(bins=50),
414
+ plot_kws=dict(pmax=0.9),
415
+ )
416
+ plt.savefig(save_dir / "labels_correlogram.jpg", dpi=200)
417
+ plt.close()
418
+
419
+ # Matplotlib labels
420
+ ax = plt.subplots(2, 2, figsize=(8, 8), tight_layout=True)[1].ravel()
421
+ y = ax[0].hist(cls, bins=np.linspace(0, nc, nc + 1) - 0.5, rwidth=0.8)
422
+ with contextlib.suppress(Exception): # color histogram bars by class
423
+ [
424
+ y[2].patches[i].set_color([x / 255 for x in colors(i)])
425
+ for i in range(nc)
426
+ ] # known issue #3195
427
+ ax[0].set_ylabel("instances")
428
+ if 0 < len(names) < 30:
429
+ ax[0].set_xticks(range(len(names)))
430
+ ax[0].set_xticklabels(list(names.values()), rotation=90, fontsize=10)
431
+ else:
432
+ ax[0].set_xlabel("classes")
433
+ sn.histplot(x, x="x", y="y", ax=ax[2], bins=50, pmax=0.9)
434
+ sn.histplot(x, x="width", y="height", ax=ax[3], bins=50, pmax=0.9)
435
+
436
+ # Rectangles
437
+ boxes[:, 0:2] = 0.5 # center
438
+ boxes = xywh2xyxy(boxes) * 1000
439
+ img = Image.fromarray(np.ones((1000, 1000, 3), dtype=np.uint8) * 255)
440
+ for cls, box in zip(cls[:500], boxes[:500]):
441
+ ImageDraw.Draw(img).rectangle(
442
+ box, width=1, outline=colors(cls)
443
+ ) # plot
444
+ ax[1].imshow(img)
445
+ ax[1].axis("off")
446
+
447
+ for a in [0, 1, 2, 3]:
448
+ for s in ["top", "right", "left", "bottom"]:
449
+ ax[a].spines[s].set_visible(False)
450
+
451
+ fname = save_dir / "labels.jpg"
452
+ plt.savefig(fname, dpi=200)
453
+ plt.close()
454
+ if on_plot:
455
+ on_plot(fname)
456
+
457
+
458
+ def save_one_box(
459
+ xyxy,
460
+ im,
461
+ file=Path("im.jpg"),
462
+ gain=1.02,
463
+ pad=10,
464
+ square=False,
465
+ BGR=False,
466
+ save=True,
467
+ ):
468
+ """Save image crop as {file} with crop size multiple {gain} and {pad} pixels. Save and/or return crop."""
469
+ b = xyxy2xywh(xyxy.view(-1, 4)) # boxes
470
+ if square:
471
+ b[:, 2:] = (
472
+ b[:, 2:].max(1)[0].unsqueeze(1)
473
+ ) # attempt rectangle to square
474
+ b[:, 2:] = b[:, 2:] * gain + pad # box wh * gain + pad
475
+ xyxy = xywh2xyxy(b).long()
476
+ clip_boxes(xyxy, im.shape)
477
+ crop = im[
478
+ int(xyxy[0, 1]) : int(xyxy[0, 3]),
479
+ int(xyxy[0, 0]) : int(xyxy[0, 2]),
480
+ :: (1 if BGR else -1),
481
+ ]
482
+ if save:
483
+ file.parent.mkdir(parents=True, exist_ok=True) # make directory
484
+ f = str(increment_path(file).with_suffix(".jpg"))
485
+ # cv2.imwrite(f, crop) # save BGR, https://github.com/ultralytics/yolov5/issues/7007 chroma subsampling issue
486
+ Image.fromarray(crop[..., ::-1]).save(
487
+ f, quality=95, subsampling=0
488
+ ) # save RGB
489
+ return crop
490
+
491
+
492
+ @threaded
493
+ def plot_images(
494
+ images,
495
+ batch_idx,
496
+ cls,
497
+ bboxes=np.zeros(0, dtype=np.float32),
498
+ masks=np.zeros(0, dtype=np.uint8),
499
+ kpts=np.zeros((0, 51), dtype=np.float32),
500
+ paths=None,
501
+ fname="images.jpg",
502
+ names=None,
503
+ on_plot=None,
504
+ ):
505
+ # Plot image grid with labels
506
+ if isinstance(images, torch.Tensor):
507
+ images = images.cpu().float().numpy()
508
+ if isinstance(cls, torch.Tensor):
509
+ cls = cls.cpu().numpy()
510
+ if isinstance(bboxes, torch.Tensor):
511
+ bboxes = bboxes.cpu().numpy()
512
+ if isinstance(masks, torch.Tensor):
513
+ masks = masks.cpu().numpy().astype(int)
514
+ if isinstance(kpts, torch.Tensor):
515
+ kpts = kpts.cpu().numpy()
516
+ if isinstance(batch_idx, torch.Tensor):
517
+ batch_idx = batch_idx.cpu().numpy()
518
+
519
+ max_size = 1920 # max image size
520
+ max_subplots = 16 # max image subplots, i.e. 4x4
521
+ bs, _, h, w = images.shape # batch size, _, height, width
522
+ bs = min(bs, max_subplots) # limit plot images
523
+ ns = np.ceil(bs**0.5) # number of subplots (square)
524
+ if np.max(images[0]) <= 1:
525
+ images *= 255 # de-normalise (optional)
526
+
527
+ # Build Image
528
+ mosaic = np.full(
529
+ (int(ns * h), int(ns * w), 3), 255, dtype=np.uint8
530
+ ) # init
531
+ for i, im in enumerate(images):
532
+ if i == max_subplots: # if last batch has fewer images than we expect
533
+ break
534
+ x, y = int(w * (i // ns)), int(h * (i % ns)) # block origin
535
+ im = im.transpose(1, 2, 0)
536
+ mosaic[y : y + h, x : x + w, :] = im
537
+
538
+ # Resize (optional)
539
+ scale = max_size / ns / max(h, w)
540
+ if scale < 1:
541
+ h = math.ceil(scale * h)
542
+ w = math.ceil(scale * w)
543
+ mosaic = cv2.resize(mosaic, tuple(int(x * ns) for x in (w, h)))
544
+
545
+ # Annotate
546
+ fs = int((h + w) * ns * 0.01) # font size
547
+ annotator = Annotator(
548
+ mosaic,
549
+ line_width=round(fs / 10),
550
+ font_size=fs,
551
+ pil=True,
552
+ example=names,
553
+ )
554
+ for i in range(i + 1):
555
+ x, y = int(w * (i // ns)), int(h * (i % ns)) # block origin
556
+ annotator.rectangle(
557
+ [x, y, x + w, y + h], None, (255, 255, 255), width=2
558
+ ) # borders
559
+ if paths:
560
+ annotator.text(
561
+ (x + 5, y + 5),
562
+ text=Path(paths[i]).name[:40],
563
+ txt_color=(220, 220, 220),
564
+ ) # filenames
565
+ if len(cls) > 0:
566
+ idx = batch_idx == i
567
+ classes = cls[idx].astype("int")
568
+
569
+ if len(bboxes):
570
+ boxes = xywh2xyxy(bboxes[idx, :4]).T
571
+ labels = bboxes.shape[1] == 4 # labels if no conf column
572
+ conf = (
573
+ None if labels else bboxes[idx, 4]
574
+ ) # check for confidence presence (label vs pred)
575
+
576
+ if boxes.shape[1]:
577
+ if (
578
+ boxes.max() <= 1.01
579
+ ): # if normalized with tolerance 0.01
580
+ boxes[[0, 2]] *= w # scale to pixels
581
+ boxes[[1, 3]] *= h
582
+ elif (
583
+ scale < 1
584
+ ): # absolute coords need scale if image scales
585
+ boxes *= scale
586
+ boxes[[0, 2]] += x
587
+ boxes[[1, 3]] += y
588
+ for j, box in enumerate(boxes.T.tolist()):
589
+ c = classes[j]
590
+ color = colors(c)
591
+ c = names.get(c, c) if names else c
592
+ if labels or conf[j] > 0.25: # 0.25 conf thresh
593
+ label = f"{c}" if labels else f"{c} {conf[j]:.1f}"
594
+ annotator.box_label(box, label, color=color)
595
+ elif len(classes):
596
+ for c in classes:
597
+ color = colors(c)
598
+ c = names.get(c, c) if names else c
599
+ annotator.text(
600
+ (x, y), f"{c}", txt_color=color, box_style=True
601
+ )
602
+
603
+ # Plot keypoints
604
+ if len(kpts):
605
+ kpts_ = kpts[idx].copy()
606
+ if len(kpts_):
607
+ if (
608
+ kpts_[..., 0].max() <= 1.01
609
+ or kpts_[..., 1].max() <= 1.01
610
+ ): # if normalized with tolerance .01
611
+ kpts_[..., 0] *= w # scale to pixels
612
+ kpts_[..., 1] *= h
613
+ elif (
614
+ scale < 1
615
+ ): # absolute coords need scale if image scales
616
+ kpts_ *= scale
617
+ kpts_[..., 0] += x
618
+ kpts_[..., 1] += y
619
+ for j in range(len(kpts_)):
620
+ if labels or conf[j] > 0.25: # 0.25 conf thresh
621
+ annotator.kpts(kpts_[j])
622
+
623
+ # Plot masks
624
+ if len(masks):
625
+ if idx.shape[0] == masks.shape[0]: # overlap_masks=False
626
+ image_masks = masks[idx]
627
+ else: # overlap_masks=True
628
+ image_masks = masks[[i]] # (1, 640, 640)
629
+ nl = idx.sum()
630
+ index = np.arange(nl).reshape((nl, 1, 1)) + 1
631
+ image_masks = np.repeat(image_masks, nl, axis=0)
632
+ image_masks = np.where(image_masks == index, 1.0, 0.0)
633
+
634
+ im = np.asarray(annotator.im).copy()
635
+ for j, box in enumerate(boxes.T.tolist()):
636
+ if labels or conf[j] > 0.25: # 0.25 conf thresh
637
+ color = colors(classes[j])
638
+ mh, mw = image_masks[j].shape
639
+ if mh != h or mw != w:
640
+ mask = image_masks[j].astype(np.uint8)
641
+ mask = cv2.resize(mask, (w, h))
642
+ mask = mask.astype(bool)
643
+ else:
644
+ mask = image_masks[j].astype(bool)
645
+ with contextlib.suppress(Exception):
646
+ im[y : y + h, x : x + w, :][mask] = (
647
+ im[y : y + h, x : x + w, :][mask] * 0.4
648
+ + np.array(color) * 0.6
649
+ )
650
+ annotator.fromarray(im)
651
+ annotator.im.save(fname) # save
652
+ if on_plot:
653
+ on_plot(fname)
654
+
655
+
656
+ @plt_settings()
657
+ def plot_results(
658
+ file="path/to/results.csv",
659
+ dir="",
660
+ segment=False,
661
+ pose=False,
662
+ classify=False,
663
+ on_plot=None,
664
+ ):
665
+ """Plot training results.csv. Usage: from utils.plots import *; plot_results('path/to/results.csv')."""
666
+ import pandas as pd
667
+
668
+ save_dir = Path(file).parent if file else Path(dir)
669
+ if classify:
670
+ fig, ax = plt.subplots(2, 2, figsize=(6, 6), tight_layout=True)
671
+ index = [1, 4, 2, 3]
672
+ elif segment:
673
+ fig, ax = plt.subplots(2, 8, figsize=(18, 6), tight_layout=True)
674
+ index = [1, 2, 3, 4, 5, 6, 9, 10, 13, 14, 15, 16, 7, 8, 11, 12]
675
+ elif pose:
676
+ fig, ax = plt.subplots(2, 9, figsize=(21, 6), tight_layout=True)
677
+ index = [1, 2, 3, 4, 5, 6, 7, 10, 11, 14, 15, 16, 17, 18, 8, 9, 12, 13]
678
+ else:
679
+ fig, ax = plt.subplots(2, 5, figsize=(12, 6), tight_layout=True)
680
+ index = [1, 2, 3, 4, 5, 8, 9, 10, 6, 7]
681
+ ax = ax.ravel()
682
+ files = list(save_dir.glob("results*.csv"))
683
+ assert len(
684
+ files
685
+ ), f"No results.csv files found in {save_dir.resolve()}, nothing to plot."
686
+ for f in files:
687
+ try:
688
+ data = pd.read_csv(f)
689
+ s = [x.strip() for x in data.columns]
690
+ x = data.values[:, 0]
691
+ for i, j in enumerate(index):
692
+ y = data.values[:, j].astype("float")
693
+ # y[y == 0] = np.nan # don't show zero values
694
+ ax[i].plot(
695
+ x, y, marker=".", label=f.stem, linewidth=2, markersize=8
696
+ ) # actual results
697
+ ax[i].plot(
698
+ x,
699
+ gaussian_filter1d(y, sigma=3),
700
+ ":",
701
+ label="smooth",
702
+ linewidth=2,
703
+ ) # smoothing line
704
+ ax[i].set_title(s[j], fontsize=12)
705
+ # if j in [8, 9, 10]: # share train and val loss y axes
706
+ # ax[i].get_shared_y_axes().join(ax[i], ax[i - 5])
707
+ except Exception as e:
708
+ LOGGER.warning(f"WARNING: Plotting error for {f}: {e}")
709
+ ax[1].legend()
710
+ fname = save_dir / "results.png"
711
+ fig.savefig(fname, dpi=200)
712
+ plt.close()
713
+ if on_plot:
714
+ on_plot(fname)
715
+
716
+
717
+ def output_to_target(output, max_det=300):
718
+ """Convert model output to target format [batch_id, class_id, x, y, w, h, conf] for plotting."""
719
+ targets = []
720
+ for i, o in enumerate(output):
721
+ box, conf, cls = o[:max_det, :6].cpu().split((4, 1, 1), 1)
722
+ j = torch.full((conf.shape[0], 1), i)
723
+ targets.append(torch.cat((j, cls, xyxy2xywh(box), conf), 1))
724
+ targets = torch.cat(targets, 0).numpy()
725
+ return targets[:, 0], targets[:, 1], targets[:, 2:]
726
+
727
+
728
+ def feature_visualization(
729
+ x, module_type, stage, n=32, save_dir=Path("runs/detect/exp")
730
+ ):
731
+ """
732
+ Visualize feature maps of a given model module during inference.
733
+
734
+ Args:
735
+ x (torch.Tensor): Features to be visualized.
736
+ module_type (str): Module type.
737
+ stage (int): Module stage within the model.
738
+ n (int, optional): Maximum number of feature maps to plot. Defaults to 32.
739
+ save_dir (Path, optional): Directory to save results. Defaults to Path('runs/detect/exp').
740
+ """
741
+ for m in ["Detect", "Pose", "Segment"]:
742
+ if m in module_type:
743
+ return
744
+ batch, channels, height, width = x.shape # batch, channels, height, width
745
+ if height > 1 and width > 1:
746
+ f = (
747
+ save_dir
748
+ / f"stage{stage}_{module_type.split('.')[-1]}_features.png"
749
+ ) # filename
750
+
751
+ blocks = torch.chunk(
752
+ x[0].cpu(), channels, dim=0
753
+ ) # select batch index 0, block by channels
754
+ n = min(n, channels) # number of plots
755
+ fig, ax = plt.subplots(
756
+ math.ceil(n / 8), 8, tight_layout=True
757
+ ) # 8 rows x n/8 cols
758
+ ax = ax.ravel()
759
+ plt.subplots_adjust(wspace=0.05, hspace=0.05)
760
+ for i in range(n):
761
+ ax[i].imshow(blocks[i].squeeze()) # cmap='gray'
762
+ ax[i].axis("off")
763
+
764
+ LOGGER.info(f"Saving {f}... ({n}/{channels})")
765
+ plt.savefig(f, dpi=300, bbox_inches="tight")
766
+ plt.close()
767
+ np.save(str(f.with_suffix(".npy")), x[0].cpu().numpy()) # npy save