主頁 >  其他 > 【YOLOV5-5.0 原始碼解讀】plots.py

【YOLOV5-5.0 原始碼解讀】plots.py

2021-08-04 07:29:49 其他

目錄

  • 前言
  • 0、匯入需要的包和基本配置
  • 1、Colors
  • 2、plot_one_box、plot_one_box_PIL
    • 2.1、plot_one_box
    • 2.2、plot_one_box_PIL(沒用到)
  • 3、plot_wh_methods(沒用到)
  • 4、output_to_target、plot_images
    • 4.1、output_to_target
    • 4.1、plot_images
  • 5、plot_lr_scheduler
  • 6、hist2d、plot_test_txt、plot_targets_txt
    • 6.1、hist2d
    • 6.2、plot_test_txt
    • 6.3、plot_targets_txt(沒用到)
  • 7、plot_labels
  • 8、plot_evolution
  • 9、plot_results、plot_results_overlay、butter_lowpass_filtfilt
    • 9.1、plot_results
    • 9.2、plot_results_overlay
    • 9.3、butter_lowpass_filtfilt
  • 10、feature_visualization
  • 11、plot_study_txt(沒用到)、profile_idetection(沒用到)
  • 總結

前言

原始碼: YOLOv5原始碼.
導航: 【YOLOV5-5.0 原始碼講解】整體專案檔案導航.

\qquad 這個檔案都是一些畫圖函式,是一個工具類,代碼本身邏輯并不難,主要是一些包的函式可能大家沒見過,這里我總結了一些畫圖包的一些常見的畫圖函式: 【Opencv、ImageDraw、Matplotlib、Pandas、Seaborn】一些常見的畫圖函式,如果在下面代碼中碰到不太熟的畫圖函式,可以查一下我的筆記或者自己百度一下,

0、匯入需要的包和基本配置

import glob   # 僅支持部分通配符的檔案搜索模塊
import math   # 數學公式模塊
import os     # 與作業系統進行互動的模塊
from copy import copy  # 提供通用的淺層和深層copy操作
from pathlib import Path  # Path將str轉換為Path物件 使字串路徑易于操作的模塊

import cv2    # opencv庫
import matplotlib  # matplotlib模塊
import matplotlib.pyplot as plt  # matplotlib畫圖模塊
import numpy as np   # numpy矩陣處理函式庫
import pandas as pd  # pandas矩陣操作模塊
import seaborn as sn  # 基于matplotlib的圖形可視化python包 能夠做出各種有吸引力的統計圖表
import torch  # pytorch框架
import yaml   # yaml組態檔讀寫模塊
from PIL import Image, ImageDraw, ImageFont  # 圖片操作模塊
from torchvision import transforms  # 包含很多種對影像資料進行變換的函式

from utils.general import increment_path, xywh2xyxy, xyxy2xywh
from utils.metrics import fitness

# 設定一些基本的配置  Settings
matplotlib.rc('font', **{'size': 11})  # 自定義matplotlib圖上字體font大小size=11
# 在PyCharm 頁面中控制繪圖顯示與否
# 如果這句話放在import matplotlib.pyplot as plt之前就算加上plt.show()也不會再螢屏上繪圖 放在之后其實沒什么用
matplotlib.use('Agg')  # for writing to files only

1、Colors

\qquad 這是一個顏色類,用于選擇相應的顏色,比如畫框線的顏色,字體顏色等等,

Colors類代碼:

class Colors:
    # Ultralytics color palette https://ultralytics.com/
    def __init__(self):
        # hex = matplotlib.colors.TABLEAU_COLORS.values()
        hex = ('FF3838', 'FF9D97', 'FF701F', 'FFB21D', 'CFD231', '48F90A', '92CC17', '3DDB86', '1A9334', '00D4BB',
               '2C99A8', '00C2FF', '344593', '6473FF', '0018EC', '8438FF', '520085', 'CB38FF', 'FF95C8', 'FF37C7')
        # 將hex串列中所有hex格式(十六進制)的顏色轉換rgb格式的顏色
        self.palette = [self.hex2rgb('#' + c) for c in hex]
        # 顏色個數
        self.n = len(self.palette)

    def __call__(self, i, bgr=False):
        # 根據輸入的index 選擇對應的rgb顏色
        c = self.palette[int(i) % self.n]
        # 回傳選擇的顏色 默認是rgb
        return (c[2], c[1], c[0]) if bgr else c

    @staticmethod
    def hex2rgb(h):  # rgb order (PIL)
        # hex -> rgb
        return tuple(int(h[1 + i:1 + i + 2], 16) for i in (0, 2, 4))
        
colors = Colors()  # 初始化Colors物件 下面呼叫colors的時候會呼叫__call__函式

使用起來也是比較簡單只要直接輸入顏色序號即可:
在這里插入圖片描述

2、plot_one_box、plot_one_box_PIL

\qquad plot_one_box 和 plot_one_box_PIL 這兩個函式都是用于在原圖im上畫一個bounding box,區別在于前者使用的是opencv畫框,后者使用PIL畫框,這兩個函式的功能其實是重復的,其實我們用的比較多的是plot_one_box函式,plot_one_box_PIL幾乎沒用,了解下即可,

2.1、plot_one_box

\qquad 這個函式通常用在檢測nms后(detect.py中)將最終的預測bounding box在原圖中畫出來,不過這個函式依次只能畫一個框框,

plot_one_box函式代碼:

def plot_one_box(x, im, color=(128, 128, 128), label=None, line_thickness=3):
    """一般會用在detect.py中在nms之后變數每一個預測框,再將每個預測框畫在原圖上
    使用opencv在原圖im上畫一個bounding box
    :params x: 預測得到的bounding box  [x1 y1 x2 y2]
    :params im: 原圖 要將bounding box畫在這個圖上  array
    :params color: bounding box線的顏色
    :params labels: 標簽上的框框資訊  類別 + score
    :params line_thickness: bounding box的線寬
    """
    # check im記憶體是否連續
    assert im.data.contiguous, 'Image not contiguous. Apply np.ascontiguousarray(im) to plot_on_box() input image.'
    # tl = 框框的線寬  要么等于line_thickness要么根據原圖im長寬資訊自適應生成一個
    tl = line_thickness or round(0.002 * (im.shape[0] + im.shape[1]) / 2) + 1  # line/font thickness
    # c1 = (x1, y1) = 矩形框的左上角   c2 = (x2, y2) = 矩形框的右下角
    c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3]))
    # cv2.rectangle: 在im上畫出框框   c1: start_point(x1, y1)  c2: end_point(x2, y2)
    # 注意: 這里的c1+c2可以是左上角+右下角  也可以是左下角+右上角都可以
    cv2.rectangle(im, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA)
    # 如果label不為慷訓要在框框上面顯示標簽label + score
    if label:
        tf = max(tl - 1, 1)  # label字體的線寬 font thickness
        # cv2.getTextSize: 根據輸入的label資訊計算文本字串的寬度和高度
        # 0: 文字字體型別  fontScale: 字體縮放系數  thickness: 字體筆畫線寬
        # 回傳retval 字體的寬高 (width, height), baseLine 相對于最底端文本的 y 坐標
        t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0]
        c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3
        # 同上面一樣是個畫框的步驟  但是線寬thickness=-1表示整個矩形都填充color顏色
        cv2.rectangle(im, c1, c2, color, -1, cv2.LINE_AA)  # filled
        # cv2.putText: 在圖片上寫文本 這里是在上面這個矩形框里寫label + score文本
        # (c1[0], c1[1] - 2)文本左下角坐標  0: 文字樣式  fontScale: 字體縮放系數
        # [225, 255, 255]: 文字顏色  thickness: tf字體筆畫線寬     lineType: 線樣式
        cv2.putText(im, label, (c1[0], c1[1] - 2), 0, tl / 3, [225, 255, 255], thickness=tf, lineType=cv2.LINE_AA)

