μ½˜ν…μΈ λ‘œ κ±΄λ„ˆλ›°κΈ°

μ°Έμ‘° ultralytics/utils/plotting.py

μ°Έκ³ 

이 νŒŒμΌμ€ https://github.com/ultralytics/ ultralytics/blob/main/ ultralytics/utils/plotting .pyμ—μ„œ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€. 문제λ₯Ό λ°œκ²¬ν•˜λ©΄ ν’€ λ¦¬ν€˜μŠ€νŠΈ (πŸ› οΈ)λ₯Ό μ œμΆœν•˜μ—¬ 문제λ₯Ό ν•΄κ²°ν•˜λ„λ‘ λ„μ™€μ£Όμ„Έμš”. κ°μ‚¬ν•©λ‹ˆλ‹€ πŸ™!



ultralytics.utils.plotting.Colors

Ultralytics κΈ°λ³Έ 색상 νŒ”λ ˆνŠΈ https://ultralytics.com/.

이 ν΄λž˜μŠ€λŠ” 16μ§„μˆ˜ 색상 μ½”λ“œλ₯Ό RGB κ°’μœΌλ‘œ λ³€ν™˜ν•˜λŠ” 것을 ν¬ν•¨ν•˜μ—¬ Ultralytics 색상 νŒ”λ ˆνŠΈλ‘œ μž‘μ—…ν•˜λŠ” λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€. RGB κ°’μœΌλ‘œ λ³€ν™˜ν•˜λŠ” 것을 ν¬ν•¨ν•©λ‹ˆλ‹€.

속성:

이름 μœ ν˜• μ„€λͺ…
palette list of tuple

RGB 색상 κ°’ λͺ©λ‘μž…λ‹ˆλ‹€.

n int

νŒ”λ ˆνŠΈμ— μžˆλŠ” μƒ‰μ˜ κ°œμˆ˜μž…λ‹ˆλ‹€.

pose_palette ndarray

νŠΉμ • 색상 νŒ”λ ˆνŠΈ λ°°μ—΄(dtype np.uint8)μž…λ‹ˆλ‹€.

의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
class Colors:
    """
    Ultralytics default color palette https://ultralytics.com/.

    This class provides methods to work with the Ultralytics color palette, including converting hex color codes to
    RGB values.

    Attributes:
        palette (list of tuple): List of RGB color values.
        n (int): The number of colors in the palette.
        pose_palette (np.ndarray): A specific color palette array with dtype np.uint8.
    """

    def __init__(self):
        """Initialize colors as hex = matplotlib.colors.TABLEAU_COLORS.values()."""
        hexs = (
            "FF3838",
            "FF9D97",
            "FF701F",
            "FFB21D",
            "CFD231",
            "48F90A",
            "92CC17",
            "3DDB86",
            "1A9334",
            "00D4BB",
            "2C99A8",
            "00C2FF",
            "344593",
            "6473FF",
            "0018EC",
            "8438FF",
            "520085",
            "CB38FF",
            "FF95C8",
            "FF37C7",
        )
        self.palette = [self.hex2rgb(f"#{c}") for c in hexs]
        self.n = len(self.palette)
        self.pose_palette = np.array(
            [
                [255, 128, 0],
                [255, 153, 51],
                [255, 178, 102],
                [230, 230, 0],
                [255, 153, 255],
                [153, 204, 255],
                [255, 102, 255],
                [255, 51, 255],
                [102, 178, 255],
                [51, 153, 255],
                [255, 153, 153],
                [255, 102, 102],
                [255, 51, 51],
                [153, 255, 153],
                [102, 255, 102],
                [51, 255, 51],
                [0, 255, 0],
                [0, 0, 255],
                [255, 0, 0],
                [255, 255, 255],
            ],
            dtype=np.uint8,
        )

    def __call__(self, i, bgr=False):
        """Converts hex color codes to RGB values."""
        c = self.palette[int(i) % self.n]
        return (c[2], c[1], c[0]) if bgr else c

    @staticmethod
    def hex2rgb(h):
        """Converts hex color codes to RGB values (i.e. default PIL order)."""
        return tuple(int(h[1 + i : 1 + i + 2], 16) for i in (0, 2, 4))

__call__(i, bgr=False)

16μ§„μˆ˜ 색상 μ½”λ“œλ₯Ό RGB κ°’μœΌλ‘œ λ³€ν™˜ν•©λ‹ˆλ‹€.

의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def __call__(self, i, bgr=False):
    """Converts hex color codes to RGB values."""
    c = self.palette[int(i) % self.n]
    return (c[2], c[1], c[0]) if bgr else c

__init__()

색상을 ν—₯슀 = matplotlib.colors.TABLEAU_COLORS.values()둜 μ΄ˆκΈ°ν™”ν•©λ‹ˆλ‹€.

의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def __init__(self):
    """Initialize colors as hex = matplotlib.colors.TABLEAU_COLORS.values()."""
    hexs = (
        "FF3838",
        "FF9D97",
        "FF701F",
        "FFB21D",
        "CFD231",
        "48F90A",
        "92CC17",
        "3DDB86",
        "1A9334",
        "00D4BB",
        "2C99A8",
        "00C2FF",
        "344593",
        "6473FF",
        "0018EC",
        "8438FF",
        "520085",
        "CB38FF",
        "FF95C8",
        "FF37C7",
    )
    self.palette = [self.hex2rgb(f"#{c}") for c in hexs]
    self.n = len(self.palette)
    self.pose_palette = np.array(
        [
            [255, 128, 0],
            [255, 153, 51],
            [255, 178, 102],
            [230, 230, 0],
            [255, 153, 255],
            [153, 204, 255],
            [255, 102, 255],
            [255, 51, 255],
            [102, 178, 255],
            [51, 153, 255],
            [255, 153, 153],
            [255, 102, 102],
            [255, 51, 51],
            [153, 255, 153],
            [102, 255, 102],
            [51, 255, 51],
            [0, 255, 0],
            [0, 0, 255],
            [255, 0, 0],
            [255, 255, 255],
        ],
        dtype=np.uint8,
    )

hex2rgb(h) staticmethod

16μ§„μˆ˜ 색상 μ½”λ“œλ₯Ό RGB κ°’μœΌλ‘œ λ³€ν™˜ν•©λ‹ˆλ‹€(예: κΈ°λ³Έ PIL μˆœμ„œ).

의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
@staticmethod
def hex2rgb(h):
    """Converts hex color codes to RGB values (i.e. default PIL order)."""
    return tuple(int(h[1 + i : 1 + i + 2], 16) for i in (0, 2, 4))



ultralytics.utils.plotting.Annotator

Ultralytics 트레인/λ°Έλ₯˜ λͺ¨μžμ΄ν¬μ™€ JPG 및 예츑 주석을 μœ„ν•œ μ–΄λ…Έν…Œμ΄ν„°μž…λ‹ˆλ‹€.

속성:

이름 μœ ν˜• μ„€λͺ…
im Image.Image or numpy array

주석을 달 μ΄λ―Έμ§€μž…λ‹ˆλ‹€.

pil bool

λ“œλ‘œμž‰ 주석에 PIL λ˜λŠ” cv2λ₯Ό μ‚¬μš©ν• μ§€ μ—¬λΆ€μž…λ‹ˆλ‹€.

font truetype or load_default

ν…μŠ€νŠΈ 주석에 μ‚¬μš©λ˜λŠ” κΈ€κΌ΄μž…λ‹ˆλ‹€.

lw float

그리기용 μ„  λ„ˆλΉ„μž…λ‹ˆλ‹€.

skeleton List[List[int]]

ν‚€ν¬μΈνŠΈμ˜ μŠ€μΌˆλ ˆν†€ κ΅¬μ‘°μž…λ‹ˆλ‹€.

limb_color List[int]

νŒ”λ‹€λ¦¬μš© 색상 νŒ”λ ˆνŠΈ.

kpt_color List[int]

ν‚€ν¬μΈνŠΈμš© 색상 νŒ”λ ˆνŠΈ.