這個函式一般會用在detect.py中在nms之后變數每一個預測框,再將每個預測框畫在原圖上如:
在這里插入圖片描述
效果如下所示:
在這里插入圖片描述

2.2、plot_one_box_PIL(沒用到)

\qquad 這個函式是用PIL在原圖中畫一個框,作用和plot_one_box一樣,而且我們一般都是用plot_one_box而不用這個函式,所以了解下即可,

plot_one_box_PIL函式代碼:

def plot_one_box_PIL(box, im, color=(128, 128, 128), label=None, line_thickness=None):
    """
    使用PIL在原圖im上畫一個bounding box
    :params box: 預測得到的bounding box  [x1 y1 x2 y2]
    :params im: 原圖 要將bounding box畫在這個圖上  array
    :params color: bounding box線的顏色
    :params label: 標簽上的bounding box框框資訊  類別 + score
    :params line_thickness: bounding box的線寬
    """
    # 將原圖array格式->Image格式
    im = Image.fromarray(im)
    # (初始化)創建一個可以在給定影像(im)上繪圖的物件, 在之后呼叫draw.函式的時候不需要傳入im引數,它是直接針對im上進行繪畫的
    draw = ImageDraw.Draw(im)
    # 設定繪制bounding box的線寬
    line_thickness = line_thickness or max(int(min(im.size) / 200), 2)
    # 在im影像上繪制bounding box
    # xy: box [x1 y1 x2 y2] 左上角 + 右下角  width: 線寬  outline: 矩形外框顏色color  fill: 將整個矩形填充顏色color
    # outline和fill一般根據需求二選一
    draw.rectangle(box, width=line_thickness, outline=color)  # plot
    # 如果label不為慷訓要在框框上面顯示標簽label + score
    if label:
        # 加載一個TrueType或者OpenType字體檔案("Arial.ttf"), 并且創建一個字體物件font, font寫出的字體大小size=12
        font = ImageFont.truetype("Arial.ttf", size=max(round(max(im.size) / 40), 12))
        # 回傳給定文本label的寬度txt_width和高度txt_height
        txt_width, txt_height = font.getsize(label)
        # 在im影像上繪制矩形框 整個框框填充顏色color(用來存放label資訊)  [x1 y1 x2 y2]  左上角 + 右下角
        draw.rectangle([box[0], box[1] - txt_height + 4, box[0] + txt_width, box[1]], fill=color)
        # 在上面這個矩形中寫入text資訊(label)  x1y1 左上角
        draw.text((box[0], box[1] - txt_height + 1), label, fill=(255, 255, 255), font=font)

    # 再回傳array型別的im(繪好bounding box和label的)
    return np.asarray(im)

3、plot_wh_methods(沒用到)

\qquad 這個函式主要是用來比較 y a = e x y_a = e^x ya?=ex y b = ( 2 ? s i g m o i d ( x ) ) 2 y_b = (2 * sigmoid(x))^2 yb?=(2?sigmoid(x))2 y c = ( 2 ? s i g m o i d ( x ) ) 1.6 y_c = (2 * sigmoid(x))^{1.6} yc?=(2?sigmoid(x))1.6 這三個函式圖形的,其中 y a y_a ya? 是普通的yolo method, y b y_b yb? y c y_c yc?是作者提出的powe method方法,在 https://github.com/ultralytics/yolov3/issues/168.中,作者由討論過這個issue,作者在實驗中發現使用原來的yolo method損失計算有時候會突然迅速走向無限None值, 而power method方式計算wh損失下降會比較平穩,最后實驗證明 y b y_b yb? 是最好的wh損失計算方式, yolov5-5.0的wh損失計算代碼用的就是 y b y_b yb? 計算方式 如:

yolo.py:
在這里插入代碼片
loss.py:
在這里插入圖片描述
plot_wh_methods函式代碼:

def plot_wh_methods():
    """沒用到
    比較ya=e^x、yb=(2 * sigmoid(x))^2 以及 yc=(2 * sigmoid(x))^1.6 三個圖形
    wh損失計算的方式ya、yb、yc三種  ya: yolo method  yb/yc: power method
    實驗發現使用原來的yolo method損失計算有時候會突然迅速走向無限None值, 而power method方式計算wh損失下降會比較平穩
    最后實驗證明yb是最好的wh損失計算方式, yolov5-5.0的wh損失計算代碼用的就是yb計算方式
    Compares the two methods for width-height anchor multiplication
    https://github.com/ultralytics/yolov3/issues/168
    """
    x = np.arange(-4.0, 4.0, .1)  # (-4.0, 4.0) 每隔0.1取一個值
    ya = np.exp(x)  # ya = e^x  yolo method
    yb = torch.sigmoid(torch.from_numpy(x)).numpy() * 2  # yb = 2 * sigmoid(x)

    fig = plt.figure(figsize=(6, 3), tight_layout=True)  # 創建自定義影像 初始化畫布
    plt.plot(x, ya, '.-', label='YOLOv3')  # 繪制折線圖  可以任意加幾條線
    plt.plot(x, yb ** 2, '.-', label='YOLOv5 ^2')
    plt.plot(x, yb ** 1.6, '.-', label='YOLOv5 ^1.6')
    plt.xlim(left=-4, right=4)  # 設定x軸、y軸范圍
    plt.ylim(bottom=0, top=6)
    plt.xlabel('input')  # 設定x軸、y軸標簽
    plt.ylabel('output')
    plt.grid()  # 生成網格
    plt.legend()  # 加上圖例 如果是折線圖,需要在plt.plot中加入label引數(圖例名)
    fig.savefig('comparison.png', dpi=200)  # plt繪完圖, fig.savefig()保存圖片

\qquad 其實這個函式倒不是特別重要,只是可視化一下這三個函式,看看他們的區別,在代碼中也沒呼叫過這個函式,但是了解這種新型 wh 損失計算的方式(Power Method)還是很有必要的,

4、output_to_target、plot_images

\qquad 這兩個函式其實也是對檢測到的目標格式進行處理(output_to_target)然后再將其畫框顯示在原圖上(plot_images),不過這兩個函式是用在test.py中的,針對的也不再是一張圖片一個框,而是整個batch中的所有框,而且plot_images會將整個batch的圖片都畫在一張大圖mosaic中,畫不下的就洗掉,這些都是plot_images函式和plot_one_box的區別,

4.1、output_to_target

\qquad 這個函式是用于將經過nms后的output [num_obj,x1y1x2y2+conf+cls] -> [num_obj,batch_id+class+xywh+conf],并不涉及畫圖操作,而是轉化predict的格式,通常放在畫圖操作plot_images之前,

output_to_target函式代碼:

def output_to_target(output):
    """用在test.py中進行繪制前3個batch的預測框predictions 因為只有predictions需要修改格式 target是不需要修改格式的
    將經過nms后的output [num_obj,x1y1x2y2+conf+cls] -> [num_obj, batch_id+class+x+y+w+h+conf] 轉變格式
    以便在plot_images中進行繪圖 + 顯示label
    Convert model output to target format [batch_id, class_id, x, y, w, h, conf]
    :params output: list{tensor(8)}分別對應著當前batch的8(batch_size)張圖片做完nms后的結果
                    list中每個tensor[n, 6]  n表示當前圖片檢測到的目標個數  6=x1y1x2y2+conf+cls
    :return np.array(targets): [num_targets, batch_id+class+xywh+conf]  其中num_targets為當前batch中所有檢測到目標框的個數
    """
    targets = []
    for i, o in enumerate(output):   # 對每張圖片分別做處理
        for *box, conf, cls in o.cpu().numpy():  # 對每張圖片的每個檢測到的目標框進行convert格式
            targets.append([i, cls, *list(*xyxy2xywh(np.array(box)[None])), conf])
    return np.array(targets)

4.1、plot_images

\qquad 這個函式是用來繪制一個batch的所有圖片的框框(真實框或預測框),使用在test.py中,且在output_to_target函式之后,而且這個函式是將一個batch的圖片都放在一個大圖mosaic上面,放不下洗掉,

plot_images函式代碼:

def plot_images(images, targets, paths=None, fname='images.jpg', names=None, max_size=640, max_subplots=16):
    """用在test.py中進行繪制前3個batch的ground truth和預測框predictions(兩個圖) 一起保存 或者train.py中
    將整個batch的labels都畫在這個batch的images上
    Plot image grid with labels
    :params images: 當前batch的所有圖片  Tensor [batch_size, 3, h, w]  且圖片都是歸一化后的
    :params targets:  直接來自target: Tensor[num_target, img_index+class+xywh]  [num_target, 6]
                      來自output_to_target: Tensor[num_pred, batch_id+class+xywh+conf] [num_pred, 7]
    :params paths: tuple  當前batch中所有圖片的地址
                   如: '..\\datasets\\coco128\\images\\train2017\\000000000315.jpg'
    :params fname: 最終保存的檔案路徑 + 名字  runs\train\exp8\train_batch2.jpg
    :params names: 傳入的類名 從class index可以相應的key值  但是默認是None 只顯示class index不顯示類名
    :params max_size: 圖片的最大尺寸640  如果images有圖片的大小(w/h)大于640則需要resize 如果都是小于640則不需要resize
    :params max_subplots: 最大子圖個數 16
    :params mosaic: 一張大圖  最多可以顯示max_subplots張圖片  將總多的圖片(包括各自的label框框)一起貼在一起顯示
                    mosaic每張圖片的左上方還會顯示當前圖片的名字  最好以fname為名保存起來
    """
    if isinstance(images, torch.Tensor):
        images = images.cpu().float().numpy()  # tensor -> numpy array
    if isinstance(targets, torch.Tensor):
        targets = targets.cpu().numpy()

    # 反歸一化 將歸一化后的圖片還原  un-normalise
    if np.max(images[0]) <= 1:
        images *= 255

    # 設定一些基礎變數
    tl = 3  # 設定線寬 line thickness  3
    tf = max(tl - 1, 1)  # 設定字體筆畫線寬 font thickness  2
    bs, _, h, w = images.shape  # batch size 4, channel 3, height 512, width 512
    bs = min(bs, max_subplots)  # 子圖總數  正方形  limit plot images  4
    ns = np.ceil(bs ** 0.5)  # ns=每行/每列最大子圖個數  子圖總數=ns*ns ceil向上取整  2

    # Check if we should resize
    # 如果images有圖片的大小(w/h)大于640則需要resize 如果都是小于640則不需要resize
    scale_factor = max_size / max(h, w)  # 1.25
    if scale_factor < 1:
        # 如果w/h有任何一條邊超過640, 就要將較長邊縮放到640, 另外一條邊相應也縮放
        h = math.ceil(scale_factor * h)  # 512
        w = math.ceil(scale_factor * w)  # 512

    # np.full 回傳一個指定形狀、型別和數值的陣列
    # shape: (int(ns * h), int(ns * w), 3) (1024, 1024, 3)  填充的值: 255   dtype 填充型別: np.uint8
    mosaic = np.full((int(ns * h), int(ns * w), 3), 255, dtype=np.uint8)  # init
    # 對batch內每張圖片
    for i, img in enumerate(images):   # img (3, 512, 512)
        # 如果圖片要超過max_subplots我們就不管了
        if i == max_subplots:  # if last batch has fewer images than we expect
            break

        # (block_x, block_y) 相當于是左上角的左邊
        block_x = int(w * (i // ns))  # // 取整  0   0   512  512    ns=2
        block_y = int(h * (i % ns))   # % 取余   0  512   0   512

        img = img.transpose(1, 2, 0)  # (512, 512, 3)  h w c
        if scale_factor < 1:  # 如果scale_factor < 1說明h/w超過max_size 需要resize回來
            img = cv2.resize(img, (w, h))

        # 將這個batch的圖片一張張的貼到mosaic相應的位置上  hwc  這里最好自己畫個圖理解下
        # 第一張圖mosaic[0:512, 0:512, :] 第二張圖mosaic[512:1024, 0:512, :]
        # 第三張圖mosaic[0:512, 512:1024, :] 第四張圖mosaic[512:1024, 512:1024, :]
        mosaic[block_y:block_y + h, block_x:block_x + w, :] = img
        if len(targets) > 0:
            # 求出屬于這張img的target
            image_targets = targets[targets[:, 0] == i]
            # 將這張圖片的所有target的xywh -> xyxy
            boxes = xywh2xyxy(image_targets[:, 2:6]).T
            # 得到這張圖片所有target的類別classes
            classes = image_targets[:, 1].astype('int')
            # 如果image_targets.shape[1] == 6則說明沒有置信度資訊(此時target實際上是真實框)
            # 如果長度為7則第7個資訊就是置信度資訊(此時target為預測框資訊)
            labels = image_targets.shape[1] == 6  # labels if no conf column
            # 得到當前這張圖的所有target的置信度資訊(pred) 如果沒有就為空(真實label)
            # check for confidence presence (label vs pred)
            conf = None if labels else image_targets[:, 6]

            if boxes.shape[1]:  # boxes.shape[1]不為空說明這張圖有target目標
                if boxes.max() <= 1.01:  # if normalized with tolerance 0.01
                    # 因為圖片是反歸一化的 所以這里boxes也反歸一化
                    boxes[[0, 2]] *= w   # scale to pixels
                    boxes[[1, 3]] *= h
                elif scale_factor < 1:
                    # 如果scale_factor < 1 說明resize過, 那么boxes也要相應變化
                    # absolute coords need scale if image scales
                    boxes *= scale_factor
            # 上面得到的boxes資訊是相對img這張圖片的標簽資訊 因為我們最終是要將img貼到mosaic上 所以還要變換label->mosaic
            boxes[[0, 2]] += block_x
            boxes[[1, 3]] += block_y

            # 將當前的圖片img的所有標簽boxes畫到mosaic上
            for j, box in enumerate(boxes.T):
                # 遍歷每個box
                cls = int(classes[j])  # 得到這個box的class index
                color = colors(cls)    # 得到這個box框線的顏色
                cls = names[cls] if names else cls  # 如果傳入類名就顯示類名 如果沒傳入類名就顯示class index

                # 如果labels不為空說明是在顯示真實target 不需要conf置信度 直接顯示label即可
                # 如果conf[j] > 0.25 首先說明是在顯示pred 且這個box的conf必須大于0.25 相當于又是一輪nms篩選 顯示label + conf
                if labels or conf[j] > 0.25:  # 0.25 conf thresh
                    label = '%s' % cls if labels else '%s %.1f' % (cls, conf[j])  # 框框上面的顯示資訊
                    plot_one_box(box, mosaic, label=label, color=color, line_thickness=tl)  # 一個個的畫框

        # 在mosaic每張圖片相對位置的左上角寫上每張圖片的檔案名 如000000000315.jpg
        if paths:
            # paths[i]: '..\\datasets\\coco128\\images\\train2017\\000000000315.jpg'  Path: str -> Wins地址
            # .name: str'000000000315.jpg'  [:40]取前40個字符  最侄訓是等于str'000000000315.jpg'
            label = Path(paths[i]).name[:40]  # trim to 40 char
            # 回傳文本 label 的寬高 (width, height)
            t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0]
            # 在mosaic上寫文本資訊
            # 要繪制的影像 +  要寫上前的文本資訊 + 文本左下角坐標 + 要使用的字體 + 字體縮放系數 + 字體的顏色 + 字體的線寬 + 矩形邊框的線型
            cv2.putText(mosaic, label, (block_x + 5, block_y + t_size[1] + 5), 0,
                        tl / 3, [220, 220, 220], thickness=tf, lineType=cv2.LINE_AA)

        # mosaic內每張圖片與圖片之間弄一個邊界框隔開 好看點 其實做法特簡單 就是將每個img在mosaic中畫個框即可
        cv2.rectangle(mosaic, (block_x, block_y), (block_x + w, block_y + h), (255, 255, 255), thickness=3)

    # 最后一步 check是否需要將mosaic圖片保存起來
    if fname:   # 檔案名不為空的話 fname = runs\train\exp8\train_batch2.jpg
        # 限制mosaic圖片尺寸
        r = min(1280. / max(h, w) / ns, 1.0)  # ratio to limit image size
        mosaic = cv2.resize(mosaic, (int(ns * w * r), int(ns * h * r)), interpolation=cv2.INTER_AREA)
        # cv2.imwrite(fname, cv2.cvtColor(mosaic, cv2.COLOR_BGR2RGB))  # cv2 save  最好BGR -> RGB再保存
        Image.fromarray(mosaic).save(fname)  # PIL save  必須要numpy array -> tensor格式 才能保存
    return mosaic

這兩個函式都是用在test.py函式中的:

在這里插入圖片描述
用在train.py:
在這里插入圖片描述

執行效果test.py(target):
在這里插入圖片描述執行效果test.py(predict):
在這里插入圖片描述

5、plot_lr_scheduler

\qquad 這個函式是用來畫出在訓練程序中每個epoch的學習率變化情況,

plot_lr_scheduler函式代碼:

def plot_lr_scheduler(optimizer, scheduler, epochs=300, save_dir=''):
    """用在train.py中學習率設定后可視化一下
    Plot LR simulating training for full epochs
    :params optimizer: 優化器
    :params scheduler: 策略調整器
    :params epochs: x
    :params save_dir: lr圖片 保存地址
    """
    optimizer, scheduler = copy(optimizer), copy(scheduler)  # do not modify originals
    y = []  # 存放每個epoch的學習率

    # 從optimizer中取學習率 一個epoch取一個 共取epochs個 每取一次需要使用scheduler.step更新下一個epoch的學習率
    for _ in range(epochs):
        scheduler.step()  # 更新下一個epoch的學習率
        # ptimizer.param_groups[0]['lr']: 取下一個epoch的學習率lr
        y.append(optimizer.param_groups[0]['lr'])
    plt.plot(y, '.-', label='LR')  # 沒有傳入x 默認會傳入 0..epochs-1
    plt.xlabel('epoch')
    plt.ylabel('LR')
    plt.grid()
    plt.xlim(0, epochs)
    plt.ylim(0)
    plt.savefig(Path(save_dir) / 'LR.png', dpi=200)  # 保存
    plt.close()

通常用于train.py中學習率設定后可視化一下:
在這里插入圖片描述
執行效果:

在這里插入圖片描述

6、hist2d、plot_test_txt、plot_targets_txt

6.1、hist2d

\qquad 這個函式是使用numpy工具畫出2d直方圖,不過好像用的不多,大多數都是呼叫工具包封裝好的2d直方圖方法,所以這個包其實只在plot_evolution函式和plot_test_txt函式中使用,

hist2d函式代碼:

def hist2d(x, y, n=100):
    """用在plot_evolution和plot_test_txt
    使用numpy畫出2d直方圖
    2d histogram used in labels.png and evolve.png
    """
    # xedges: 回傳在start=x.min()和stop=x.max()之間回傳均勻間隔的n個資料
    xedges, yedges = np.linspace(x.min(), x.max(), n), np.linspace(y.min(), y.max(), n)
    # np.histogram2d: 2d直方圖  x: x軸坐標  y: y軸坐標  (xedges, yedges): bins  x, y軸的長條形數目
    # 回傳hist: 直方圖物件   xedges: x軸物件  yedges: y軸物件
    hist, xedges, yedges = np.histogram2d(x, y, (xedges, yedges))
    # np.clip: 截取函式 令目標內所有資料都屬于一個范圍 [0, hist.shape[0] - 1] 小于0的等于0 大于同理
    # np.digitize 用于磁區
    xidx = np.clip(np.digitize(x, xedges) - 1, 0, hist.shape[0] - 1)  # x軸坐標
    yidx = np.clip(np.digitize(y, yedges) - 1, 0, hist.shape[1] - 1)  # y軸坐標
    return np.log(hist[xidx, yidx])

6.2、plot_test_txt

\qquad 這個函式是利用test.py中生成的test.txt檔案(或者其他的*.txt檔案),畫出其xy直方圖和xy雙直方圖,其實這個plot_test_txt這個函式作者并沒有使用它,但是我們確實可以自己寫一個腳本來執行這個函式,觀察一下預測到的所有影像的目標情況(wh分布),

plot_test_txt函式代碼:

def plot_test_txt(test_dir='test.txt'):
    """可以自己寫個腳本執行test.txt檔案
    利用test.txt  xyxy畫出其直方圖和雙直方圖
    Plot test.txt histograms
    :params test_dir: test.py中生成的一些 save_dir/labels中的txt檔案
    """
    # x [:, xyxy]
    x = np.loadtxt(test_dir, dtype=np.float32)
    box = xyxy2xywh(x[:, 2:6])  # xyxy to xywh  這里我改了下 原來是0:4 但我發現txt中存放的是 cls+conf+xyxy
    cx, cy = box[:, 0], box[:, 1]  # x y

    # 將figure分成1行1列  figure size=(6, 6)  tight_layout=true 會自動調整子圖引數, 使之填充整個影像區域
    # 回傳figure(繪圖物件)和axes(坐標物件)
    fig, ax = plt.subplots(1, 1, figsize=(6, 6), tight_layout=True)
    # hist2d: 雙直方圖   cx: x坐標   cy: y坐標   bins: 橫豎分為幾條  cmax、cmin: 所有的bins的值少于cmin和大于cmax的不顯示
    ax.hist2d(cx, cy, bins=600, cmax=10, cmin=0)
    ax.set_aspect('equal')  # 設定兩個軸的長度始終相同  figure為正方形
    plt.savefig('hist2d.png', dpi=300)

    fig, ax = plt.subplots(1, 2, figsize=(12, 6), tight_layout=True)
    # hist 正常直方圖 cx: 繪圖資料  bins: 直方圖的長條形數目  normed: 是否將得到的直方圖向量歸一化
    #               facecolor: 長條形的顏色  edgecolor:長條形邊框的顏色  alpha:透明度
    ax[0].hist(cx, bins=600)
    ax[1].hist(cy, bins=600)
    plt.savefig('hist1d.png', dpi=200)

自己寫個腳本使用:
在這里插入圖片描述
hist1d.png效果(橫坐標分別是w和h,縱坐標是個數):
在這里插入圖片描述
hist2d.png效果(橫坐標是x縱坐標是y):
在這里插入圖片描述

6.3、plot_targets_txt(沒用到)

\qquad 這個函式是利用targets.txt(真實框的xywh)畫出其直方圖,但是并沒有使用這個函式,而且細心的可以發現這個函式和之后的plot_labels函式是重復的,所以這個函式就隨便看看吧,

plot_targets_txt函式代碼:

def plot_targets_txt():
    """沒用到 和plot_labels作用重復
    利用targets.txt  xywh畫出其直方圖
    Plot targets.txt histograms
    """
    # x [:, xywh]
    x = np.loadtxt('targets.txt', dtype=np.float32).T
    s = ['x targets', 'y targets', 'width targets', 'height targets']
    fig, ax = plt.subplots(2, 2, figsize=(8, 8), tight_layout=True)
    ax = ax.ravel()   # 將多維陣列降位一維
    for i in range(4):
        ax[i].hist(x[i], bins=100, label='%.3g +/- %.3g' % (x[i].mean(), x[i].std()))
        ax[i].legend()  # 顯示上行label圖例
        ax[i].set_title(s[i])
    plt.savefig('targets.jpg', dpi=200)

7、plot_labels

\qquad 這個函式是根據從datasets中取到的labels,分析其類別分布,畫出labels相關直方圖資訊,最侄訓生成labels_correlogram.jpg和labels.jpg兩張圖片,labels_correlogram.jpg包含所有標簽的 x,y,w,h的多變數聯合分布直方圖:查看兩個或兩個以上變數之間兩兩相互關系的可視化形式(里面包含x、y、w、h兩兩之間的分布直方圖),而labels.jpg包含了:ax[0]畫出classes的各個類的分布直方圖,ax[1]畫出所有的真實框;ax[2]畫出xy直方圖;ax[3]畫出wh直方圖,

plot_labels函式代碼:

def plot_labels(labels, names=(), save_dir=Path(''), loggers=None):
    """通常用在train.py中 加載資料datasets和labels后 對labels進行可視化 分析labels資訊
    plot dataset labels  生成labels_correlogram.jpg和labels.jpg   畫出資料集的labels相關直方圖資訊
    :params labels: 資料集的全部真實框標簽  (num_targets, class+xywh)  (929, 5)
    :params names: 資料集的所有類別名
    :params save_dir: runs\train\exp21
    :params loggers: 日志物件
    """
    print('Plotting labels... ')
    # c: classes (929)    b: boxes  xywh (4, 929)   .transpose() 將(4, 929) -> (929, 4)
    c, b = labels[:, 0], labels[:, 1:].transpose()
    nc = int(c.max() + 1)  # 類別總數 number of classes  80
    # pd.DataFrame: 創建DataFrame, 類似于一種excel, 表頭是['x', 'y', 'width', 'height']  表格資料: b中資料按行依次存盤
    x = pd.DataFrame(b.transpose(), columns=['x', 'y', 'width', 'height'])

    # 1、畫出labels的 xywh 各自聯合分布直方圖  labels_correlogram.jpg
    # seaborn correlogram  seaborn.pairplot  多變數聯合分布圖: 查看兩個或兩個以上變數之間兩兩相互關系的可視化形式
    # data: 聯合分布資料x   diag_kind:表示聯合分布圖中對角線圖的型別   kind:表示聯合分布圖中非對角線圖的型別
    # corner: True 表示只顯示左下側 因為左下和右上是重復的   plot_kws,diag_kws: 可以接受字典的引數,對圖形進行微調
    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)  # 保存labels_correlogram.jpg
    plt.close()

    # 2、畫出classes的各個類的分布直方圖ax[0], 畫出所有的真實框ax[1], 畫出xy直方圖ax[2], 畫出wh直方圖ax[3] labels.jpg
    matplotlib.use('svg')  # faster
    # 將整個figure分成2*2四個區域
    ax = plt.subplots(2, 2, figsize=(8, 8), tight_layout=True)[1].ravel()
    # 第一個區域ax[1]畫出classes的分布直方圖
    y = ax[0].hist(c, bins=np.linspace(0, nc, nc + 1) - 0.5, rwidth=0.8)
    # [y[2].patches[i].set_color([x / 255 for x in colors(i)]) for i in range(nc)]  # update colors bug #3195
    ax[0].set_ylabel('instances')  # 設定y軸label
    if 0 < len(names) < 30:  # 小于30個類別就把所有的類別名作為橫坐標
        ax[0].set_xticks(range(len(names)))   # 設定刻度
        ax[0].set_xticklabels(names, rotation=90, fontsize=10)  # 旋轉90度 設定每個刻度標簽
    else:
        ax[0].set_xlabel('classes')  # 如果類別數大于30個, 可能就放不下去了, 所以只顯示x軸label
    # 第三個區域ax[2]畫出xy直方圖     第四個區域ax[3]畫出wh直方圖
    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)

    # 第二個區域ax[1]畫出所有的真實框
    labels[:, 1:3] = 0.5  # center xy
    labels[:, 1:] = xywh2xyxy(labels[:, 1:]) * 2000  # xyxy
    img = Image.fromarray(np.ones((2000, 2000, 3), dtype=np.uint8) * 255)  # 初始化一個視窗
    for cls, *box in labels[:1000]:  # 把所有的框畫在img視窗中
        ImageDraw.Draw(img).rectangle(box, width=1, outline=colors(cls))  # plot
    ax[1].imshow(img)
    ax[1].axis('off')  # 不要xy軸

    # 去掉上下左右坐標系(去掉上下左右邊框)
    for a in [0, 1, 2, 3]:
        for s in ['top', 'right', 'left', 'bottom']:
            ax[a].spines[s].set_visible(False)

    plt.savefig(save_dir / 'labels.jpg', dpi=200)
    matplotlib.use('Agg')
    plt.close()

    # 列印日志 loggers
    for k, v in loggers.items() or {}:
        if k == 'wandb' and v:
            v.log({"Labels": [v.Image(str(x), caption=x.name) for x in save_dir.glob('*labels*.jpg')]}, commit=False)