의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
class Annotator:
    """
    Ultralytics Annotator for train/val mosaics and JPGs and predictions annotations.

    Attributes:
        im (Image.Image or numpy array): The image to annotate.
        pil (bool): Whether to use PIL or cv2 for drawing annotations.
        font (ImageFont.truetype or ImageFont.load_default): Font used for text annotations.
        lw (float): Line width for drawing.
        skeleton (List[List[int]]): Skeleton structure for keypoints.
        limb_color (List[int]): Color palette for limbs.
        kpt_color (List[int]): Color palette for keypoints.
    """

    def __init__(self, im, line_width=None, font_size=None, font="Arial.ttf", pil=False, example="abc"):
        """Initialize the Annotator class with image and line width along with color palette for keypoints and limbs."""
        non_ascii = not is_ascii(example)  # non-latin labels, i.e. asian, arabic, cyrillic
        input_is_pil = isinstance(im, Image.Image)
        self.pil = pil or non_ascii or input_is_pil
        self.lw = line_width or max(round(sum(im.size if input_is_pil else im.shape) / 2 * 0.003), 2)
        if self.pil:  # use PIL
            self.im = im if input_is_pil else Image.fromarray(im)
            self.draw = ImageDraw.Draw(self.im)
            try:
                font = check_font("Arial.Unicode.ttf" if non_ascii else font)
                size = font_size or max(round(sum(self.im.size) / 2 * 0.035), 12)
                self.font = ImageFont.truetype(str(font), size)
            except Exception:
                self.font = ImageFont.load_default()
            # Deprecation fix for w, h = getsize(string) -> _, _, w, h = getbox(string)
            if check_version(pil_version, "9.2.0"):
                self.font.getsize = lambda x: self.font.getbbox(x)[2:4]  # text width, height
        else:  # use cv2
            assert im.data.contiguous, "Image not contiguous. Apply np.ascontiguousarray(im) to Annotator input images."
            self.im = im if im.flags.writeable else im.copy()
            self.tf = max(self.lw - 1, 1)  # font thickness
            self.sf = self.lw / 3  # font scale
        # Pose
        self.skeleton = [
            [16, 14],
            [14, 12],
            [17, 15],
            [15, 13],
            [12, 13],
            [6, 12],
            [7, 13],
            [6, 7],
            [6, 8],
            [7, 9],
            [8, 10],
            [9, 11],
            [2, 3],
            [1, 2],
            [1, 3],
            [2, 4],
            [3, 5],
            [4, 6],
            [5, 7],
        ]

        self.limb_color = colors.pose_palette[[9, 9, 9, 9, 7, 7, 7, 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 16, 16]]
        self.kpt_color = colors.pose_palette[[16, 16, 16, 16, 16, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9]]

    def box_label(self, box, label="", color=(128, 128, 128), txt_color=(255, 255, 255), rotated=False):
        """Add one xyxy box to image with label."""
        if isinstance(box, torch.Tensor):
            box = box.tolist()
        if self.pil or not is_ascii(label):
            if rotated:
                p1 = box[0]
                # NOTE: PIL-version polygon needs tuple type.
                self.draw.polygon([tuple(b) for b in box], width=self.lw, outline=color)
            else:
                p1 = (box[0], box[1])
                self.draw.rectangle(box, width=self.lw, outline=color)  # box
            if label:
                w, h = self.font.getsize(label)  # text width, height
                outside = p1[1] - h >= 0  # label fits outside box
                self.draw.rectangle(
                    (p1[0], p1[1] - h if outside else p1[1], p1[0] + w + 1, p1[1] + 1 if outside else p1[1] + h + 1),
                    fill=color,
                )
                # self.draw.text((box[0], box[1]), label, fill=txt_color, font=self.font, anchor='ls')  # for PIL>8.0
                self.draw.text((p1[0], p1[1] - h if outside else p1[1]), label, fill=txt_color, font=self.font)
        else:  # cv2
            if rotated:
                p1 = [int(b) for b in box[0]]
                # NOTE: cv2-version polylines needs np.asarray type.
                cv2.polylines(self.im, [np.asarray(box, dtype=int)], True, color, self.lw)
            else:
                p1, p2 = (int(box[0]), int(box[1])), (int(box[2]), int(box[3]))
                cv2.rectangle(self.im, p1, p2, color, thickness=self.lw, lineType=cv2.LINE_AA)
            if label:
                w, h = cv2.getTextSize(label, 0, fontScale=self.sf, thickness=self.tf)[0]  # text width, height
                outside = p1[1] - h >= 3
                p2 = p1[0] + w, p1[1] - h - 3 if outside else p1[1] + h + 3
                cv2.rectangle(self.im, p1, p2, color, -1, cv2.LINE_AA)  # filled
                cv2.putText(
                    self.im,
                    label,
                    (p1[0], p1[1] - 2 if outside else p1[1] + h + 2),
                    0,
                    self.sf,
                    txt_color,
                    thickness=self.tf,
                    lineType=cv2.LINE_AA,
                )

    def masks(self, masks, colors, im_gpu, alpha=0.5, retina_masks=False):
        """
        Plot masks on image.

        Args:
            masks (tensor): Predicted masks on cuda, shape: [n, h, w]
            colors (List[List[Int]]): Colors for predicted masks, [[r, g, b] * n]
            im_gpu (tensor): Image is in cuda, shape: [3, h, w], range: [0, 1]
            alpha (float): Mask transparency: 0.0 fully transparent, 1.0 opaque
            retina_masks (bool): Whether to use high resolution masks or not. Defaults to False.
        """
        if self.pil:
            # Convert to numpy first
            self.im = np.asarray(self.im).copy()
        if len(masks) == 0:
            self.im[:] = im_gpu.permute(1, 2, 0).contiguous().cpu().numpy() * 255
        if im_gpu.device != masks.device:
            im_gpu = im_gpu.to(masks.device)
        colors = torch.tensor(colors, device=masks.device, dtype=torch.float32) / 255.0  # shape(n,3)
        colors = colors[:, None, None]  # shape(n,1,1,3)
        masks = masks.unsqueeze(3)  # shape(n,h,w,1)
        masks_color = masks * (colors * alpha)  # shape(n,h,w,3)

        inv_alpha_masks = (1 - masks * alpha).cumprod(0)  # shape(n,h,w,1)
        mcs = masks_color.max(dim=0).values  # shape(n,h,w,3)

        im_gpu = im_gpu.flip(dims=[0])  # flip channel
        im_gpu = im_gpu.permute(1, 2, 0).contiguous()  # shape(h,w,3)
        im_gpu = im_gpu * inv_alpha_masks[-1] + mcs
        im_mask = im_gpu * 255
        im_mask_np = im_mask.byte().cpu().numpy()
        self.im[:] = im_mask_np if retina_masks else ops.scale_image(im_mask_np, self.im.shape)
        if self.pil:
            # Convert im back to PIL and update draw
            self.fromarray(self.im)

    def kpts(self, kpts, shape=(640, 640), radius=5, kpt_line=True):
        """
        Plot keypoints on the image.

        Args:
            kpts (tensor): Predicted keypoints with shape [17, 3]. Each keypoint has (x, y, confidence).
            shape (tuple): Image shape as a tuple (h, w), where h is the height and w is the width.
            radius (int, optional): Radius of the drawn keypoints. Default is 5.
            kpt_line (bool, optional): If True, the function will draw lines connecting keypoints
                                       for human pose. Default is True.

        Note:
            `kpt_line=True` currently only supports human pose plotting.
        """
        if self.pil:
            # Convert to numpy first
            self.im = np.asarray(self.im).copy()
        nkpt, ndim = kpts.shape
        is_pose = nkpt == 17 and ndim in {2, 3}
        kpt_line &= is_pose  # `kpt_line=True` for now only supports human pose plotting
        for i, k in enumerate(kpts):
            color_k = [int(x) for x in self.kpt_color[i]] if is_pose else colors(i)
            x_coord, y_coord = k[0], k[1]
            if x_coord % shape[1] != 0 and y_coord % shape[0] != 0:
                if len(k) == 3:
                    conf = k[2]
                    if conf < 0.5:
                        continue
                cv2.circle(self.im, (int(x_coord), int(y_coord)), radius, color_k, -1, lineType=cv2.LINE_AA)

        if kpt_line:
            ndim = kpts.shape[-1]
            for i, sk in enumerate(self.skeleton):
                pos1 = (int(kpts[(sk[0] - 1), 0]), int(kpts[(sk[0] - 1), 1]))
                pos2 = (int(kpts[(sk[1] - 1), 0]), int(kpts[(sk[1] - 1), 1]))
                if ndim == 3:
                    conf1 = kpts[(sk[0] - 1), 2]
                    conf2 = kpts[(sk[1] - 1), 2]
                    if conf1 < 0.5 or conf2 < 0.5:
                        continue
                if pos1[0] % shape[1] == 0 or pos1[1] % shape[0] == 0 or pos1[0] < 0 or pos1[1] < 0:
                    continue
                if pos2[0] % shape[1] == 0 or pos2[1] % shape[0] == 0 or pos2[0] < 0 or pos2[1] < 0:
                    continue
                cv2.line(self.im, pos1, pos2, [int(x) for x in self.limb_color[i]], thickness=2, lineType=cv2.LINE_AA)
        if self.pil:
            # Convert im back to PIL and update draw
            self.fromarray(self.im)

    def rectangle(self, xy, fill=None, outline=None, width=1):
        """Add rectangle to image (PIL-only)."""
        self.draw.rectangle(xy, fill, outline, width)

    def text(self, xy, text, txt_color=(255, 255, 255), anchor="top", box_style=False):
        """Adds text to an image using PIL or cv2."""
        if anchor == "bottom":  # start y from font bottom
            w, h = self.font.getsize(text)  # text width, height
            xy[1] += 1 - h
        if self.pil:
            if box_style:
                w, h = self.font.getsize(text)
                self.draw.rectangle((xy[0], xy[1], xy[0] + w + 1, xy[1] + h + 1), fill=txt_color)
                # Using `txt_color` for background and draw fg with white color
                txt_color = (255, 255, 255)
            if "\n" in text:
                lines = text.split("\n")
                _, h = self.font.getsize(text)
                for line in lines:
                    self.draw.text(xy, line, fill=txt_color, font=self.font)
                    xy[1] += h
            else:
                self.draw.text(xy, text, fill=txt_color, font=self.font)
        else:
            if box_style:
                w, h = cv2.getTextSize(text, 0, fontScale=self.sf, thickness=self.tf)[0]  # text width, height
                outside = xy[1] - h >= 3
                p2 = xy[0] + w, xy[1] - h - 3 if outside else xy[1] + h + 3
                cv2.rectangle(self.im, xy, p2, txt_color, -1, cv2.LINE_AA)  # filled
                # Using `txt_color` for background and draw fg with white color
                txt_color = (255, 255, 255)
            cv2.putText(self.im, text, xy, 0, self.sf, txt_color, thickness=self.tf, lineType=cv2.LINE_AA)

    def fromarray(self, im):
        """Update self.im from a numpy array."""
        self.im = im if isinstance(im, Image.Image) else Image.fromarray(im)
        self.draw = ImageDraw.Draw(self.im)

    def result(self):
        """Return annotated image as array."""
        return np.asarray(self.im)

    def show(self, title=None):
        """Show the annotated image."""
        Image.fromarray(np.asarray(self.im)[..., ::-1]).show(title)

    def save(self, filename="image.jpg"):
        """Save the annotated image to 'filename'."""
        cv2.imwrite(filename, np.asarray(self.im))

    def draw_region(self, reg_pts=None, color=(0, 255, 0), thickness=5):
        """
        Draw region line.

        Args:
            reg_pts (list): Region Points (for line 2 points, for region 4 points)
            color (tuple): Region Color value
            thickness (int): Region area thickness value
        """
        cv2.polylines(self.im, [np.array(reg_pts, dtype=np.int32)], isClosed=True, color=color, thickness=thickness)

    def draw_centroid_and_tracks(self, track, color=(255, 0, 255), track_thickness=2):
        """
        Draw centroid point and track trails.

        Args:
            track (list): object tracking points for trails display
            color (tuple): tracks line color
            track_thickness (int): track line thickness value
        """
        points = np.hstack(track).astype(np.int32).reshape((-1, 1, 2))
        cv2.polylines(self.im, [points], isClosed=False, color=color, thickness=track_thickness)
        cv2.circle(self.im, (int(track[-1][0]), int(track[-1][1])), track_thickness * 2, color, -1)

    def count_labels(self, counts=0, count_txt_size=2, color=(255, 255, 255), txt_color=(0, 0, 0)):
        """
        Plot counts for object counter.

        Args:
            counts (int): objects counts value
            count_txt_size (int): text size for counts display
            color (tuple): background color of counts display
            txt_color (tuple): text color of counts display
        """
        self.tf = count_txt_size
        tl = self.tf or round(0.002 * (self.im.shape[0] + self.im.shape[1]) / 2) + 1
        tf = max(tl - 1, 1)

        # Get text size for in_count and out_count
        t_size_in = cv2.getTextSize(str(counts), 0, fontScale=tl / 2, thickness=tf)[0]

        # Calculate positions for counts label
        text_width = t_size_in[0]
        text_x = (self.im.shape[1] - text_width) // 2  # Center x-coordinate
        text_y = t_size_in[1]

        # Create a rounded rectangle for in_count
        cv2.rectangle(
            self.im, (text_x - 5, text_y - 5), (text_x + text_width + 7, text_y + t_size_in[1] + 7), color, -1
        )
        cv2.putText(
            self.im, str(counts), (text_x, text_y + t_size_in[1]), 0, tl / 2, txt_color, self.tf, lineType=cv2.LINE_AA
        )

    @staticmethod
    def estimate_pose_angle(a, b, c):
        """
        Calculate the pose angle for object.

        Args:
            a (float) : The value of pose point a
            b (float): The value of pose point b
            c (float): The value o pose point c

        Returns:
            angle (degree): Degree value of angle between three points
        """
        a, b, c = np.array(a), np.array(b), np.array(c)
        radians = np.arctan2(c[1] - b[1], c[0] - b[0]) - np.arctan2(a[1] - b[1], a[0] - b[0])
        angle = np.abs(radians * 180.0 / np.pi)
        if angle > 180.0:
            angle = 360 - angle
        return angle

    def draw_specific_points(self, keypoints, indices=[2, 5, 7], shape=(640, 640), radius=2):
        """
        Draw specific keypoints for gym steps counting.

        Args:
            keypoints (list): list of keypoints data to be plotted
            indices (list): keypoints ids list to be plotted
            shape (tuple): imgsz for model inference
            radius (int): Keypoint radius value
        """
        for i, k in enumerate(keypoints):
            if i in indices:
                x_coord, y_coord = k[0], k[1]
                if x_coord % shape[1] != 0 and y_coord % shape[0] != 0:
                    if len(k) == 3:
                        conf = k[2]
                        if conf < 0.5:
                            continue
                    cv2.circle(self.im, (int(x_coord), int(y_coord)), radius, (0, 255, 0), -1, lineType=cv2.LINE_AA)
        return self.im

    def plot_angle_and_count_and_stage(self, angle_text, count_text, stage_text, center_kpt, line_thickness=2):
        """
        Plot the pose angle, count value and step stage.

        Args:
            angle_text (str): angle value for workout monitoring
            count_text (str): counts value for workout monitoring
            stage_text (str): stage decision for workout monitoring
            center_kpt (int): centroid pose index for workout monitoring
            line_thickness (int): thickness for text display
        """
        angle_text, count_text, stage_text = (f" {angle_text:.2f}", f"Steps : {count_text}", f" {stage_text}")
        font_scale = 0.6 + (line_thickness / 10.0)

        # Draw angle
        (angle_text_width, angle_text_height), _ = cv2.getTextSize(angle_text, 0, font_scale, line_thickness)
        angle_text_position = (int(center_kpt[0]), int(center_kpt[1]))
        angle_background_position = (angle_text_position[0], angle_text_position[1] - angle_text_height - 5)
        angle_background_size = (angle_text_width + 2 * 5, angle_text_height + 2 * 5 + (line_thickness * 2))
        cv2.rectangle(
            self.im,
            angle_background_position,
            (
                angle_background_position[0] + angle_background_size[0],
                angle_background_position[1] + angle_background_size[1],
            ),
            (255, 255, 255),
            -1,
        )
        cv2.putText(self.im, angle_text, angle_text_position, 0, font_scale, (0, 0, 0), line_thickness)

        # Draw Counts
        (count_text_width, count_text_height), _ = cv2.getTextSize(count_text, 0, font_scale, line_thickness)
        count_text_position = (angle_text_position[0], angle_text_position[1] + angle_text_height + 20)
        count_background_position = (
            angle_background_position[0],
            angle_background_position[1] + angle_background_size[1] + 5,
        )
        count_background_size = (count_text_width + 10, count_text_height + 10 + (line_thickness * 2))

        cv2.rectangle(
            self.im,
            count_background_position,
            (
                count_background_position[0] + count_background_size[0],
                count_background_position[1] + count_background_size[1],
            ),
            (255, 255, 255),
            -1,
        )
        cv2.putText(self.im, count_text, count_text_position, 0, font_scale, (0, 0, 0), line_thickness)

        # Draw Stage
        (stage_text_width, stage_text_height), _ = cv2.getTextSize(stage_text, 0, font_scale, line_thickness)
        stage_text_position = (int(center_kpt[0]), int(center_kpt[1]) + angle_text_height + count_text_height + 40)
        stage_background_position = (stage_text_position[0], stage_text_position[1] - stage_text_height - 5)
        stage_background_size = (stage_text_width + 10, stage_text_height + 10)

        cv2.rectangle(
            self.im,
            stage_background_position,
            (
                stage_background_position[0] + stage_background_size[0],
                stage_background_position[1] + stage_background_size[1],
            ),
            (255, 255, 255),
            -1,
        )
        cv2.putText(self.im, stage_text, stage_text_position, 0, font_scale, (0, 0, 0), line_thickness)

    def seg_bbox(self, mask, mask_color=(255, 0, 255), det_label=None, track_label=None):
        """
        Function for drawing segmented object in bounding box shape.

        Args:
            mask (list): masks data list for instance segmentation area plotting
            mask_color (tuple): mask foreground color
            det_label (str): Detection label text
            track_label (str): Tracking label text
        """
        cv2.polylines(self.im, [np.int32([mask])], isClosed=True, color=mask_color, thickness=2)

        label = f"Track ID: {track_label}" if track_label else det_label
        text_size, _ = cv2.getTextSize(label, 0, 0.7, 1)

        cv2.rectangle(
            self.im,
            (int(mask[0][0]) - text_size[0] // 2 - 10, int(mask[0][1]) - text_size[1] - 10),
            (int(mask[0][0]) + text_size[0] // 2 + 5, int(mask[0][1] + 5)),
            mask_color,
            -1,
        )

        cv2.putText(
            self.im, label, (int(mask[0][0]) - text_size[0] // 2, int(mask[0][1]) - 5), 0, 0.7, (255, 255, 255), 2
        )

    def plot_distance_and_line(self, distance_m, distance_mm, centroids, line_color, centroid_color):
        """
        Plot the distance and line on frame.

        Args:
            distance_m (float): Distance between two bbox centroids in meters.
            distance_mm (float): Distance between two bbox centroids in millimeters.
            centroids (list): Bounding box centroids data.
            line_color (RGB): Distance line color.
            centroid_color (RGB): Bounding box centroid color.
        """
        (text_width_m, text_height_m), _ = cv2.getTextSize(
            f"Distance M: {distance_m:.2f}m", cv2.FONT_HERSHEY_SIMPLEX, 0.8, 2
        )
        cv2.rectangle(self.im, (15, 25), (15 + text_width_m + 10, 25 + text_height_m + 20), (255, 255, 255), -1)
        cv2.putText(
            self.im,
            f"Distance M: {distance_m:.2f}m",
            (20, 50),
            cv2.FONT_HERSHEY_SIMPLEX,
            0.8,
            (0, 0, 0),
            2,
            cv2.LINE_AA,
        )

        (text_width_mm, text_height_mm), _ = cv2.getTextSize(
            f"Distance MM: {distance_mm:.2f}mm", cv2.FONT_HERSHEY_SIMPLEX, 0.8, 2
        )
        cv2.rectangle(self.im, (15, 75), (15 + text_width_mm + 10, 75 + text_height_mm + 20), (255, 255, 255), -1)
        cv2.putText(
            self.im,
            f"Distance MM: {distance_mm:.2f}mm",
            (20, 100),
            cv2.FONT_HERSHEY_SIMPLEX,
            0.8,
            (0, 0, 0),
            2,
            cv2.LINE_AA,
        )

        cv2.line(self.im, centroids[0], centroids[1], line_color, 3)
        cv2.circle(self.im, centroids[0], 6, centroid_color, -1)
        cv2.circle(self.im, centroids[1], 6, centroid_color, -1)

    def visioneye(self, box, center_point, color=(235, 219, 11), pin_color=(255, 0, 255), thickness=2, pins_radius=10):
        """
        Function for pinpoint human-vision eye mapping and plotting.

        Args:
            box (list): Bounding box coordinates
            center_point (tuple): center point for vision eye view
            color (tuple): object centroid and line color value
            pin_color (tuple): visioneye point color value
            thickness (int): int value for line thickness
            pins_radius (int): visioneye point radius value
        """
        center_bbox = int((box[0] + box[2]) / 2), int((box[1] + box[3]) / 2)
        cv2.circle(self.im, center_point, pins_radius, pin_color, -1)
        cv2.circle(self.im, center_bbox, pins_radius, color, -1)
        cv2.line(self.im, center_point, center_bbox, color, thickness)

__init__(im, line_width=None, font_size=None, font='Arial.ttf', pil=False, example='abc')

ν‚€ν¬μΈνŠΈμ™€ νŒ”λ‹€λ¦¬μ— λŒ€ν•œ 색상 νŒ”λ ˆνŠΈμ™€ ν•¨κ»˜ 이미지 및 μ„  λ„ˆλΉ„λ₯Ό μ‚¬μš©ν•˜μ—¬ μ£Όμ„μž 클래슀λ₯Ό μ΄ˆκΈ°ν™”ν•©λ‹ˆλ‹€.

의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def __init__(self, im, line_width=None, font_size=None, font="Arial.ttf", pil=False, example="abc"):
    """Initialize the Annotator class with image and line width along with color palette for keypoints and limbs."""
    non_ascii = not is_ascii(example)  # non-latin labels, i.e. asian, arabic, cyrillic
    input_is_pil = isinstance(im, Image.Image)
    self.pil = pil or non_ascii or input_is_pil
    self.lw = line_width or max(round(sum(im.size if input_is_pil else im.shape) / 2 * 0.003), 2)
    if self.pil:  # use PIL
        self.im = im if input_is_pil else Image.fromarray(im)
        self.draw = ImageDraw.Draw(self.im)
        try:
            font = check_font("Arial.Unicode.ttf" if non_ascii else font)
            size = font_size or max(round(sum(self.im.size) / 2 * 0.035), 12)
            self.font = ImageFont.truetype(str(font), size)
        except Exception:
            self.font = ImageFont.load_default()
        # Deprecation fix for w, h = getsize(string) -> _, _, w, h = getbox(string)
        if check_version(pil_version, "9.2.0"):
            self.font.getsize = lambda x: self.font.getbbox(x)[2:4]  # text width, height
    else:  # use cv2
        assert im.data.contiguous, "Image not contiguous. Apply np.ascontiguousarray(im) to Annotator input images."
        self.im = im if im.flags.writeable else im.copy()
        self.tf = max(self.lw - 1, 1)  # font thickness
        self.sf = self.lw / 3  # font scale
    # Pose
    self.skeleton = [
        [16, 14],
        [14, 12],
        [17, 15],
        [15, 13],
        [12, 13],
        [6, 12],
        [7, 13],
        [6, 7],
        [6, 8],
        [7, 9],
        [8, 10],
        [9, 11],
        [2, 3],
        [1, 2],
        [1, 3],
        [2, 4],
        [3, 5],
        [4, 6],
        [5, 7],
    ]

    self.limb_color = colors.pose_palette[[9, 9, 9, 9, 7, 7, 7, 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 16, 16]]
    self.kpt_color = colors.pose_palette[[16, 16, 16, 16, 16, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9]]

box_label(box, label='', color=(128, 128, 128), txt_color=(255, 255, 255), rotated=False)

λ ˆμ΄λΈ”μ΄ μžˆλŠ” 이미지에 xyxy μƒμž ν•˜λ‚˜λ₯Ό μΆ”κ°€ν•©λ‹ˆλ‹€.

의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def box_label(self, box, label="", color=(128, 128, 128), txt_color=(255, 255, 255), rotated=False):
    """Add one xyxy box to image with label."""
    if isinstance(box, torch.Tensor):
        box = box.tolist()
    if self.pil or not is_ascii(label):
        if rotated:
            p1 = box[0]
            # NOTE: PIL-version polygon needs tuple type.
            self.draw.polygon([tuple(b) for b in box], width=self.lw, outline=color)
        else:
            p1 = (box[0], box[1])
            self.draw.rectangle(box, width=self.lw, outline=color)  # box
        if label:
            w, h = self.font.getsize(label)  # text width, height
            outside = p1[1] - h >= 0  # label fits outside box
            self.draw.rectangle(
                (p1[0], p1[1] - h if outside else p1[1], p1[0] + w + 1, p1[1] + 1 if outside else p1[1] + h + 1),
                fill=color,
            )
            # self.draw.text((box[0], box[1]), label, fill=txt_color, font=self.font, anchor='ls')  # for PIL>8.0
            self.draw.text((p1[0], p1[1] - h if outside else p1[1]), label, fill=txt_color, font=self.font)
    else:  # cv2
        if rotated:
            p1 = [int(b) for b in box[0]]
            # NOTE: cv2-version polylines needs np.asarray type.
            cv2.polylines(self.im, [np.asarray(box, dtype=int)], True, color, self.lw)
        else:
            p1, p2 = (int(box[0]), int(box[1])), (int(box[2]), int(box[3]))
            cv2.rectangle(self.im, p1, p2, color, thickness=self.lw, lineType=cv2.LINE_AA)
        if label:
            w, h = cv2.getTextSize(label, 0, fontScale=self.sf, thickness=self.tf)[0]  # text width, height
            outside = p1[1] - h >= 3
            p2 = p1[0] + w, p1[1] - h - 3 if outside else p1[1] + h + 3
            cv2.rectangle(self.im, p1, p2, color, -1, cv2.LINE_AA)  # filled
            cv2.putText(
                self.im,
                label,
                (p1[0], p1[1] - 2 if outside else p1[1] + h + 2),
                0,
                self.sf,
                txt_color,
                thickness=self.tf,
                lineType=cv2.LINE_AA,
            )

count_labels(counts=0, count_txt_size=2, color=(255, 255, 255), txt_color=(0, 0, 0))

개체 μΉ΄μš΄ν„°μ— λŒ€ν•œ ν”Œλ‘― κ°œμˆ˜μž…λ‹ˆλ‹€.

λ§€κ°œλ³€μˆ˜:

이름 μœ ν˜• μ„€λͺ… κΈ°λ³Έκ°’
counts int

객체 수 κ°’

0
count_txt_size int

개수 ν‘œμ‹œλ₯Ό μœ„ν•œ ν…μŠ€νŠΈ 크기

2
color tuple

카운트 ν‘œμ‹œμ˜ 배경색

(255, 255, 255)
txt_color tuple

카운트 ν‘œμ‹œμ˜ ν…μŠ€νŠΈ 색상

(0, 0, 0)
의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def count_labels(self, counts=0, count_txt_size=2, color=(255, 255, 255), txt_color=(0, 0, 0)):
    """
    Plot counts for object counter.

    Args:
        counts (int): objects counts value
        count_txt_size (int): text size for counts display
        color (tuple): background color of counts display
        txt_color (tuple): text color of counts display
    """
    self.tf = count_txt_size
    tl = self.tf or round(0.002 * (self.im.shape[0] + self.im.shape[1]) / 2) + 1
    tf = max(tl - 1, 1)

    # Get text size for in_count and out_count
    t_size_in = cv2.getTextSize(str(counts), 0, fontScale=tl / 2, thickness=tf)[0]

    # Calculate positions for counts label
    text_width = t_size_in[0]
    text_x = (self.im.shape[1] - text_width) // 2  # Center x-coordinate
    text_y = t_size_in[1]

    # Create a rounded rectangle for in_count
    cv2.rectangle(
        self.im, (text_x - 5, text_y - 5), (text_x + text_width + 7, text_y + t_size_in[1] + 7), color, -1
    )
    cv2.putText(
        self.im, str(counts), (text_x, text_y + t_size_in[1]), 0, tl / 2, txt_color, self.tf, lineType=cv2.LINE_AA
    )

draw_centroid_and_tracks(track, color=(255, 0, 255), track_thickness=2)

쀑심점을 그리고 ꢀ적을 μΆ”μ ν•©λ‹ˆλ‹€.

λ§€κ°œλ³€μˆ˜:

이름 μœ ν˜• μ„€λͺ… κΈ°λ³Έκ°’
track list

트레일 ν‘œμ‹œλ₯Ό μœ„ν•œ 개체 좔적 지점

ν•„μˆ˜
color tuple

μ„  색상 좔적

(255, 0, 255)
track_thickness int

νŠΈλž™ μ„  λ‘κ»˜ κ°’

2
의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def draw_centroid_and_tracks(self, track, color=(255, 0, 255), track_thickness=2):
    """
    Draw centroid point and track trails.

    Args:
        track (list): object tracking points for trails display
        color (tuple): tracks line color
        track_thickness (int): track line thickness value
    """
    points = np.hstack(track).astype(np.int32).reshape((-1, 1, 2))
    cv2.polylines(self.im, [points], isClosed=False, color=color, thickness=track_thickness)
    cv2.circle(self.im, (int(track[-1][0]), int(track[-1][1])), track_thickness * 2, color, -1)

draw_region(reg_pts=None, color=(0, 255, 0), thickness=5)

μ˜μ—­ 선을 κ·Έλ¦½λ‹ˆλ‹€.

λ§€κ°œλ³€μˆ˜:

이름 μœ ν˜• μ„€λͺ… κΈ°λ³Έκ°’
reg_pts list

지역 포인트(라인 2포인트, 지역 4포인트)

None
color tuple

지역 색상 κ°’

(0, 255, 0)
thickness int

μ˜μ—­ μ˜μ—­ λ‘κ»˜ κ°’

5
의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def draw_region(self, reg_pts=None, color=(0, 255, 0), thickness=5):
    """
    Draw region line.

    Args:
        reg_pts (list): Region Points (for line 2 points, for region 4 points)
        color (tuple): Region Color value
        thickness (int): Region area thickness value
    """
    cv2.polylines(self.im, [np.array(reg_pts, dtype=np.int32)], isClosed=True, color=color, thickness=thickness)

draw_specific_points(keypoints, indices=[2, 5, 7], shape=(640, 640), radius=2)

μ²΄μœ‘κ΄€ 걸음 수 계산을 μœ„ν•œ νŠΉμ • ν‚€ 포인트λ₯Ό κ·Έλ¦½λ‹ˆλ‹€.

λ§€κ°œλ³€μˆ˜:

이름 μœ ν˜• μ„€λͺ… κΈ°λ³Έκ°’
keypoints list

ν”Œλ‘œνŒ…ν•  ν‚€ν¬μΈνŠΈ 데이터 λͺ©λ‘

ν•„μˆ˜
indices list

ν”Œλ‘―ν•  ν‚€ν¬μΈνŠΈ ID λͺ©λ‘

[2, 5, 7]
shape tuple

λͺ¨λΈ 좔둠을 μœ„ν•œ IMGSZ

(640, 640)
radius int

ν‚€ν¬μΈνŠΈ 반경 κ°’

2
의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def draw_specific_points(self, keypoints, indices=[2, 5, 7], shape=(640, 640), radius=2):
    """
    Draw specific keypoints for gym steps counting.

    Args:
        keypoints (list): list of keypoints data to be plotted
        indices (list): keypoints ids list to be plotted
        shape (tuple): imgsz for model inference
        radius (int): Keypoint radius value
    """
    for i, k in enumerate(keypoints):
        if i in indices:
            x_coord, y_coord = k[0], k[1]
            if x_coord % shape[1] != 0 and y_coord % shape[0] != 0:
                if len(k) == 3:
                    conf = k[2]
                    if conf < 0.5:
                        continue
                cv2.circle(self.im, (int(x_coord), int(y_coord)), radius, (0, 255, 0), -1, lineType=cv2.LINE_AA)
    return self.im

estimate_pose_angle(a, b, c) staticmethod

객체의 포즈 각도λ₯Ό κ³„μ‚°ν•©λ‹ˆλ‹€.

λ§€κ°œλ³€μˆ˜:

이름 μœ ν˜• μ„€λͺ… κΈ°λ³Έκ°’
a float)

포즈 포인트 a의 κ°’

ν•„μˆ˜
b float

포즈 포인트 b의 κ°’

ν•„μˆ˜
c float

포즈 포인트 C κ°’

ν•„μˆ˜

λ°˜ν™˜ν•©λ‹ˆλ‹€:

이름 μœ ν˜• μ„€λͺ…
angle degree

μ„Έ 점 μ‚¬μ΄μ˜ 각도 κ°’

의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
@staticmethod
def estimate_pose_angle(a, b, c):
    """
    Calculate the pose angle for object.

    Args:
        a (float) : The value of pose point a
        b (float): The value of pose point b
        c (float): The value o pose point c

    Returns:
        angle (degree): Degree value of angle between three points
    """
    a, b, c = np.array(a), np.array(b), np.array(c)
    radians = np.arctan2(c[1] - b[1], c[0] - b[0]) - np.arctan2(a[1] - b[1], a[0] - b[0])
    angle = np.abs(radians * 180.0 / np.pi)
    if angle > 180.0:
        angle = 360 - angle
    return angle

fromarray(im)

널 λ°°μ—΄μ—μ„œ self.im을 μ—…λ°μ΄νŠΈν•©λ‹ˆλ‹€.

의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def fromarray(self, im):
    """Update self.im from a numpy array."""
    self.im = im if isinstance(im, Image.Image) else Image.fromarray(im)
    self.draw = ImageDraw.Draw(self.im)

kpts(kpts, shape=(640, 640), radius=5, kpt_line=True)

이미지에 ν‚€ν¬μΈνŠΈλ₯Ό ν”Œλ‘―ν•©λ‹ˆλ‹€.

λ§€κ°œλ³€μˆ˜:

이름 μœ ν˜• μ„€λͺ… κΈ°λ³Έκ°’
kpts tensor

λͺ¨μ–‘ [17, 3]의 예츑된 ν‚€ν¬μΈνŠΈ. 각 ν‚€ν¬μΈνŠΈμ—λŠ” (x, y, 신뒰도)κ°€ μžˆμŠ΅λ‹ˆλ‹€.

ν•„μˆ˜
shape tuple

이미지 λͺ¨μ–‘을 νŠœν”Œ(h, w)둜 ν‘œν˜„ν•©λ‹ˆλ‹€. μ—¬κΈ°μ„œ hλŠ” 높이, wλŠ” λ„ˆλΉ„μž…λ‹ˆλ‹€.

(640, 640)
radius int

그렀진 ν‚€ν¬μΈνŠΈμ˜ λ°˜κ²½μž…λ‹ˆλ‹€. 기본값은 5μž…λ‹ˆλ‹€.

5
kpt_line bool

True인 경우, 이 ν•¨μˆ˜λŠ” μ‚¬λžŒ 포즈의 ν‚€ν¬μΈνŠΈλ₯Ό μ—°κ²°ν•˜λŠ” μ„ ( 을 μ—°κ²°ν•˜λŠ” 선을 κ·Έλ¦½λ‹ˆλ‹€. 기본값은 Trueμž…λ‹ˆλ‹€.

True
μ°Έκ³ 

kpt_line=True λŠ” ν˜„μž¬ μ‚¬λžŒ 포즈 ν”Œλ‘œνŒ…λ§Œ μ§€μ›ν•©λ‹ˆλ‹€.

의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def kpts(self, kpts, shape=(640, 640), radius=5, kpt_line=True):
    """
    Plot keypoints on the image.

    Args:
        kpts (tensor): Predicted keypoints with shape [17, 3]. Each keypoint has (x, y, confidence).
        shape (tuple): Image shape as a tuple (h, w), where h is the height and w is the width.
        radius (int, optional): Radius of the drawn keypoints. Default is 5.
        kpt_line (bool, optional): If True, the function will draw lines connecting keypoints
                                   for human pose. Default is True.

    Note:
        `kpt_line=True` currently only supports human pose plotting.
    """
    if self.pil:
        # Convert to numpy first
        self.im = np.asarray(self.im).copy()
    nkpt, ndim = kpts.shape
    is_pose = nkpt == 17 and ndim in {2, 3}
    kpt_line &= is_pose  # `kpt_line=True` for now only supports human pose plotting
    for i, k in enumerate(kpts):
        color_k = [int(x) for x in self.kpt_color[i]] if is_pose else colors(i)
        x_coord, y_coord = k[0], k[1]
        if x_coord % shape[1] != 0 and y_coord % shape[0] != 0:
            if len(k) == 3:
                conf = k[2]
                if conf < 0.5:
                    continue
            cv2.circle(self.im, (int(x_coord), int(y_coord)), radius, color_k, -1, lineType=cv2.LINE_AA)

    if kpt_line:
        ndim = kpts.shape[-1]
        for i, sk in enumerate(self.skeleton):
            pos1 = (int(kpts[(sk[0] - 1), 0]), int(kpts[(sk[0] - 1), 1]))
            pos2 = (int(kpts[(sk[1] - 1), 0]), int(kpts[(sk[1] - 1), 1]))
            if ndim == 3:
                conf1 = kpts[(sk[0] - 1), 2]
                conf2 = kpts[(sk[1] - 1), 2]
                if conf1 < 0.5 or conf2 < 0.5:
                    continue
            if pos1[0] % shape[1] == 0 or pos1[1] % shape[0] == 0 or pos1[0] < 0 or pos1[1] < 0:
                continue
            if pos2[0] % shape[1] == 0 or pos2[1] % shape[0] == 0 or pos2[0] < 0 or pos2[1] < 0:
                continue
            cv2.line(self.im, pos1, pos2, [int(x) for x in self.limb_color[i]], thickness=2, lineType=cv2.LINE_AA)
    if self.pil:
        # Convert im back to PIL and update draw
        self.fromarray(self.im)

masks(masks, colors, im_gpu, alpha=0.5, retina_masks=False)

이미지에 마슀크λ₯Ό ν”Œλ‘―ν•©λ‹ˆλ‹€.

λ§€κ°œλ³€μˆ˜:

이름 μœ ν˜• μ„€λͺ… κΈ°λ³Έκ°’
masks tensor

μΏ λ‹€, λͺ¨μ–‘에 λŒ€ν•œ 예츑 마슀크: [n, h, w]

ν•„μˆ˜
colors List[List[Int]]

예츑 마슀크의 색상, [[r, g, b] * n]

ν•„μˆ˜
im_gpu tensor

μ΄λ―Έμ§€λŠ” μΏ λ‹€, λͺ¨μ–‘μž…λ‹ˆλ‹€: [3, h, w], λ²”μœ„: [0, 1]

ν•„μˆ˜
alpha float

마슀크 투λͺ…도: 0.0 μ™„μ „ 투λͺ…, 1.0 뢈투λͺ…

0.5
retina_masks bool

고해상도 마슀크λ₯Ό μ‚¬μš©ν• μ§€ μ—¬λΆ€μž…λ‹ˆλ‹€. 기본값은 Falseμž…λ‹ˆλ‹€.

False
의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def masks(self, masks, colors, im_gpu, alpha=0.5, retina_masks=False):
    """
    Plot masks on image.

    Args:
        masks (tensor): Predicted masks on cuda, shape: [n, h, w]
        colors (List[List[Int]]): Colors for predicted masks, [[r, g, b] * n]
        im_gpu (tensor): Image is in cuda, shape: [3, h, w], range: [0, 1]
        alpha (float): Mask transparency: 0.0 fully transparent, 1.0 opaque
        retina_masks (bool): Whether to use high resolution masks or not. Defaults to False.
    """
    if self.pil:
        # Convert to numpy first
        self.im = np.asarray(self.im).copy()
    if len(masks) == 0:
        self.im[:] = im_gpu.permute(1, 2, 0).contiguous().cpu().numpy() * 255
    if im_gpu.device != masks.device:
        im_gpu = im_gpu.to(masks.device)
    colors = torch.tensor(colors, device=masks.device, dtype=torch.float32) / 255.0  # shape(n,3)
    colors = colors[:, None, None]  # shape(n,1,1,3)
    masks = masks.unsqueeze(3)  # shape(n,h,w,1)
    masks_color = masks * (colors * alpha)  # shape(n,h,w,3)

    inv_alpha_masks = (1 - masks * alpha).cumprod(0)  # shape(n,h,w,1)
    mcs = masks_color.max(dim=0).values  # shape(n,h,w,3)

    im_gpu = im_gpu.flip(dims=[0])  # flip channel
    im_gpu = im_gpu.permute(1, 2, 0).contiguous()  # shape(h,w,3)
    im_gpu = im_gpu * inv_alpha_masks[-1] + mcs
    im_mask = im_gpu * 255
    im_mask_np = im_mask.byte().cpu().numpy()
    self.im[:] = im_mask_np if retina_masks else ops.scale_image(im_mask_np, self.im.shape)
    if self.pil:
        # Convert im back to PIL and update draw
        self.fromarray(self.im)

plot_angle_and_count_and_stage(angle_text, count_text, stage_text, center_kpt, line_thickness=2)

포즈 각도, 카운트 κ°’, μŠ€ν… μŠ€ν…Œμ΄μ§€λ₯Ό ν”Œλ‘―ν•©λ‹ˆλ‹€.

λ§€κ°œλ³€μˆ˜:

이름 μœ ν˜• μ„€λͺ… κΈ°λ³Έκ°’
angle_text str

μš΄λ™ λͺ¨λ‹ˆν„°λ§μ„ μœ„ν•œ 각도 κ°’

ν•„μˆ˜
count_text str

μš΄λ™ λͺ¨λ‹ˆν„°λ§μ„ μœ„ν•œ κ°€μΉ˜ 계산

ν•„μˆ˜
stage_text str

μš΄λ™ λͺ¨λ‹ˆν„°λ§μ„ μœ„ν•œ 단계 κ²°μ •

ν•„μˆ˜
center_kpt int

μš΄λ™ λͺ¨λ‹ˆν„°λ§μ„ μœ„ν•œ 쀑심 μžμ„Έ μ§€μˆ˜

ν•„μˆ˜
line_thickness int

ν…μŠ€νŠΈ ν‘œμ‹œλ₯Ό μœ„ν•œ λ‘κ»˜

2
의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def plot_angle_and_count_and_stage(self, angle_text, count_text, stage_text, center_kpt, line_thickness=2):
    """
    Plot the pose angle, count value and step stage.

    Args:
        angle_text (str): angle value for workout monitoring
        count_text (str): counts value for workout monitoring
        stage_text (str): stage decision for workout monitoring
        center_kpt (int): centroid pose index for workout monitoring
        line_thickness (int): thickness for text display
    """
    angle_text, count_text, stage_text = (f" {angle_text:.2f}", f"Steps : {count_text}", f" {stage_text}")
    font_scale = 0.6 + (line_thickness / 10.0)

    # Draw angle
    (angle_text_width, angle_text_height), _ = cv2.getTextSize(angle_text, 0, font_scale, line_thickness)
    angle_text_position = (int(center_kpt[0]), int(center_kpt[1]))
    angle_background_position = (angle_text_position[0], angle_text_position[1] - angle_text_height - 5)
    angle_background_size = (angle_text_width + 2 * 5, angle_text_height + 2 * 5 + (line_thickness * 2))
    cv2.rectangle(
        self.im,
        angle_background_position,
        (
            angle_background_position[0] + angle_background_size[0],
            angle_background_position[1] + angle_background_size[1],
        ),
        (255, 255, 255),
        -1,
    )
    cv2.putText(self.im, angle_text, angle_text_position, 0, font_scale, (0, 0, 0), line_thickness)

    # Draw Counts
    (count_text_width, count_text_height), _ = cv2.getTextSize(count_text, 0, font_scale, line_thickness)
    count_text_position = (angle_text_position[0], angle_text_position[1] + angle_text_height + 20)
    count_background_position = (
        angle_background_position[0],
        angle_background_position[1] + angle_background_size[1] + 5,
    )
    count_background_size = (count_text_width + 10, count_text_height + 10 + (line_thickness * 2))

    cv2.rectangle(
        self.im,
        count_background_position,
        (
            count_background_position[0] + count_background_size[0],
            count_background_position[1] + count_background_size[1],
        ),
        (255, 255, 255),
        -1,
    )
    cv2.putText(self.im, count_text, count_text_position, 0, font_scale, (0, 0, 0), line_thickness)

    # Draw Stage
    (stage_text_width, stage_text_height), _ = cv2.getTextSize(stage_text, 0, font_scale, line_thickness)
    stage_text_position = (int(center_kpt[0]), int(center_kpt[1]) + angle_text_height + count_text_height + 40)
    stage_background_position = (stage_text_position[0], stage_text_position[1] - stage_text_height - 5)
    stage_background_size = (stage_text_width + 10, stage_text_height + 10)

    cv2.rectangle(
        self.im,
        stage_background_position,
        (
            stage_background_position[0] + stage_background_size[0],
            stage_background_position[1] + stage_background_size[1],
        ),
        (255, 255, 255),
        -1,
    )
    cv2.putText(self.im, stage_text, stage_text_position, 0, font_scale, (0, 0, 0), line_thickness)

plot_distance_and_line(distance_m, distance_mm, centroids, line_color, centroid_color)

ν”„λ ˆμž„μ— 거리와 선을 ν”Œλ‘―ν•©λ‹ˆλ‹€.

λ§€κ°œλ³€μˆ˜:

이름 μœ ν˜• μ„€λͺ… κΈ°λ³Έκ°’
distance_m float

두 λ°•μŠ€ 쀑심 μ‚¬μ΄μ˜ 거리(λ―Έν„°)μž…λ‹ˆλ‹€.

ν•„μˆ˜
distance_mm float

두 λ°•μŠ€ ꡬ심점 μ‚¬μ΄μ˜ 거리(밀리미터)μž…λ‹ˆλ‹€.

ν•„μˆ˜
centroids list

λ°”μš΄λ”© λ°•μŠ€ 쀑심 데이터.

ν•„μˆ˜
line_color RGB

거리 μ„  색상.

ν•„μˆ˜
centroid_color RGB

λ°”μš΄λ”© λ°•μŠ€ 쀑심색.

ν•„μˆ˜
의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def plot_distance_and_line(self, distance_m, distance_mm, centroids, line_color, centroid_color):
    """
    Plot the distance and line on frame.

    Args:
        distance_m (float): Distance between two bbox centroids in meters.
        distance_mm (float): Distance between two bbox centroids in millimeters.
        centroids (list): Bounding box centroids data.
        line_color (RGB): Distance line color.
        centroid_color (RGB): Bounding box centroid color.
    """
    (text_width_m, text_height_m), _ = cv2.getTextSize(
        f"Distance M: {distance_m:.2f}m", cv2.FONT_HERSHEY_SIMPLEX, 0.8, 2
    )
    cv2.rectangle(self.im, (15, 25), (15 + text_width_m + 10, 25 + text_height_m + 20), (255, 255, 255), -1)
    cv2.putText(
        self.im,
        f"Distance M: {distance_m:.2f}m",
        (20, 50),
        cv2.FONT_HERSHEY_SIMPLEX,
        0.8,
        (0, 0, 0),
        2,
        cv2.LINE_AA,
    )

    (text_width_mm, text_height_mm), _ = cv2.getTextSize(
        f"Distance MM: {distance_mm:.2f}mm", cv2.FONT_HERSHEY_SIMPLEX, 0.8, 2
    )
    cv2.rectangle(self.im, (15, 75), (15 + text_width_mm + 10, 75 + text_height_mm + 20), (255, 255, 255), -1)
    cv2.putText(
        self.im,
        f"Distance MM: {distance_mm:.2f}mm",
        (20, 100),
        cv2.FONT_HERSHEY_SIMPLEX,
        0.8,
        (0, 0, 0),
        2,
        cv2.LINE_AA,
    )

    cv2.line(self.im, centroids[0], centroids[1], line_color, 3)
    cv2.circle(self.im, centroids[0], 6, centroid_color, -1)
    cv2.circle(self.im, centroids[1], 6, centroid_color, -1)

rectangle(xy, fill=None, outline=None, width=1)

이미지에 μ§μ‚¬κ°ν˜• μΆ”κ°€(PIL μ „μš©).

의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def rectangle(self, xy, fill=None, outline=None, width=1):
    """Add rectangle to image (PIL-only)."""
    self.draw.rectangle(xy, fill, outline, width)

result()

주석이 달린 이미지λ₯Ό λ°°μ—΄λ‘œ λ°˜ν™˜ν•©λ‹ˆλ‹€.

의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def result(self):
    """Return annotated image as array."""
    return np.asarray(self.im)

save(filename='image.jpg')

주석이 달린 이미지λ₯Ό '파일 이름'에 μ €μž₯ν•©λ‹ˆλ‹€.

의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def save(self, filename="image.jpg"):
    """Save the annotated image to 'filename'."""
    cv2.imwrite(filename, np.asarray(self.im))

seg_bbox(mask, mask_color=(255, 0, 255), det_label=None, track_label=None)

경계 μƒμž λͺ¨μ–‘μœΌλ‘œ λΆ„ν• λœ 개체λ₯Ό κ·Έλ¦¬λŠ” ν•¨μˆ˜μž…λ‹ˆλ‹€.

λ§€κ°œλ³€μˆ˜:

이름 μœ ν˜• μ„€λͺ… κΈ°λ³Έκ°’
mask list

μΈμŠ€ν„΄μŠ€ μ„ΈλΆ„ν™” μ˜μ—­ ν”Œλ‘œνŒ…μ„ μœ„ν•œ 데이터 λͺ©λ‘ λ§ˆμŠ€ν‚Ή

ν•„μˆ˜
mask_color tuple

전경색 마슀크

(255, 0, 255)
det_label str

감지 라벨 ν…μŠ€νŠΈ

None
track_label str

라벨 ν…μŠ€νŠΈ 좔적

None
의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def seg_bbox(self, mask, mask_color=(255, 0, 255), det_label=None, track_label=None):
    """
    Function for drawing segmented object in bounding box shape.

    Args:
        mask (list): masks data list for instance segmentation area plotting
        mask_color (tuple): mask foreground color
        det_label (str): Detection label text
        track_label (str): Tracking label text
    """
    cv2.polylines(self.im, [np.int32([mask])], isClosed=True, color=mask_color, thickness=2)

    label = f"Track ID: {track_label}" if track_label else det_label
    text_size, _ = cv2.getTextSize(label, 0, 0.7, 1)

    cv2.rectangle(
        self.im,
        (int(mask[0][0]) - text_size[0] // 2 - 10, int(mask[0][1]) - text_size[1] - 10),
        (int(mask[0][0]) + text_size[0] // 2 + 5, int(mask[0][1] + 5)),
        mask_color,
        -1,
    )

    cv2.putText(
        self.im, label, (int(mask[0][0]) - text_size[0] // 2, int(mask[0][1]) - 5), 0, 0.7, (255, 255, 255), 2
    )

show(title=None)

주석이 달린 이미지λ₯Ό ν‘œμ‹œν•©λ‹ˆλ‹€.

의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def show(self, title=None):
    """Show the annotated image."""
    Image.fromarray(np.asarray(self.im)[..., ::-1]).show(title)

text(xy, text, txt_color=(255, 255, 255), anchor='top', box_style=False)

PIL λ˜λŠ” cv2λ₯Ό μ‚¬μš©ν•˜μ—¬ 이미지에 ν…μŠ€νŠΈλ₯Ό μΆ”κ°€ν•©λ‹ˆλ‹€.

의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def text(self, xy, text, txt_color=(255, 255, 255), anchor="top", box_style=False):
    """Adds text to an image using PIL or cv2."""
    if anchor == "bottom":  # start y from font bottom
        w, h = self.font.getsize(text)  # text width, height
        xy[1] += 1 - h
    if self.pil:
        if box_style:
            w, h = self.font.getsize(text)
            self.draw.rectangle((xy[0], xy[1], xy[0] + w + 1, xy[1] + h + 1), fill=txt_color)
            # Using `txt_color` for background and draw fg with white color
            txt_color = (255, 255, 255)
        if "\n" in text:
            lines = text.split("\n")
            _, h = self.font.getsize(text)
            for line in lines:
                self.draw.text(xy, line, fill=txt_color, font=self.font)
                xy[1] += h
        else:
            self.draw.text(xy, text, fill=txt_color, font=self.font)
    else:
        if box_style:
            w, h = cv2.getTextSize(text, 0, fontScale=self.sf, thickness=self.tf)[0]  # text width, height
            outside = xy[1] - h >= 3
            p2 = xy[0] + w, xy[1] - h - 3 if outside else xy[1] + h + 3
            cv2.rectangle(self.im, xy, p2, txt_color, -1, cv2.LINE_AA)  # filled
            # Using `txt_color` for background and draw fg with white color
            txt_color = (255, 255, 255)
        cv2.putText(self.im, text, xy, 0, self.sf, txt_color, thickness=self.tf, lineType=cv2.LINE_AA)

visioneye(box, center_point, color=(235, 219, 11), pin_color=(255, 0, 255), thickness=2, pins_radius=10)

μ‚¬λžŒμ˜ μ‹œκ°μ„ μ •ν™•νžˆ νŒŒμ•…ν•˜μ—¬ λ§€ν•‘ν•˜κ³  ν”Œλ‘œνŒ…ν•˜λŠ” κΈ°λŠ₯μž…λ‹ˆλ‹€.

λ§€κ°œλ³€μˆ˜:

이름 μœ ν˜• μ„€λͺ… κΈ°λ³Έκ°’
box list

경계 μƒμž μ’Œν‘œ

ν•„μˆ˜
center_point tuple

μ‹œμ•Όλ₯Ό μœ„ν•œ 쀑심점

ν•„μˆ˜
color tuple

개체 쀑심 및 μ„  색상 κ°’

(235, 219, 11)
pin_color tuple

비전아이 포인트 색상 κ°’

(255, 0, 255)
thickness int

μ„  λ‘κ»˜μ— λŒ€ν•œ int κ°’

2
pins_radius int

비전아이 포인트 반경 κ°’

10
의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def visioneye(self, box, center_point, color=(235, 219, 11), pin_color=(255, 0, 255), thickness=2, pins_radius=10):
    """
    Function for pinpoint human-vision eye mapping and plotting.

    Args:
        box (list): Bounding box coordinates
        center_point (tuple): center point for vision eye view
        color (tuple): object centroid and line color value
        pin_color (tuple): visioneye point color value
        thickness (int): int value for line thickness
        pins_radius (int): visioneye point radius value
    """
    center_bbox = int((box[0] + box[2]) / 2), int((box[1] + box[3]) / 2)
    cv2.circle(self.im, center_point, pins_radius, pin_color, -1)
    cv2.circle(self.im, center_bbox, pins_radius, color, -1)
    cv2.line(self.im, center_point, center_bbox, color, thickness)



ultralytics.utils.plotting.plot_labels(boxes, cls, names=(), save_dir=Path(''), on_plot=None)

클래슀 νžˆμŠ€ν† κ·Έλž¨κ³Ό λ°•μŠ€ 톡계λ₯Ό ν¬ν•¨ν•œ νŠΈλ ˆμ΄λ‹ λ ˆμ΄λΈ”μ„ ν”Œλ‘―ν•©λ‹ˆλ‹€.

의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
@TryExcept()  # known issue https://github.com/ultralytics/yolov5/issues/5395
@plt_settings()
def plot_labels(boxes, cls, names=(), save_dir=Path(""), on_plot=None):
    """Plot training labels including class histograms and box statistics."""
    import pandas as pd
    import seaborn as sn

    # Filter matplotlib>=3.7.2 warning and Seaborn use_inf and is_categorical FutureWarnings
    warnings.filterwarnings("ignore", category=UserWarning, message="The figure layout has changed to tight")
    warnings.filterwarnings("ignore", category=FutureWarning)

    # Plot dataset labels
    LOGGER.info(f"Plotting labels to {save_dir / 'labels.jpg'}... ")
    nc = int(cls.max() + 1)  # number of classes
    boxes = boxes[:1000000]  # limit to 1M boxes
    x = pd.DataFrame(boxes, columns=["x", "y", "width", "height"])

    # Seaborn correlogram
    sn.pairplot(x, corner=True, diag_kind="auto", kind="hist", diag_kws=dict(bins=50), plot_kws=dict(pmax=0.9))
    plt.savefig(save_dir / "labels_correlogram.jpg", dpi=200)
    plt.close()

    # Matplotlib labels
    ax = plt.subplots(2, 2, figsize=(8, 8), tight_layout=True)[1].ravel()
    y = ax[0].hist(cls, bins=np.linspace(0, nc, nc + 1) - 0.5, rwidth=0.8)
    for i in range(nc):
        y[2].patches[i].set_color([x / 255 for x in colors(i)])
    ax[0].set_ylabel("instances")
    if 0 < len(names) < 30:
        ax[0].set_xticks(range(len(names)))
        ax[0].set_xticklabels(list(names.values()), rotation=90, fontsize=10)
    else:
        ax[0].set_xlabel("classes")
    sn.histplot(x, x="x", y="y", ax=ax[2], bins=50, pmax=0.9)
    sn.histplot(x, x="width", y="height", ax=ax[3], bins=50, pmax=0.9)

    # Rectangles
    boxes[:, 0:2] = 0.5  # center
    boxes = ops.xywh2xyxy(boxes) * 1000
    img = Image.fromarray(np.ones((1000, 1000, 3), dtype=np.uint8) * 255)
    for cls, box in zip(cls[:500], boxes[:500]):
        ImageDraw.Draw(img).rectangle(box, width=1, outline=colors(cls))  # plot
    ax[1].imshow(img)
    ax[1].axis("off")

    for a in [0, 1, 2, 3]:
        for s in ["top", "right", "left", "bottom"]:
            ax[a].spines[s].set_visible(False)

    fname = save_dir / "labels.jpg"
    plt.savefig(fname, dpi=200)
    plt.close()
    if on_plot:
        on_plot(fname)



ultralytics.utils.plotting.save_one_box(xyxy, im, file=Path('im.jpg'), gain=1.02, pad=10, square=False, BGR=False, save=True)

이미지 자λ₯΄κΈ°λ₯Ό {파일}둜 μ €μž₯ν•˜κ³  자λ₯΄κΈ° ν¬κΈ°λŠ” {게인}κ³Ό {νŒ¨λ“œ} 픽셀을 κ³±ν•œ κ°’μœΌλ‘œ μ„€μ •ν•©λ‹ˆλ‹€. 자λ₯Έ 이미지λ₯Ό μ €μž₯ 및/λ˜λŠ” λ°˜ν™˜ν•©λ‹ˆλ‹€.

이 ν•¨μˆ˜λŠ” 경계 μƒμžμ™€ 이미지λ₯Ό κ°€μ Έμ˜¨ λ‹€μŒ 경계 μƒμžμ— 따라 μ΄λ―Έμ§€μ˜ 잘린 뢀뢄을 에 따라 μ΄λ―Έμ§€μ˜ 잘린 뢀뢄을 μ €μž₯ν•©λ‹ˆλ‹€. μ„ νƒμ μœΌλ‘œ 자λ₯Έ 뢀뢄을 μ œκ³±ν•  수 있으며, 이 ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜μ—¬ 경계 μƒμžμ— 게인 및 νŒ¨λ”©( 쑰정이 κ°€λŠ₯ν•©λ‹ˆλ‹€.

λ§€κ°œλ³€μˆ˜:

이름 μœ ν˜• μ„€λͺ… κΈ°λ³Έκ°’
xyxy Tensor or list

tensor λ˜λŠ” xyxy ν˜•μ‹μ˜ 경계 μƒμžλ₯Ό λ‚˜νƒ€λ‚΄λŠ” λͺ©λ‘μž…λ‹ˆλ‹€.

ν•„μˆ˜
im ndarray

μž…λ ₯ μ΄λ―Έμ§€μž…λ‹ˆλ‹€.

ν•„μˆ˜
file Path

잘린 이미지가 μ €μž₯될 κ²½λ‘œμž…λ‹ˆλ‹€. 기본값은 'im.jpg'μž…λ‹ˆλ‹€.

Path('im.jpg')
gain float

λ°”μš΄λ”© λ°•μŠ€μ˜ 크기λ₯Ό 늘리기 μœ„ν•œ κ³±μ…ˆ κ³„μˆ˜μž…λ‹ˆλ‹€. 기본값은 1.02μž…λ‹ˆλ‹€.

1.02
pad int

경계 μƒμžμ˜ λ„ˆλΉ„μ™€ 높이에 μΆ”κ°€ν•  ν”½μ…€ μˆ˜μž…λ‹ˆλ‹€. 기본값은 10μž…λ‹ˆλ‹€.

10
square bool

True이면 λ°”μš΄λ”© λ°•μŠ€κ°€ μ •μ‚¬κ°ν˜•μœΌλ‘œ λ³€ν™˜λ©λ‹ˆλ‹€. 기본값은 False μž…λ‹ˆλ‹€.

False
BGR bool

True이면 이미지가 BGR ν˜•μ‹μœΌλ‘œ μ €μž₯되고, 그렇지 μ•ŠμœΌλ©΄ RGB둜 μ €μž₯λ©λ‹ˆλ‹€. 기본값은 Falseμž…λ‹ˆλ‹€.

False
save bool

True둜 μ„€μ •ν•˜λ©΄ 잘린 이미지가 λ””μŠ€ν¬μ— μ €μž₯λ©λ‹ˆλ‹€. 기본값은 Trueμž…λ‹ˆλ‹€.

True

λ°˜ν™˜ν•©λ‹ˆλ‹€:

μœ ν˜• μ„€λͺ…
ndarray

잘린 μ΄λ―Έμ§€μž…λ‹ˆλ‹€.

예제
from ultralytics.utils.plotting import save_one_box

xyxy = [50, 50, 150, 150]
im = cv2.imread('image.jpg')
cropped_im = save_one_box(xyxy, im, file='cropped.jpg', square=True)
의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def save_one_box(xyxy, im, file=Path("im.jpg"), gain=1.02, pad=10, square=False, BGR=False, save=True):
    """
    Save image crop as {file} with crop size multiple {gain} and {pad} pixels. Save and/or return crop.

    This function takes a bounding box and an image, and then saves a cropped portion of the image according
    to the bounding box. Optionally, the crop can be squared, and the function allows for gain and padding
    adjustments to the bounding box.

    Args:
        xyxy (torch.Tensor or list): A tensor or list representing the bounding box in xyxy format.
        im (numpy.ndarray): The input image.
        file (Path, optional): The path where the cropped image will be saved. Defaults to 'im.jpg'.
        gain (float, optional): A multiplicative factor to increase the size of the bounding box. Defaults to 1.02.
        pad (int, optional): The number of pixels to add to the width and height of the bounding box. Defaults to 10.
        square (bool, optional): If True, the bounding box will be transformed into a square. Defaults to False.
        BGR (bool, optional): If True, the image will be saved in BGR format, otherwise in RGB. Defaults to False.
        save (bool, optional): If True, the cropped image will be saved to disk. Defaults to True.

    Returns:
        (numpy.ndarray): The cropped image.

    Example:
        ```python
        from ultralytics.utils.plotting import save_one_box

        xyxy = [50, 50, 150, 150]
        im = cv2.imread('image.jpg')
        cropped_im = save_one_box(xyxy, im, file='cropped.jpg', square=True)
        ```
    """

    if not isinstance(xyxy, torch.Tensor):  # may be list
        xyxy = torch.stack(xyxy)
    b = ops.xyxy2xywh(xyxy.view(-1, 4))  # boxes
    if square:
        b[:, 2:] = b[:, 2:].max(1)[0].unsqueeze(1)  # attempt rectangle to square
    b[:, 2:] = b[:, 2:] * gain + pad  # box wh * gain + pad
    xyxy = ops.xywh2xyxy(b).long()
    xyxy = ops.clip_boxes(xyxy, im.shape)
    crop = im[int(xyxy[0, 1]) : int(xyxy[0, 3]), int(xyxy[0, 0]) : int(xyxy[0, 2]), :: (1 if BGR else -1)]
    if save:
        file.parent.mkdir(parents=True, exist_ok=True)  # make directory
        f = str(increment_path(file).with_suffix(".jpg"))
        # cv2.imwrite(f, crop)  # save BGR, https://github.com/ultralytics/yolov5/issues/7007 chroma subsampling issue
        Image.fromarray(crop[..., ::-1]).save(f, quality=95, subsampling=0)  # save RGB
    return crop



ultralytics.utils.plotting.plot_images(images, batch_idx, cls, bboxes=np.zeros(0, dtype=np.float32), confs=None, masks=np.zeros(0, dtype=np.uint8), kpts=np.zeros((0, 51), dtype=np.float32), paths=None, fname='images.jpg', names=None, on_plot=None, max_subplots=16, save=True, conf_thres=0.25)

λ ˆμ΄λΈ”μ΄ μžˆλŠ” 이미지 κ·Έλ¦¬λ“œλ₯Ό ν”Œλ‘―ν•©λ‹ˆλ‹€.

의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
@threaded
def plot_images(
    images,
    batch_idx,
    cls,
    bboxes=np.zeros(0, dtype=np.float32),
    confs=None,
    masks=np.zeros(0, dtype=np.uint8),
    kpts=np.zeros((0, 51), dtype=np.float32),
    paths=None,
    fname="images.jpg",
    names=None,
    on_plot=None,
    max_subplots=16,
    save=True,
    conf_thres=0.25,
):
    """Plot image grid with labels."""
    if isinstance(images, torch.Tensor):
        images = images.cpu().float().numpy()
    if isinstance(cls, torch.Tensor):
        cls = cls.cpu().numpy()
    if isinstance(bboxes, torch.Tensor):
        bboxes = bboxes.cpu().numpy()
    if isinstance(masks, torch.Tensor):
        masks = masks.cpu().numpy().astype(int)
    if isinstance(kpts, torch.Tensor):
        kpts = kpts.cpu().numpy()
    if isinstance(batch_idx, torch.Tensor):
        batch_idx = batch_idx.cpu().numpy()

    max_size = 1920  # max image size
    bs, _, h, w = images.shape  # batch size, _, height, width
    bs = min(bs, max_subplots)  # limit plot images
    ns = np.ceil(bs**0.5)  # number of subplots (square)
    if np.max(images[0]) <= 1:
        images *= 255  # de-normalise (optional)

    # Build Image
    mosaic = np.full((int(ns * h), int(ns * w), 3), 255, dtype=np.uint8)  # init
    for i in range(bs):
        x, y = int(w * (i // ns)), int(h * (i % ns))  # block origin
        mosaic[y : y + h, x : x + w, :] = images[i].transpose(1, 2, 0)

    # Resize (optional)
    scale = max_size / ns / max(h, w)
    if scale < 1:
        h = math.ceil(scale * h)
        w = math.ceil(scale * w)
        mosaic = cv2.resize(mosaic, tuple(int(x * ns) for x in (w, h)))

    # Annotate
    fs = int((h + w) * ns * 0.01)  # font size
    annotator = Annotator(mosaic, line_width=round(fs / 10), font_size=fs, pil=True, example=names)
    for i in range(bs):
        x, y = int(w * (i // ns)), int(h * (i % ns))  # block origin
        annotator.rectangle([x, y, x + w, y + h], None, (255, 255, 255), width=2)  # borders
        if paths:
            annotator.text((x + 5, y + 5), text=Path(paths[i]).name[:40], txt_color=(220, 220, 220))  # filenames
        if len(cls) > 0:
            idx = batch_idx == i
            classes = cls[idx].astype("int")
            labels = confs is None

            if len(bboxes):
                boxes = bboxes[idx]
                conf = confs[idx] if confs is not None else None  # check for confidence presence (label vs pred)
                is_obb = boxes.shape[-1] == 5  # xywhr
                boxes = ops.xywhr2xyxyxyxy(boxes) if is_obb else ops.xywh2xyxy(boxes)
                if len(boxes):
                    if boxes[:, :4].max() <= 1.1:  # if normalized with tolerance 0.1
                        boxes[..., 0::2] *= w  # scale to pixels
                        boxes[..., 1::2] *= h
                    elif scale < 1:  # absolute coords need scale if image scales
                        boxes[..., :4] *= scale
                boxes[..., 0::2] += x
                boxes[..., 1::2] += y
                for j, box in enumerate(boxes.astype(np.int64).tolist()):
                    c = classes[j]
                    color = colors(c)
                    c = names.get(c, c) if names else c
                    if labels or conf[j] > conf_thres:
                        label = f"{c}" if labels else f"{c} {conf[j]:.1f}"
                        annotator.box_label(box, label, color=color, rotated=is_obb)

            elif len(classes):
                for c in classes:
                    color = colors(c)
                    c = names.get(c, c) if names else c
                    annotator.text((x, y), f"{c}", txt_color=color, box_style=True)

            # Plot keypoints
            if len(kpts):
                kpts_ = kpts[idx].copy()
                if len(kpts_):
                    if kpts_[..., 0].max() <= 1.01 or kpts_[..., 1].max() <= 1.01:  # if normalized with tolerance .01
                        kpts_[..., 0] *= w  # scale to pixels
                        kpts_[..., 1] *= h
                    elif scale < 1:  # absolute coords need scale if image scales
                        kpts_ *= scale
                kpts_[..., 0] += x
                kpts_[..., 1] += y
                for j in range(len(kpts_)):
                    if labels or conf[j] > conf_thres:
                        annotator.kpts(kpts_[j])

            # Plot masks
            if len(masks):
                if idx.shape[0] == masks.shape[0]:  # overlap_masks=False
                    image_masks = masks[idx]
                else:  # overlap_masks=True
                    image_masks = masks[[i]]  # (1, 640, 640)
                    nl = idx.sum()
                    index = np.arange(nl).reshape((nl, 1, 1)) + 1
                    image_masks = np.repeat(image_masks, nl, axis=0)
                    image_masks = np.where(image_masks == index, 1.0, 0.0)

                im = np.asarray(annotator.im).copy()
                for j in range(len(image_masks)):
                    if labels or conf[j] > conf_thres:
                        color = colors(classes[j])
                        mh, mw = image_masks[j].shape
                        if mh != h or mw != w:
                            mask = image_masks[j].astype(np.uint8)
                            mask = cv2.resize(mask, (w, h))
                            mask = mask.astype(bool)
                        else:
                            mask = image_masks[j].astype(bool)
                        with contextlib.suppress(Exception):
                            im[y : y + h, x : x + w, :][mask] = (
                                im[y : y + h, x : x + w, :][mask] * 0.4 + np.array(color) * 0.6
                            )
                annotator.fromarray(im)
    if not save:
        return np.asarray(annotator.im)
    annotator.im.save(fname)  # save
    if on_plot:
        on_plot(fname)



ultralytics.utils.plotting.plot_results(file='path/to/results.csv', dir='', segment=False, pose=False, classify=False, on_plot=None)

κ²°κ³Ό CSV νŒŒμΌμ—μ„œ ν›ˆλ ¨ κ²°κ³Όλ₯Ό ν”Œλ‘œνŒ…ν•©λ‹ˆλ‹€. 이 κΈ°λŠ₯은 μ„ΈλΆ„ν™”λ₯Ό ν¬ν•¨ν•œ λ‹€μ–‘ν•œ μœ ν˜•μ˜ 데이터λ₯Ό μ§€μ›ν•©λ‹ˆλ‹€, 포즈 μΆ”μ •, λΆ„λ₯˜ λ“± λ‹€μ–‘ν•œ μœ ν˜•μ˜ 데이터λ₯Ό μ§€μ›ν•©λ‹ˆλ‹€. ν”Œλ‘―μ€ CSVκ°€ μžˆλŠ” 디렉토리에 'results.png'λΌλŠ” μ΄λ¦„μœΌλ‘œ μ €μž₯λ©λ‹ˆλ‹€.

λ§€κ°œλ³€μˆ˜:

이름 μœ ν˜• μ„€λͺ… κΈ°λ³Έκ°’
file str

νŠΈλ ˆμ΄λ‹ κ²°κ³Όκ°€ ν¬ν•¨λœ CSV 파일의 κ²½λ‘œμž…λ‹ˆλ‹€. 기본값은 '경둜/λŒ€μƒ/κ²°κ³Ό.csv'μž…λ‹ˆλ‹€.

'path/to/results.csv'
dir str

'file'이 μ œκ³΅λ˜μ§€ μ•Šμ€ 경우 CSV 파일이 μžˆλŠ” λ””λ ‰ν„°λ¦¬μž…λ‹ˆλ‹€. 기본값은 ''μž…λ‹ˆλ‹€.

''
segment bool

데이터가 μ„ΈλΆ„ν™”μš©μΈμ§€ μ—¬λΆ€λ₯Ό λ‚˜νƒ€λ‚΄λŠ” ν”Œλž˜κ·Έμž…λ‹ˆλ‹€. 기본값은 Falseμž…λ‹ˆλ‹€.

False
pose bool

데이터가 포즈 μΆ”μ •μš©μΈμ§€ μ—¬λΆ€λ₯Ό λ‚˜νƒ€λ‚΄λŠ” ν”Œλž˜κ·Έμž…λ‹ˆλ‹€. 기본값은 Falseμž…λ‹ˆλ‹€.

False
classify bool

데이터가 λΆ„λ₯˜μš©μΈμ§€ μ—¬λΆ€λ₯Ό λ‚˜νƒ€λ‚΄λŠ” ν”Œλž˜κ·Έμž…λ‹ˆλ‹€. 기본값은 Falseμž…λ‹ˆλ‹€.

False
on_plot callable

ν”Œλ‘œνŒ… ν›„ μ‹€ν–‰ν•  콜백 ν•¨μˆ˜. 파일 이름을 인수둜 λ°›μŠ΅λ‹ˆλ‹€. 기본값은 Noneμž…λ‹ˆλ‹€.

None
예제
from ultralytics.utils.plotting import plot_results

plot_results('path/to/results.csv', segment=True)
의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
@plt_settings()
def plot_results(file="path/to/results.csv", dir="", segment=False, pose=False, classify=False, on_plot=None):
    """
    Plot training results from a results CSV file. The function supports various types of data including segmentation,
    pose estimation, and classification. Plots are saved as 'results.png' in the directory where the CSV is located.

    Args:
        file (str, optional): Path to the CSV file containing the training results. Defaults to 'path/to/results.csv'.
        dir (str, optional): Directory where the CSV file is located if 'file' is not provided. Defaults to ''.
        segment (bool, optional): Flag to indicate if the data is for segmentation. Defaults to False.
        pose (bool, optional): Flag to indicate if the data is for pose estimation. Defaults to False.
        classify (bool, optional): Flag to indicate if the data is for classification. Defaults to False.
        on_plot (callable, optional): Callback function to be executed after plotting. Takes filename as an argument.
            Defaults to None.

    Example:
        ```python
        from ultralytics.utils.plotting import plot_results

        plot_results('path/to/results.csv', segment=True)
        ```
    """
    import pandas as pd
    from scipy.ndimage import gaussian_filter1d

    save_dir = Path(file).parent if file else Path(dir)
    if classify:
        fig, ax = plt.subplots(2, 2, figsize=(6, 6), tight_layout=True)
        index = [1, 4, 2, 3]
    elif segment:
        fig, ax = plt.subplots(2, 8, figsize=(18, 6), tight_layout=True)
        index = [1, 2, 3, 4, 5, 6, 9, 10, 13, 14, 15, 16, 7, 8, 11, 12]
    elif pose:
        fig, ax = plt.subplots(2, 9, figsize=(21, 6), tight_layout=True)
        index = [1, 2, 3, 4, 5, 6, 7, 10, 11, 14, 15, 16, 17, 18, 8, 9, 12, 13]
    else:
        fig, ax = plt.subplots(2, 5, figsize=(12, 6), tight_layout=True)
        index = [1, 2, 3, 4, 5, 8, 9, 10, 6, 7]
    ax = ax.ravel()
    files = list(save_dir.glob("results*.csv"))
    assert len(files), f"No results.csv files found in {save_dir.resolve()}, nothing to plot."
    for f in files:
        try:
            data = pd.read_csv(f)
            s = [x.strip() for x in data.columns]
            x = data.values[:, 0]
            for i, j in enumerate(index):
                y = data.values[:, j].astype("float")
                # y[y == 0] = np.nan  # don't show zero values
                ax[i].plot(x, y, marker=".", label=f.stem, linewidth=2, markersize=8)  # actual results
                ax[i].plot(x, gaussian_filter1d(y, sigma=3), ":", label="smooth", linewidth=2)  # smoothing line
                ax[i].set_title(s[j], fontsize=12)
                # if j in [8, 9, 10]:  # share train and val loss y axes
                #     ax[i].get_shared_y_axes().join(ax[i], ax[i - 5])
        except Exception as e:
            LOGGER.warning(f"WARNING: Plotting error for {f}: {e}")
    ax[1].legend()
    fname = save_dir / "results.png"
    fig.savefig(fname, dpi=200)
    plt.close()
    if on_plot:
        on_plot(fname)



ultralytics.utils.plotting.plt_color_scatter(v, f, bins=20, cmap='viridis', alpha=0.8, edgecolors='none')

2D νžˆμŠ€ν† κ·Έλž¨μ„ 기반으둜 색상이 μ§€μ •λœ 점으둜 λΆ„μ‚°ν˜• 차트λ₯Ό ν‘œμ‹œν•©λ‹ˆλ‹€.

λ§€κ°œλ³€μˆ˜:

이름 μœ ν˜• μ„€λͺ… κΈ°λ³Έκ°’
v array - like

XμΆ•μ˜ κ°’μž…λ‹ˆλ‹€.

ν•„μˆ˜
f array - like

YμΆ•μ˜ κ°’μž…λ‹ˆλ‹€.

ν•„μˆ˜
bins int

νžˆμŠ€ν† κ·Έλž¨μ˜ ꡬ간차원 μˆ˜μž…λ‹ˆλ‹€. 기본값은 20μž…λ‹ˆλ‹€.

20
cmap str

λΆ„μ‚°ν˜• 차트의 μ»¬λŸ¬λ§΅μž…λ‹ˆλ‹€. 기본값은 'viridis'μž…λ‹ˆλ‹€.

'viridis'
alpha float

μ‚°μ λ„μ˜ μ•ŒνŒŒμž…λ‹ˆλ‹€. 기본값은 0.8μž…λ‹ˆλ‹€.

0.8
edgecolors str

λΆ„μ‚°ν˜• 차트의 κ°€μž₯자리 μƒ‰μƒμž…λ‹ˆλ‹€. 기본값은 'μ—†μŒ'μž…λ‹ˆλ‹€.

'none'

μ˜ˆμ‹œ:

>>> v = np.random.rand(100)
>>> f = np.random.rand(100)
>>> plt_color_scatter(v, f)
의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def plt_color_scatter(v, f, bins=20, cmap="viridis", alpha=0.8, edgecolors="none"):
    """
    Plots a scatter plot with points colored based on a 2D histogram.

    Args:
        v (array-like): Values for the x-axis.
        f (array-like): Values for the y-axis.
        bins (int, optional): Number of bins for the histogram. Defaults to 20.
        cmap (str, optional): Colormap for the scatter plot. Defaults to 'viridis'.
        alpha (float, optional): Alpha for the scatter plot. Defaults to 0.8.
        edgecolors (str, optional): Edge colors for the scatter plot. Defaults to 'none'.

    Examples:
        >>> v = np.random.rand(100)
        >>> f = np.random.rand(100)
        >>> plt_color_scatter(v, f)
    """

    # Calculate 2D histogram and corresponding colors
    hist, xedges, yedges = np.histogram2d(v, f, bins=bins)
    colors = [
        hist[
            min(np.digitize(v[i], xedges, right=True) - 1, hist.shape[0] - 1),
            min(np.digitize(f[i], yedges, right=True) - 1, hist.shape[1] - 1),
        ]
        for i in range(len(v))
    ]

    # Scatter plot
    plt.scatter(v, f, c=colors, cmap=cmap, alpha=alpha, edgecolors=edgecolors)



ultralytics.utils.plotting.plot_tune_results(csv_file='tune_results.csv')

'tune_results.csv' νŒŒμΌμ— μ €μž₯된 진화 κ²°κ³Όλ₯Ό ν”Œλ‘œνŒ…ν•©λ‹ˆλ‹€. 이 ν•¨μˆ˜λŠ” 각 킀에 λŒ€ν•œ 산점도λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€. 에 λŒ€ν•΄ 산포도λ₯Ό μƒμ„±ν•˜λ©°, 적합도 μ μˆ˜μ— 따라 μƒ‰μƒμœΌλ‘œ κ΅¬λΆ„λ©λ‹ˆλ‹€. κ°€μž₯ μ„±λŠ₯이 쒋은 ꡬ성은 ν”Œλ‘―μ—μ„œ κ°•μ‘° ν‘œμ‹œλ©λ‹ˆλ‹€.

λ§€κ°œλ³€μˆ˜:

이름 μœ ν˜• μ„€λͺ… κΈ°λ³Έκ°’
csv_file str

νŠœλ‹ κ²°κ³Όκ°€ ν¬ν•¨λœ CSV 파일의 κ²½λ‘œμž…λ‹ˆλ‹€. 기본값은 'tune_results.csv'μž…λ‹ˆλ‹€.

'tune_results.csv'

μ˜ˆμ‹œ:

>>> plot_tune_results('path/to/tune_results.csv')
의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def plot_tune_results(csv_file="tune_results.csv"):
    """
    Plot the evolution results stored in an 'tune_results.csv' file. The function generates a scatter plot for each key
    in the CSV, color-coded based on fitness scores. The best-performing configurations are highlighted on the plots.

    Args:
        csv_file (str, optional): Path to the CSV file containing the tuning results. Defaults to 'tune_results.csv'.

    Examples:
        >>> plot_tune_results('path/to/tune_results.csv')
    """

    import pandas as pd
    from scipy.ndimage import gaussian_filter1d

    # Scatter plots for each hyperparameter
    csv_file = Path(csv_file)
    data = pd.read_csv(csv_file)
    num_metrics_columns = 1
    keys = [x.strip() for x in data.columns][num_metrics_columns:]
    x = data.values
    fitness = x[:, 0]  # fitness
    j = np.argmax(fitness)  # max fitness index
    n = math.ceil(len(keys) ** 0.5)  # columns and rows in plot
    plt.figure(figsize=(10, 10), tight_layout=True)
    for i, k in enumerate(keys):
        v = x[:, i + num_metrics_columns]
        mu = v[j]  # best single result
        plt.subplot(n, n, i + 1)
        plt_color_scatter(v, fitness, cmap="viridis", alpha=0.8, edgecolors="none")
        plt.plot(mu, fitness.max(), "k+", markersize=15)
        plt.title(f"{k} = {mu:.3g}", fontdict={"size": 9})  # limit to 40 characters
        plt.tick_params(axis="both", labelsize=8)  # Set axis label size to 8
        if i % n != 0:
            plt.yticks([])

    file = csv_file.with_name("tune_scatter_plots.png")  # filename
    plt.savefig(file, dpi=200)
    plt.close()
    LOGGER.info(f"Saved {file}")

    # Fitness vs iteration
    x = range(1, len(fitness) + 1)
    plt.figure(figsize=(10, 6), tight_layout=True)
    plt.plot(x, fitness, marker="o", linestyle="none", label="fitness")
    plt.plot(x, gaussian_filter1d(fitness, sigma=3), ":", label="smoothed", linewidth=2)  # smoothing line
    plt.title("Fitness vs Iteration")
    plt.xlabel("Iteration")
    plt.ylabel("Fitness")
    plt.grid(True)
    plt.legend()

    file = csv_file.with_name("tune_fitness.png")  # filename
    plt.savefig(file, dpi=200)
    plt.close()
    LOGGER.info(f"Saved {file}")



ultralytics.utils.plotting.output_to_target(output, max_det=300)

λͺ¨λΈ 좜λ ₯을 ν”Œλ‘œνŒ…μ„ μœ„ν•΄ λŒ€μƒ ν˜•μ‹[batch_id, class_id, x, y, w, h, conf]으둜 λ³€ν™˜ν•©λ‹ˆλ‹€.

의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def output_to_target(output, max_det=300):
    """Convert model output to target format [batch_id, class_id, x, y, w, h, conf] for plotting."""
    targets = []
    for i, o in enumerate(output):
        box, conf, cls = o[:max_det, :6].cpu().split((4, 1, 1), 1)
        j = torch.full((conf.shape[0], 1), i)
        targets.append(torch.cat((j, cls, ops.xyxy2xywh(box), conf), 1))
    targets = torch.cat(targets, 0).numpy()
    return targets[:, 0], targets[:, 1], targets[:, 2:-1], targets[:, -1]



ultralytics.utils.plotting.output_to_rotated_target(output, max_det=300)

λͺ¨λΈ 좜λ ₯을 ν”Œλ‘œνŒ…μ„ μœ„ν•΄ λŒ€μƒ ν˜•μ‹[batch_id, class_id, x, y, w, h, conf]으둜 λ³€ν™˜ν•©λ‹ˆλ‹€.

의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def output_to_rotated_target(output, max_det=300):
    """Convert model output to target format [batch_id, class_id, x, y, w, h, conf] for plotting."""
    targets = []
    for i, o in enumerate(output):
        box, conf, cls, angle = o[:max_det].cpu().split((4, 1, 1, 1), 1)
        j = torch.full((conf.shape[0], 1), i)
        targets.append(torch.cat((j, cls, box, angle, conf), 1))
    targets = torch.cat(targets, 0).numpy()
    return targets[:, 0], targets[:, 1], targets[:, 2:-1], targets[:, -1]



ultralytics.utils.plotting.feature_visualization(x, module_type, stage, n=32, save_dir=Path('runs/detect/exp'))

μΆ”λ‘  쀑에 주어진 λͺ¨λΈ λͺ¨λ“ˆμ˜ νŠΉμ§• 맡을 μ‹œκ°ν™”ν•©λ‹ˆλ‹€.

λ§€κ°œλ³€μˆ˜:

이름 μœ ν˜• μ„€λͺ… κΈ°λ³Έκ°’
x Tensor

μ‹œκ°ν™”ν•  κΈ°λŠ₯.

ν•„μˆ˜
module_type str

λͺ¨λ“ˆ μœ ν˜•.

ν•„μˆ˜
stage int

λͺ¨λΈ λ‚΄ λͺ¨λ“ˆ 단계.

ν•„μˆ˜
n int

ν”Œλ‘œνŒ…ν•  ν”Όμ²˜ 맡의 μ΅œλŒ€ κ°œμˆ˜μž…λ‹ˆλ‹€. 기본값은 32κ°œμž…λ‹ˆλ‹€.

32
save_dir Path

디렉터리에 κ²°κ³Όλ₯Ό μ €μž₯ν•©λ‹ˆλ‹€. 기본값은 Path('runs/detect/exp')μž…λ‹ˆλ‹€.

Path('runs/detect/exp')
의 μ†ŒμŠ€ μ½”λ“œ ultralytics/utils/plotting.py
def feature_visualization(x, module_type, stage, n=32, save_dir=Path("runs/detect/exp")):
    """
    Visualize feature maps of a given model module during inference.

    Args:
        x (torch.Tensor): Features to be visualized.
        module_type (str): Module type.
        stage (int): Module stage within the model.
        n (int, optional): Maximum number of feature maps to plot. Defaults to 32.
        save_dir (Path, optional): Directory to save results. Defaults to Path('runs/detect/exp').
    """
    for m in ["Detect", "Pose", "Segment"]:
        if m in module_type:
            return
    _, channels, height, width = x.shape  # batch, channels, height, width
    if height > 1 and width > 1:
        f = save_dir / f"stage{stage}_{module_type.split('.')[-1]}_features.png"  # filename

        blocks = torch.chunk(x[0].cpu(), channels, dim=0)  # select batch index 0, block by channels
        n = min(n, channels)  # number of plots
        _, ax = plt.subplots(math.ceil(n / 8), 8, tight_layout=True)  # 8 rows x n/8 cols
        ax = ax.ravel()
        plt.subplots_adjust(wspace=0.05, hspace=0.05)
        for i in range(n):
            ax[i].imshow(blocks[i].squeeze())  # cmap='gray'
            ax[i].axis("off")

        LOGGER.info(f"Saving {f}... ({n}/{channels})")
        plt.savefig(f, dpi=300, bbox_inches="tight")
        plt.close()
        np.save(str(f.with_suffix(".npy")), x[0].cpu().numpy())  # npy save





생성됨 2023-11-12, μ—…λ°μ΄νŠΈλ¨ 2024-01-05
μž‘μ„±μž: glenn-jocher (4), Laughing-q (1)