這個函式一般會用在train.py的載入資料datasets和labels后,統計分析labels相關分布資訊:
在這里插入圖片描述
labels_correlogram.jpg執行效果:
在這里插入圖片描述

labels.jpg執行效果:
在這里插入圖片描述

8、plot_evolution

\qquad 這個函式用于超參進化的最后階段,負責畫出每個超參進化的結果,最侄訓生成evolve.png,里面是每個超參的進化情況以及相對應的mAP的散點圖,每5個超參一行,并且會用 ‘+’ 標注好最佳mAP對應的超參值,且每個散點圖會輸出最佳mAP對應的每個超參的最佳超參,而且plot_evolution也呼叫了上面的hist2d函式,

plot_evolution函式代碼:

def plot_evolution(yaml_file='data/hyp.finetune.yaml', save_dir=Path('')):
    """用在train.py的超參進化演算法后,輸出參超進化的結果
    超參進化在每一輪都會產生一系列的進化后的超參(存在yaml_file)  以及每一輪都會算出當前輪次的7個指標(evolve.txt)
    這個函式要做的就是把每個超參在所有輪次變化的值和maps以散點圖的形式顯示出來,并標出最大的map對應的超參值 一個超參一個散點圖
    :params yaml_file: 'runs/train/evolve/hyp_evolved.yaml'
    """
    with open(yaml_file) as f:
        hyp = yaml.safe_load(f)  # 匯入超參檔案
    # evolve.txt中每一行為一次進化的結果
    # 每行前七個數字(P, R, mAP, F1, test_losses(GIOU, obj, cls)) 之后為hyp
    x = np.loadtxt('evolve.txt', ndmin=2)
    f = fitness(x)  # 得到所有進化輪次后得到的加權形式的map
    # weights = (f - f.min()) ** 2  # for weighted results
    plt.figure(figsize=(10, 12), tight_layout=True)
    matplotlib.rc('font', **{'size': 8})  # 設定matplotlib引數 font_size: 8
    for i, (k, v) in enumerate(hyp.items()):
        y = x[:, i + 7]  # y=當前超參在每一輪進化后的值
        # mu = (y * weights).sum() / weights.sum()  # best weighted result
        mu = y[f.argmax()]  # 得到加權map最大的epoch時的超參(認為這個超參為所有輪次的最佳超參)
        plt.subplot(6, 5, i + 1)  # 假設有30個引數  6行5列  一個部分畫一個圖
        # 畫出每個超參變化的散點圖  x: x坐標為當前超參每一輪進化后的值y  y: y坐標為所有進化輪次后得到的加權形式的map
        # c: 色彩或顏色   cmap: Colormap實體  alpha:    edgecolors: 邊框顏色
        plt.scatter(y, f, c=hist2d(y, f, 20), cmap='viridis', alpha=.8, edgecolors='none')
        # 在當前小圖上再畫出最佳map時對應的超參  大大的 '+' 做記號
        plt.plot(mu, f.max(), 'k+', markersize=15)
        plt.title('%s = %.3g' % (k, mu), fontdict={'size': 9})  # limit to 40 characters
        if i % 5 != 0:  # 一行只能畫5個小圖
            plt.yticks([])
        print('%15s: %.3g' % (k, mu))  # 輸出最佳超參
    plt.savefig(save_dir / 'evolve.png', dpi=200)  # 保存evolve.png
    print('\nPlot saved as evolve.png')

這個函式通常會用在train.py的超參進化演算法后,輸出參超進化的結果:
在這里插入圖片描述
函式執行效果evolve.png:
在這里插入圖片描述

9、plot_results、plot_results_overlay、butter_lowpass_filtfilt

\qquad 這三個函式都是用來對result.txt中的各項指標進行可視化的,但是plot_results是將一個指標畫在折線圖上(共10個折線圖),而 plot_results_overlay要做的是將原先的10個顯示的指標,兩個兩個進行對比畫在同一個折線圖上(共5個折線圖),最后的butter_lowpass_filtfilt函式

9.1、plot_results

\qquad 這個函式是將訓練后的結果 results.txt 中相關的訓練指標畫出來,

\qquad result.txt中一行的元素分別有:當前epoch/總epochs-1 、當前的顯存容量mem、box回歸損失、obj置信度損失、cls分類損失、訓練總損失、真實目標數量num_target、圖片尺寸img_shape、Precision、Recall、map@0.5、map@0.5:0.95、測驗box回歸損失、測驗obj置信度損、測驗cls分類損失,

\qquad 在result.txt中畫出的指標有:訓練回歸損失Box、訓練置信度損失Objectness、訓練分類損失Classification、Precision、Recall、驗證回歸損失 val Box、驗證置信度損失val Objectness、驗證分類損失val Classification、mAP@0.5、mAP@0.5:0.95,

plot_results函式代碼:

def plot_results(start=0, stop=0, bucket='', id=(), save_dir=''):
    """'用在訓練結束, 對訓練結果進行可視化
    畫出訓練完的 results.txt Plot training 'results*.txt' 最終生成results.png
    :params start: 讀取資料的開始epoch   因為result.txt的資料是一個epoch一行的
    :params stop: 讀取資料的結束epoch
    :params bucket: 是否需要從googleapis中下載results*.txt檔案
    :params id: 需要從googleapis中下載的results + id.txt 默認為空
    :params save_dir: 'runs\train\exp22'
    """
    # 建造一個figure 分割成2行5列, 由10個小subplots組成
    fig, ax = plt.subplots(2, 5, figsize=(12, 6), tight_layout=True)
    ax = ax.ravel()  # 將多維陣列降為一維
    s = ['Box', 'Objectness', 'Classification', 'Precision', 'Recall',
         'val Box', 'val Objectness', 'val Classification', 'mAP@0.5', 'mAP@0.5:0.95']   # titles

    if bucket:
        # files = ['https://storage.googleapis.com/%s/results%g.txt' % (bucket, x) for x in id]
        files = ['results%g.txt' % x for x in id]
        c = ('gsutil cp ' + '%s ' * len(files) + '.') % tuple('gs://%s/results%g.txt' % (bucket, x) for x in id)  # cmd指令
        os.system(c)  # 使用cmd指令從googleapis中下載results*.txt
    else:
        # 不從網盤上下載就直接從檔案目錄中模糊查找   如files=[WindowsPath('runs/train/exp22/results.txt')]
        files = list(Path(save_dir).glob('results*.txt'))  # 搜索save_dir目錄下類似'results*.txt'檔案名的檔案
    assert len(files), 'No results.txt files found in %s, nothing to plot.' % os.path.abspath(save_dir)

    # 讀取files檔案資料進行可視化
    for fi, f in enumerate(files):
        try:
            # files 原始一行: epoch/epochs - 1, memory, Box, Objectness, Classification, sum_loss, targets.shape[0], img_shape, Precision, Recall, map@0.5, map@0.5:0.95, Val Box, Val Objectness, Val Classification
            # 只使用[2, 3, 4, 8, 9, 12, 13, 14, 10, 11]列 (10, 1) 分布對應 =>
            # [Box, Objectness, Classification, Precision, Recall, Val Box, Val Objectness, Val Classification, map@0.5, map@0.5:0.95]
            results = np.loadtxt(f, usecols=[2, 3, 4, 8, 9, 12, 13, 14, 10, 11], ndmin=2).T  # (10, 1)
            n = results.shape[1]  # number of rows 1
            # 根據start(epoch)和stop(epoch)讀取相應的輪次的資料
            x = range(start, min(stop, n) if stop else n)
            for i in range(10):  # 分別可視化這10個指標
                y = results[i, x]
                if i in [0, 1, 2, 5, 6, 7]:
                    y[y == 0] = np.nan  # loss值不能為0 要顯示為np.nan
                    # y /= y[0]  # normalize
                # label = labels[fi] if len(labels) else f.stem
                ax[i].plot(x, y, marker='.', linewidth=2, markersize=8)  # 畫子圖
                # ax[i].plot(x, y, marker='.', label=label, linewidth=2, markersize=8)
                ax[i].set_title(s[i])  # 設定子圖示題
                # if i in [5, 6, 7]:  # share train and val loss y axes
                #     ax[i].get_shared_y_axes().join(ax[i], ax[i - 5])
        except Exception as e:
            print('Warning: Plotting error for %s; %s' % (f, e))

    # ax[1].legend()
    fig.savefig(Path(save_dir) / 'results1.png', dpi=200)  # 保存results.png

這個函式會用在train.py訓練結束后對訓練結果進行可視化:
在這里插入圖片描述
執行結果result1.png:
在這里插入圖片描述

9.2、plot_results_overlay

\qquad 這個函式還是將result.txt檔案中的各項指標進行可視化,不過將原先的10個折線圖減為5個折線圖, train和val兩兩相對比,
plot_results_overlay函式代碼:

def plot_results_overlay(start=0, stop=0):
    """可以用在train.py或者自寫一個檔案
    畫出訓練完的 results.txt Plot training 'results*.txt' 而且將原先的10個折線圖縮減為5個折線圖, train和val相對比
    Plot training 'results*.txt', overlaying train and val losses
    """
    s = ['train', 'train', 'train', 'Precision', 'mAP@0.5', 'val', 'val', 'val', 'Recall', 'mAP@0.5:0.95']  # legends
    t = ['Box', 'Objectness', 'Classification', 'P-R', 'mAP-F1']  # titles

    # 遍歷每個模糊查詢匹配到的results*.txt
    for f in sorted(glob.glob('results*.txt') + glob.glob('../../Downloads/results*.txt')):
        # files 原始一行: epoch/epochs - 1, memory, Box, Objectness, Classification, sum_loss, targets.shape[0], img_shape, Precision, Recall, map@0.5, map@0.5:0.95, Val Box, Val Objectness, Val Classification
        # 只使用[2, 3, 4, 8, 9, 12, 13, 14, 10, 11]列 (10, 1) 分布對應 =>
        # [Box, Objectness, Classification, Precision, Recall, Val Box, Val Objectness, Val Classification, map@0.5, map@0.5:0.95]
        results = np.loadtxt(f, usecols=[2, 3, 4, 8, 9, 12, 13, 14, 10, 11], ndmin=2).T   # (10, 1)
        n = results.shape[1]  # number of rows  1
        # 根據start(epoch)和stop(epoch)讀取相應的輪次的資料
        x = range(start, min(stop, n) if stop else n)
        # 建造一個figure 分割成1行5列, 由5個小subplots組成 [Box, Objectness, Classification, P-R, mAP-F1]
        fig, ax = plt.subplots(1, 5, figsize=(14, 3.5), tight_layout=True)
        ax = ax.ravel()  # 將多維陣列降為一維

        # 分別可視化這5個指標 [Box, Objectness, Classification, P-R, mAP-F1]
        for i in range(5):
            for j in [i, i + 5]:  # 每個指標都要讀取train(i) + val(i+5)兩個值
                y = results[j, x]
                ax[i].plot(x, y, marker='.', label=s[j])
                # y_smooth = butter_lowpass_filtfilt(y)  # y抖動太大就取一個平滑版本
                # ax[i].plot(x, np.gradient(y_smooth), marker='.', label=s[j])

            ax[i].set_title(t[i])  # 設定子圖示題
            ax[i].legend()  # 設定子圖圖例legend
            ax[i].set_ylabel(f) if i == 0 else None  # add filename
        fig.savefig(f.replace('.txt', '.png'), dpi=200)  # 保存result.png

這個函式可以放在plot_results下面,也可以自己寫一個:
在這里插入圖片描述
執行結果result.py:
在這里插入圖片描述

9.3、butter_lowpass_filtfilt

\qquad 這個函式是為了防止在訓練時有些指標非常的抖動,導致畫出來很難看,比如下面這種情況:
在這里插入圖片描述
紅色部分真實值非常抖動,畫出來很難看,那我們就對它進行一個平滑處理,取它的一個近似值,

butter_lowpass_filtfilt函式代碼:

def butter_lowpass_filtfilt(data, cutoff=1500, fs=50000, order=5):
    """
    當data值抖動太大, 就取data的平滑曲線
    """
    from scipy.signal import butter, filtfilt

    # https://stackoverflow.com/questions/28536191/how-to-filter-smooth-with-scipy-numpy
    def butter_lowpass(cutoff, fs, order):
        nyq = 0.5 * fs
        normal_cutoff = cutoff / nyq
        return butter(order, normal_cutoff, btype='low', analog=False)

    b, a = butter_lowpass(cutoff, fs, order=order)
    return filtfilt(b, a, data)  # forward-backward filter

\qquad 這部分的代碼是用在上面plot_results_overlay函式里面的,不過它是注釋掉的,如果發現自己訓練的結果發生上面的那種抖動情況,大家可以打開注釋,或者任意呼叫這個函式達到一種平滑效果,這個函式代碼我是每有看的,感興趣可以自己讀讀,就幾行應該不難,

10、feature_visualization

\qquad 這個函式是用來可視化feature map的,而且可以實作可視化網路中任意一層的feature map,

函式代碼:

def feature_visualization(x, module_type, stage, n=64):
    """用在yolo.py的Model類中的forward_once函式中 自行選擇任意層進行可視化該層feature map
    可視化feature map(模型任意層都可以用)
    :params x: Features map   [bs, channels, height, width]
    :params module_type: Module type
    :params stage: Module stage within model
    :params n: Maximum number of feature maps to plot
    """
    batch, channels, height, width = x.shape  # batch, channels, height, width
    if height > 1 and width > 1:
        project, name = 'runs/features', 'exp'
        save_dir = increment_path(Path(project) / name)  # increment run
        save_dir.mkdir(parents=True, exist_ok=True)  # make save dir

        plt.figure(tight_layout=True)
        # torch.chunk: 與torch.cat()原理相反 將tensor x按dim(行或列)分割成channels個tensor塊, 回傳的是一個元組
        # 將第2個維度(channels)將x分成channels份  每張圖有三個block batch張圖  blocks=len(blocks)=3*batch
        blocks = torch.chunk(x, channels, dim=1)
        n = min(n, len(blocks))  # 總共可視化的feature map數量
        for i in range(n):
            feature = transforms.ToPILImage()(blocks[i].squeeze())  # tensor -> PIL Image
            ax = plt.subplot(int(math.sqrt(n)), int(math.sqrt(n)), i + 1)  # 根號n行根號n列  當前屬于第i+1張子圖
            ax.axis('off')
            plt.imshow(feature)  # cmap='gray' 可視化當前feature map

        f = f"stage_{stage}_{module_type.split('.')[-1]}_features.png"
        print(f'Saving {save_dir / f}...')
        plt.savefig(save_dir / f, dpi=300)

通常這個函式是把他放在yolo.py的Model類中的forward_once函式中:
在這里插入圖片描述
自己可以在if中選擇要查看的任意一層feature map,

原圖:
在這里插入圖片描述
執行效果:
在這里插入圖片描述

11、plot_study_txt(沒用到)、profile_idetection(沒用到)

\qquad 剩下的這兩個函式是沒什么用的,plot_study_txt是test.py中opt.task == 'study’時評估yolov5系列和yolov3-spp各個模型在各個尺度下的指標并可視化,但是其實我們幾乎用不到這里,另外一個函式profile_idetection完全沒用到,所以這兩個函式不看也可以,

def plot_study_txt(path='', x=None):
    """沒用到
    Plot study.txt generated by test.py
    """
    plot2 = False  # plot additional results
    if plot2:
        ax = plt.subplots(2, 4, figsize=(10, 6), tight_layout=True)[1].ravel()

    fig2, ax2 = plt.subplots(1, 1, figsize=(8, 4), tight_layout=True)
    # for f in [Path(path) / f'study_coco_{x}.txt' for x in ['yolov5s6', 'yolov5m6', 'yolov5l6', 'yolov5x6']]:
    for f in sorted(Path(path).glob('study*.txt')):
        y = np.loadtxt(f, dtype=np.float32, usecols=[0, 1, 2, 3, 7, 8, 9], ndmin=2).T
        x = np.arange(y.shape[1]) if x is None else np.array(x)
        if plot2:
            s = ['P', 'R', 'mAP@.5', 'mAP@.5:.95', 't_preprocess (ms/img)', 't_inference (ms/img)', 't_NMS (ms/img)']
            for i in range(7):
                ax[i].plot(x, y[i], '.-', linewidth=2, markersize=8)
                ax[i].set_title(s[i])

        j = y[3].argmax() + 1
        ax2.plot(y[5, 1:j], y[3, 1:j] * 1E2, '.-', linewidth=2, markersize=8,
                 label=f.stem.replace('study_coco_', '').replace('yolo', 'YOLO'))

    ax2.plot(1E3 / np.array([209, 140, 97, 58, 35, 18]), [34.6, 40.5, 43.0, 47.5, 49.7, 51.5],
             'k.-', linewidth=2, markersize=8, alpha=.25, label='EfficientDet')

    ax2.grid(alpha=0.2)
    ax2.set_yticks(np.arange(20, 60, 5))
    ax2.set_xlim(0, 57)
    ax2.set_ylim(30, 55)
    ax2.set_xlabel('GPU Speed (ms/img)')
    ax2.set_ylabel('COCO AP val')
    ax2.legend(loc='lower right')
    plt.savefig(str(Path(path).name) + '.png', dpi=300)

def profile_idetection(start=0, stop=0, labels=(), save_dir=''):
    """沒用到
    Plot iDetection '*.txt' per-image logs
    """
    ax = plt.subplots(2, 4, figsize=(12, 6), tight_layout=True)[1].ravel()
    s = ['Images', 'Free Storage (GB)', 'RAM Usage (GB)', 'Battery', 'dt_raw (ms)', 'dt_smooth (ms)', 'real-world FPS']
    files = list(Path(save_dir).glob('frames*.txt'))
    for fi, f in enumerate(files):
        try:
            results = np.loadtxt(f, ndmin=2).T[:, 90:-30]  # clip first and last rows
            n = results.shape[1]  # number of rows
            x = np.arange(start, min(stop, n) if stop else n)
            results = results[:, x]
            t = (results[0] - results[0].min())  # set t0=0s
            results[0] = x
            for i, a in enumerate(ax):
                if i < len(results):
                    label = labels[fi] if len(labels) else f.stem.replace('frames_', '')
                    a.plot(t, results[i], marker='.', label=label, linewidth=1, markersize=5)
                    a.set_title(s[i])
                    a.set_xlabel('time (s)')
                    # if fi == len(files) - 1:
                    #     a.set_ylim(bottom=0)
                    for side in ['top', 'right']:
                        a.spines[side].set_visible(False)
                else:
                    a.remove()
        except Exception as e:
            print('Warning: Plotting error for %s; %s' % (f, e))

    ax[1].legend()
    plt.savefig(Path(save_dir) / 'idetection_profile.png', dpi=200)

總結

\qquad 這個檔案的代碼主要是一些畫圖用的工具函式,和我們目標檢測的主要流程其實沒用什么關系,比較重要的函式有:plot_one_box、output_to_target、plot_images、plot_labels、plot_evolution、plot_results、plot_results_overlay、feature_visualization等,

–2021.08.02 22:14

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/291619.html

標籤:其他

上一篇:Java Web中上傳圖片以及顯示圖片的方法

下一篇:asp.net961-個人博客系統#畢業設計

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more