新手学习yolov8目标检测小记2--对比实验中经典模型库MMDetection使用方法(使用自己的数据集训练,并转换为yolo格式评价指标)
一、按照步骤环境配置
pip install timm==1.0.7 thop efficientnet_pytorch==0.7.1 einops grad-cam==1.4.8 dill==0.3.6 albumentations==1.4.11 pytorch_wavelets==1.3.0 tidecv PyWavelets -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install -U openmim -i https://pypi.tuna.tsinghua.edu.cn/simple
mim install mmengine -i https://pypi.tuna.tsinghua.edu.cn/simple
mim install "mmcv==2.1.0" -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install YOLO
pip install ultralytics
pip install -v -e.
二、自定义数据集放置
我这里已经将数据集按照训练集、验证集、测试集=8:1:1划分好,具体的存放目录结构如下图所示。其中test2017、train2017、val2017存放图片,testlabels、trainlabels、vallabels存放标注文件txt,数据集格式转换后,在annotations文件中。
YOLO格式转coco格式代码如下
import os
import cv2
import json
from tqdm import tqdm
from sklearn.model_selection import train_test_split
import argparse# visdrone2019
classes = ['beibie1','beibie2','beibie3']parser = argparse.ArgumentParser()
parser.add_argument('--image_path', default=r'E:\mmde\mmdetection-3.0.0\mmdetection-3.0.0\data\coco\val2017', type=str, help="path of images")
parser.add_argument('--label_path', default=r'E:\mmde\mmdetection-3.0.0\mmdetection-3.0.0\data\coco\vallabels', type=str, help="path of labels .txt")
parser.add_argument('--save_path', default='val.json', type=str,help="if not split the dataset, give a path to a json file")
arg = parser.parse_args()def yolo2coco(arg):print("Loading data from ", arg.image_path, arg.label_path)assert os.path.exists(arg.image_path)assert os.path.exists(arg.label_path)originImagesDir = arg.image_pathoriginLabelsDir = arg.label_path# images dir nameindexes = os.listdir(originImagesDir)dataset = {'categories': [], 'annotations': [], 'images': []}for i, cls in enumerate(classes, 0):dataset['categories'].append({'id': i, 'name': cls, 'supercategory': 'mark'})# 标注的idann_id_cnt = 0for k, index in enumerate(tqdm(indexes)):# 支持 png jpg 格式的图片.txtFile = f'{index[:index.rfind(".")]}.txt'stem = index[:index.rfind(".")]# 读取图像的宽和高try:im = cv2.imread(os.path.join(originImagesDir, index))height, width, _ = im.shapeexcept Exception as e:print(f'{os.path.join(originImagesDir, index)} read error.\nerror:{e}')# 添加图像的信息if not os.path.exists(os.path.join(originLabelsDir, txtFile)):# 如没标签,跳过,只保留图片信息.continuedataset['images'].append({'file_name': index,'id': stem,'width': width,'height': height})with open(os.path.join(originLabelsDir, txtFile), 'r') as fr:labelList = fr.readlines()for label in labelList:label = label.strip().split()x = float(label[1])y = float(label[2])w = float(label[3])h = float(label[4])# convert x,y,w,h to x1,y1,x2,y2H, W, _ = im.shapex1 = (x - w / 2) * Wy1 = (y - h / 2) * Hx2 = (x + w / 2) * Wy2 = (y + h / 2) * H# 标签序号从0开始计算, coco2017数据集标号混乱,不管它了。cls_id = int(label[0])width = max(0, x2 - x1)height = max(0, y2 - y1)dataset['annotations'].append({'area': width * height,'bbox': [x1, y1, width, height],'category_id': cls_id,'id': ann_id_cnt,'image_id': stem,'iscrowd': 0,# mask, 矩形是从左上角点按顺时针的四个顶点'segmentation': [[x1, y1, x2, y1, x2, y2, x1, y2]]})ann_id_cnt += 1# 保存结果with open(arg.save_path, 'w') as f:json.dump(dataset, f)print('Save annotation to {}'.format(arg.save_path))if __name__ == "__main__":yolo2coco(arg)
三、参数修改
以faster-rcnn为例,查看文件configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py内容如下:
_base_ = ['../_base_/models/faster-rcnn_r50_fpn.py','../_base_/datasets/coco_detection.py','../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'
]
根据显示内容,修改具体配置。(‘../_base_/default_runtime.py’无需修改)
(1)到‘../_base_/models/faster-rcnn_r50_fpn.py’修改
num_classes为自己的实际数据集类别数。
(2)到‘../_base_/datasets/coco_detection.py’,修改
dataset_type = 'CocoDataset'
data_root = 'data/coco/'
由于我使用的是coco数据集格式,只需要改data_root为自己数据集的位置即可。并修改
scale为自己的图像尺寸大小,我的是scale=(640, 640)。接下来根据数据集修改ann_file和data_prefix,根据如上数据集的位置放置,我的修改后该文件完整内容如下:
# dataset settings
dataset_type = 'CocoDataset'
data_root = 'data/coco/'# Example to use different file client
# Method 1: simply set the data root and let the file I/O module
# automatically infer from prefix (not support LMDB and Memcache yet)# data_root = 's3://openmmlab/datasets/detection/coco/'# Method 2: Use `backend_args`, `file_client_args` in versions before 3.0.0rc6
# backend_args = dict(
# backend='petrel',
# path_mapping=dict({
# './data/': 's3://openmmlab/datasets/detection/',
# 'data/': 's3://openmmlab/datasets/detection/'
# }))
backend_args = Nonetrain_pipeline = [dict(type='LoadImageFromFile', backend_args=backend_args),dict(type='LoadAnnotations', with_bbox=True),dict(type='Resize', scale=(640, 640), keep_ratio=True),dict(type='RandomFlip', prob=0.5),dict(type='PackDetInputs')
]
test_pipeline = [dict(type='LoadImageFromFile', backend_args=backend_args),dict(type='Resize', scale=(640, 640), keep_ratio=True),# If you don't have a gt annotation, delete the pipelinedict(type='LoadAnnotations', with_bbox=True),dict(type='PackDetInputs',meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape','scale_factor'))
]
train_dataloader = dict(batch_size=2,num_workers=2,persistent_workers=True,sampler=dict(type='DefaultSampler', shuffle=True),batch_sampler=dict(type='AspectRatioBatchSampler'),dataset=dict(type=dataset_type,data_root=data_root,ann_file='annotations/instances_train2017.json',data_prefix=dict(img='train2017/'),filter_cfg=dict(filter_empty_gt=True, min_size=32),pipeline=train_pipeline,backend_args=backend_args))
val_dataloader = dict(batch_size=1,num_workers=2,persistent_workers=True,drop_last=False,sampler=dict(type='DefaultSampler', shuffle=False),dataset=dict(type=dataset_type,data_root=data_root,ann_file='annotations/instances_val2017.json',data_prefix=dict(img='val2017/'),test_mode=True,pipeline=test_pipeline,backend_args=backend_args))
# test_dataloader = val_dataloaderval_evaluator = dict(type='CocoMetric',ann_file=data_root + 'annotations/instances_val2017.json',metric='bbox',format_only=False,backend_args=backend_args)
# test_evaluator = val_evaluator# inference on test dataset and
# format the output results for submission.
test_dataloader = dict(batch_size=2,num_workers=2,persistent_workers=True,drop_last=False,sampler=dict(type='DefaultSampler', shuffle=False),dataset=dict(type=dataset_type,data_root=data_root,ann_file=data_root + 'annotations/instances_test2017.json',data_prefix=dict(img='test2017/'),test_mode=True,pipeline=test_pipeline))
test_evaluator = dict(type='CocoMetric',metric='bbox',format_only=True,ann_file=data_root + 'annotations/instances_test2017.json',outfile_prefix='./work_dirs/coco_detection/test')
(3)到‘../_base_/schedules/schedule_1x.py’修改max_epochs为自己设置的最大训练轮次,其他的val_interval、lr、momentum、weight_decay,如果没有特别的要求可不修改。哦,根据自己的电脑情况,别忘了改base_batch_size。该文件整体内容如下:
# training schedule for 1x
train_cfg = dict(type='EpochBasedTrainLoop', max_epochs=100, val_interval=10)
val_cfg = dict(type='ValLoop')
test_cfg = dict(type='TestLoop')# learning rate
param_scheduler = [dict(type='LinearLR', start_factor=0.001, by_epoch=False, begin=0, end=500),dict(type='MultiStepLR',begin=0,end=100,by_epoch=True,milestones=[67, 92],gamma=0.1)
]# optimizer
optim_wrapper = dict(type='OptimWrapper',optimizer=dict(type='SGD', lr=0.01, momentum=0.937, weight_decay=0.0001))# Default setting for scaling LR automatically
# - `enable` means enable scaling LR automatically
# or not by default.
# - `base_batch_size` = (8 GPUs) x (2 samples per GPU).
auto_scale_lr = dict(enable=False, base_batch_size=16)
四、命令训练
使用如下命令训练:
python tools/train.py configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py
如果训练中断,使用resume继续(注意‘--work-dir work_dirs/faster-rcnn_r50_fpn_1x_coco’是训练结果输出的位置,epoch_21.pth是上次训练中断后,输出的最后一个pth,根据自己的实际情况修改):
python tools/train.py configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py --work-dir work_dirs/faster-rcnn_r50_fpn_1x_coco --resume work_dirs/faster-rcnn_r50_fpn_1x_coco/epoch_21.pth
五、转为YOLO格式的评价指标
(1)找出最佳epoch
import os
import subprocess
import pickle
import numpy as np
import json
from prettytable import PrettyTable
from tqdm import tqdm# 设置工作目录和模型文件路径
work_dir = "work_dirs/faster-rcnn_r50_fpn_1x_coco"
config_file = "configs/faster_rcnn/faster-rcnn_r50_fpn_1x_coco.py" # 你的config文件路径# 存放模型权重文件(epoch_1.pth 到 epoch_100.pth)
checkpoint_dir = os.path.join(work_dir, "") # 假设检查点文件在 'checkpoints' 子文件夹下# 遍历模型权重文件(epoch_1.pth, epoch_2.pth, ..., epoch_100.pth)
checkpoint_files = [f for f in os.listdir(checkpoint_dir) if f.endswith('.pth')]
checkpoint_files.sort(key=lambda x: int(x.split('_')[1].split('.')[0])) # 按照 epoch 数字排序# 用于存储评估结果
results = []# 循环遍历每个模型文件
for checkpoint_file in tqdm(checkpoint_files, desc="Evaluating"):checkpoint_path = os.path.join(checkpoint_dir, checkpoint_file)# 设置输出 pkl 文件的路径output_pkl = f"res_{checkpoint_file.split('.')[0]}.pkl"# 运行 test.py 脚本进行模型评估command = ["python", "tools/test.py", config_file, checkpoint_path,"--out", output_pkl # 输出 pkl 文件]# 使用 subprocess 运行命令并捕获输出result = subprocess.run(command, capture_output=True, text=True)# 调试输出print(f"Evaluating {checkpoint_file}...")print(result.stdout)# 假设输出的评估结果包含了 mAP# 解析 mAP,假设它包含在 stdout 中,例如 "bbox_mAP: 0.45"for line in result.stdout.splitlines():if "bbox_mAP" in line:try:# 提取 mAP 分数bbox_mAP = float(line.split(":")[-1].strip())epoch = int(checkpoint_file.split("_")[1].split(".")[0]) # 获取 epoch 数字results.append((epoch, bbox_mAP, output_pkl)) # 存储结果 (epoch, mAP, pkl文件)breakexcept ValueError:print(f"Error parsing bbox_mAP for {checkpoint_file}: {line}")continue# 计算并输出最佳 epoch 和 mAP
if results:# 根据 mAP 找到最佳的 epochbest_epoch, best_mAP, best_pkl = max(results, key=lambda x: x[1])print(f"Best Epoch: {best_epoch}, Best bbox_mAP: {best_mAP:.4f}, Best Output pkl: {best_pkl}")
else:print("No valid results found. Please check the log outputs.")# 计算所有 epoch 的 mAP 值
table = PrettyTable()
table.title = f"Evaluation Metrics"
table.field_names = ["Epoch", "bbox_mAP", "Output pkl"]# 添加每个 epoch 的评估结果
for epoch, mAP, pkl_file in results:table.add_row([epoch, f"{mAP:.4f}", pkl_file])print(table)
(2)根据最佳epoch生成pkl文件
python tools/test.py work_dirs/faster-rcnn_r50_fpn_1x_coco/faster-rcnn_r50_fpn_1x_coco.py work_dirs/faster-rcnn_r50_fpn_1x_coco/best_coco_bbox_mAP_epoch_90.pth --out res90.pkl
(3)根据pkl文件输出对比参数,修改内容在‘def parse_opt():’,将内容改为实际的地址名称。
完整代码如下:
import os, torch, cv2, math, tqdm, time, shutil, argparse, json, pickle
import numpy as np
from prettytable import PrettyTabledef clip_boxes(boxes, shape):# Clip boxes (xyxy) to image shape (height, width)if isinstance(boxes, torch.Tensor): # faster individuallyboxes[..., 0].clamp_(0, shape[1]) # x1boxes[..., 1].clamp_(0, shape[0]) # y1boxes[..., 2].clamp_(0, shape[1]) # x2boxes[..., 3].clamp_(0, shape[0]) # y2else: # np.array (faster grouped)boxes[..., [0, 2]] = boxes[..., [0, 2]].clip(0, shape[1]) # x1, x2boxes[..., [1, 3]] = boxes[..., [1, 3]].clip(0, shape[0]) # y1, y2def scale_boxes(img1_shape, boxes, img0_shape, ratio_pad=None):# Rescale boxes (xyxy) from img1_shape to img0_shapeif ratio_pad is None: # calculate from img0_shapegain = min(img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1]) # gain = old / newpad = (img1_shape[1] - img0_shape[1] * gain) / 2, (img1_shape[0] - img0_shape[0] * gain) / 2 # wh paddingelse:gain = ratio_pad[0][0]pad = ratio_pad[1]boxes[..., [0, 2]] -= pad[0] # x paddingboxes[..., [1, 3]] -= pad[1] # y paddingboxes[..., :4] /= gainclip_boxes(boxes, img0_shape)return boxesdef box_iou(box1, box2, eps=1e-7):"""Calculate intersection-over-union (IoU) of boxes. Both sets of boxes are expected to be in (x1, y1, x2, y2) format.Based on https://github.com/pytorch/vision/blob/master/torchvision/ops/boxes.pyArgs:box1 (torch.Tensor): A tensor of shape (N, 4) representing N bounding boxes.box2 (torch.Tensor): A tensor of shape (M, 4) representing M bounding boxes.eps (float, optional): A small value to avoid division by zero. Defaults to 1e-7.Returns:(torch.Tensor): An NxM tensor containing the pairwise IoU values for every element in box1 and box2."""# NOTE: Need .float() to get accurate iou values# inter(N,M) = (rb(N,M,2) - lt(N,M,2)).clamp(0).prod(2)(a1, a2), (b1, b2) = box1.float().unsqueeze(1).chunk(2, 2), box2.float().unsqueeze(0).chunk(2, 2)inter = (torch.min(a2, b2) - torch.max(a1, b1)).clamp_(0).prod(2)# IoU = inter / (area1 + area2 - inter)return inter / ((a2 - a1).prod(2) + (b2 - b1).prod(2) - inter + eps)def process_batch(detections, labels, iouv):"""Return correct prediction matrixArguments:detections (array[N, 6]), x1, y1, x2, y2, conf, classlabels (array[M, 5]), class, x1, y1, x2, y2Returns:correct (array[N, 10]), for 10 IoU levels"""correct = np.zeros((detections.shape[0], iouv.shape[0])).astype(bool)iou = box_iou(labels[:, 1:], detections[:, :4])correct_class = labels[:, 0:1] == detections[:, 5]for i in range(len(iouv)):x = torch.where((iou >= iouv[i]) & correct_class) # IoU > threshold and classes matchif x[0].shape[0]:matches = torch.cat((torch.stack(x, 1), iou[x[0], x[1]][:, None]), 1).cpu().numpy() # [label, detect, iou]if x[0].shape[0] > 1:matches = matches[matches[:, 2].argsort()[::-1]]matches = matches[np.unique(matches[:, 1], return_index=True)[1]]# matches = matches[matches[:, 2].argsort()[::-1]]matches = matches[np.unique(matches[:, 0], return_index=True)[1]]correct[matches[:, 1].astype(int), i] = Truereturn torch.tensor(correct, dtype=torch.bool, device=iouv.device)def smooth(y, f=0.05):# Box filter of fraction fnf = round(len(y) * f * 2) // 2 + 1 # number of filter elements (must be odd)p = np.ones(nf // 2) # ones paddingyp = np.concatenate((p * y[0], y, p * y[-1]), 0) # y paddedreturn np.convolve(yp, np.ones(nf) / nf, mode='valid') # y-smootheddef ap_per_class(tp, conf, pred_cls, target_cls, plot=False, save_dir='.', names=(), eps=1e-16, prefix=''):""" Compute the average precision, given the recall and precision curves.Source: https://github.com/rafaelpadilla/Object-Detection-Metrics.# Argumentstp: True positives (nparray, nx1 or nx10).conf: Objectness value from 0-1 (nparray).pred_cls: Predicted object classes (nparray).target_cls: True object classes (nparray).plot: Plot precision-recall curve at mAP@0.5save_dir: Plot save directory# ReturnsThe average precision as computed in py-faster-rcnn."""# Sort by objectnessi = np.argsort(-conf)tp, conf, pred_cls = tp[i], conf[i], pred_cls[i]# Find unique classesunique_classes, nt = np.unique(target_cls, return_counts=True)nc = unique_classes.shape[0] # number of classes, number of detections# Create Precision-Recall curve and compute AP for each classpx, py = np.linspace(0, 1, 1000), [] # for plottingap, p, r = np.zeros((nc, tp.shape[1])), np.zeros((nc, 1000)), np.zeros((nc, 1000))for ci, c in enumerate(unique_classes):i = pred_cls == cn_l = nt[ci] # number of labelsn_p = i.sum() # number of predictionsif n_p == 0 or n_l == 0:continue# Accumulate FPs and TPsfpc = (1 - tp[i]).cumsum(0)tpc = tp[i].cumsum(0)# Recallrecall = tpc / (n_l + eps) # recall curver[ci] = np.interp(-px, -conf[i], recall[:, 0], left=0) # negative x, xp because xp decreases# Precisionprecision = tpc / (tpc + fpc) # precision curvep[ci] = np.interp(-px, -conf[i], precision[:, 0], left=1) # p at pr_score# AP from recall-precision curvefor j in range(tp.shape[1]):ap[ci, j], mpre, mrec = compute_ap(recall[:, j], precision[:, j])if plot and j == 0:py.append(np.interp(px, mrec, mpre)) # precision at mAP@0.5# Compute F1 (harmonic mean of precision and recall)f1 = 2 * p * r / (p + r + eps)i = smooth(f1.mean(0), 0.1).argmax() # max F1 indexp, r, f1 = p[:, i], r[:, i], f1[:, i]tp = (r * nt).round() # true positivesfp = (tp / (p + eps) - tp).round() # false positivesreturn tp, fp, p, r, f1, ap, unique_classes.astype(int)def compute_ap(recall, precision):""" Compute the average precision, given the recall and precision curves# Argumentsrecall: The recall curve (list)precision: The precision curve (list)# ReturnsAverage precision, precision curve, recall curve"""# Append sentinel values to beginning and endmrec = np.concatenate(([0.0], recall, [1.0]))mpre = np.concatenate(([1.0], precision, [0.0]))# Compute the precision envelopempre = np.flip(np.maximum.accumulate(np.flip(mpre)))# Integrate area under curvemethod = 'interp' # methods: 'continuous', 'interp'if method == 'interp':x = np.linspace(0, 1, 101) # 101-point interp (COCO)ap = np.trapz(np.interp(x, mrec, mpre), x) # integrateelse: # 'continuous'i = np.where(mrec[1:] != mrec[:-1])[0] # points where x axis (recall) changesap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1]) # area under curvereturn ap, mpre, mrecdef parse_opt():parser = argparse.ArgumentParser()parser.add_argument('--label_coco', type=str, default='E:/mmde/mmdetection-3.0.0/mmdetection-3.0.0/test.json',help='label coco path')# parser.add_argument('--pred_coco', type=str, default='runs/val/exp/predictions.json', help='pred coco path')parser.add_argument('--pred_coco', type=str, default='E:/mmde/mmdetection-3.0.0/mmdetection-3.0.0/res90.pkl', help='pred coco path')parser.add_argument('--iou', type=float, default=0.7, help='iou threshold')parser.add_argument('--conf', type=float, default=0.001, help='conf threshold')opt = parser.parse_known_args()[0]return optif __name__ == '__main__':opt = parse_opt()iouv = torch.linspace(0.5, 0.95, 10) # iou vector for mAP@0.5:0.95niou = iouv.numel()stats = []label_coco_json_path, pred_coco_json_path = opt.label_coco, opt.pred_cocowith open(label_coco_json_path) as f:label = json.load(f)classes = []for data in label['categories']:classes.append(data['name'])image_id_hw_dict = {}for data in label['images']:image_id_hw_dict[data['id']] = [data['height'], data['width']]label_id_dict = {}for data in tqdm.tqdm(label['annotations'], desc='Process label...'):if data['image_id'] not in label_id_dict:label_id_dict[data['image_id']] = []category_id = data['category_id']x_min, y_min, w, h = data['bbox'][0], data['bbox'][1], data['bbox'][2], data['bbox'][3]x_max, y_max = x_min + w, y_min + hlabel_id_dict[data['image_id']].append(np.array([int(category_id), x_min, y_min, x_max, y_max]))if pred_coco_json_path.endswith('json'):with open(pred_coco_json_path) as f:pred = json.load(f)pred_id_dict = {}for data in tqdm.tqdm(pred, desc='Process pred...'):if data['image_id'] not in pred_id_dict:pred_id_dict[data['image_id']] = []score = data['score']category_id = data['category_id']x_min, y_min, w, h = data['bbox'][0], data['bbox'][1], data['bbox'][2], data['bbox'][3]x_max, y_max = x_min + w, y_min + hpred_id_dict[data['image_id']].append(np.array([x_min, y_min, x_max, y_max, float(score), int(category_id)]))else:with open(pred_coco_json_path, 'rb') as f:pred = pickle.load(f)pred_id_dict = {}for data in tqdm.tqdm(pred, desc='Process pred...'):image_id = os.path.splitext(os.path.basename(data['img_path']))[0]if image_id not in pred_id_dict:pred_id_dict[image_id] = []for i in range(data['pred_instances']['labels'].size(0)):score = data['pred_instances']['scores'][i]category_id = data['pred_instances']['labels'][i]bboxes = data['pred_instances']['bboxes'][i]x_min, y_min, x_max, y_max = bboxes.cpu().detach().numpy()# x_min, x_max = x_min / data['scale_factor'][0], x_max / data['scale_factor'][0]# y_min, y_max = y_min / data['scale_factor'][1], y_max / data['scale_factor'][1]pred_id_dict[image_id].append(np.array([x_min, y_min, x_max, y_max, float(score), int(category_id)]))for idx, image_id in enumerate(tqdm.tqdm(list(image_id_hw_dict.keys()), desc="Cal mAP...")):label = np.array(label_id_dict[image_id])if image_id not in pred_id_dict:pred = np.empty((0, 6))else:pred = torch.from_numpy(np.array(pred_id_dict[image_id]))nl, npr = label.shape[0], pred.shape[0]correct = torch.zeros(npr, niou, dtype=torch.bool)if npr == 0:if nl:stats.append((correct, *torch.zeros((2, 0)), torch.from_numpy(label[:, 0])))continueif nl:correct = process_batch(pred, torch.from_numpy(label), iouv)stats.append((correct, pred[:, 4], pred[:, 5], torch.from_numpy(label[:, 0])))stats = [torch.cat(x, 0).cpu().numpy() for x in zip(*stats)]tp, fp, p, r, f1, ap, ap_class = ap_per_class(*stats)print(f'precision:{p}')print(f'recall:{r}')print(f'mAP@0.5:{ap[:, 0]}')table = PrettyTable()table.title = f"Metrice"table.field_names = ["Classes", 'Precision', 'Recall', 'mAP50', 'mAP50-95']table.add_row(['all', f'{np.mean(p):.3f}', f'{np.mean(r):.3f}', f'{np.mean(ap[:, 0]):.3f}', f'{np.mean(ap):.3f}'])for cls_idx, classes in enumerate(classes):table.add_row([classes, f'{p[cls_idx]:.3f}', f'{r[cls_idx]:.3f}', f'{ap[cls_idx, 0]:.3f}',f'{ap[cls_idx, :].mean():.3f}'])print(table)
六、输出结果
此外,查看FLOPs参数,用命令:
python tools/analysis_tools/get_flops.py work_dirs/faster-rcnn_r50_fpn_1x_coco-0.937-best0.965/faster-rcnn_r50_fpn_1x_coco.py
结果如下图:
相关文章:
新手学习yolov8目标检测小记2--对比实验中经典模型库MMDetection使用方法(使用自己的数据集训练,并转换为yolo格式评价指标)
一、按照步骤环境配置 pip install timm1.0.7 thop efficientnet_pytorch0.7.1 einops grad-cam1.4.8 dill0.3.6 albumentations1.4.11 pytorch_wavelets1.3.0 tidecv PyWavelets -i https://pypi.tuna.tsinghua.edu.cn/simple pip install -U openmim -i https://pypi.tuna.t…...
Kubernetes开发环境minikube | 开发部署apache tomcat web单节点应用
minikube是一个主要用于开发与测试Kubernetes应用的运行环境 本文主要描述在minikube运行环境中部署J2EE tomcat web应用 minikube start --force minikube status 如上所示,在Linux中启动minikube运行环境 service docker start docker version service docker …...
浙江安吉成新的分布式光伏发电项目应用
摘 要:分布式光伏发电站是指将光伏发电组件安装在用户的建筑物屋顶、空地或其他适合的场地上,利用太阳能进行发电的一种可再生能源利用方式,与传统的大型集中式光伏电站相比,分布式光伏发电具有更灵活的布局、更低的建设成本和更高…...
Git - 记录一次由于少输入了一个命令导致的更改丢失
Git - 记录一次由于少输入了一个参数导致的更改丢失 前言 某晚我激情开发了几个小时,中途没有进行commit存档。准备睡觉时,我想创建一个新的分支并将今晚所有更改提交到新分支上(似乎应该开发时候就创建?)。 然后因…...
【Nginx】设置https和http同时使用同一个端口访问
以下是一个同时使用 HTTP 和 HTTPS 并通过 8070 端口的配置示例: server {listen 8070;server_name your_domain.com;location / {root /var/www/html;index index.html;} }server {listen 8070 ssl;server_name your_domain.com;# SSL 证书和私钥的路径ssl_certif…...
Vue 组件开发:构建高效可复用的 UI 构建块
在现代前端开发中,Vue.js 凭借其简洁的 API、渐进式框架设计和强大的生态系统,已经成为众多开发者的首选。Vue 组件化开发是其核心特性之一,它允许我们将复杂的 UI 拆分成多个独立、可复用的组件,从而提高代码的可维护性和可扩展性…...
【Uniapp-Vue3】v-if条件渲染及v-show的选择对比
如果我们想让元素根据响应式变量的值进行显示或隐藏可以使用v-if或v-show 一、v-show 另一种控制显示的方法就是使用v-show,使用方法和v-if一样,为true显示,为false则不显示。 二、v-if v-if除了可以像v-show一样单独使用外,还…...
浏览器报错:您的连接不是私密连接,Kubernetes Dashboard无法打开
问题描述 部署完成Kubernetes Dashboard后,打开HTTPS的web页面,Chrome和Edge浏览器都无法正常加载页面,会提示您的连接不是私密连接的报错。 原因: 浏览器不信任这些自签名的ssl证书,为了…...
asp.net core 属性路由和约定路由
在 ASP.NET Core 中,Web API 中的路由(Route)用于确定客户端请求的 URL 与服务器端处理逻辑之间的映射关系。路由机制在 Web API 的开发中非常重要,它帮助定义和管理不同请求路径如何触发特定的控制器和操作方法。 1. 路由概述 …...
机器学习之模型评估——混淆矩阵,交叉验证与数据标准化
目录 混淆矩阵 交叉验证 数据标准化 0-1标准化 z 标准化 混淆矩阵 混淆矩阵(Confusion Matrix)是一种用于评估分类模型性能的工具。 它是一个二维表格,其中行表示实际的类别,列表示模型预测的类别。 假设我们有一个二分类问题&…...
Java实现UDP与TCP应用程序
三、Java实现UDP应用程序 3.1 InetAddress类 java.net.InteAddress类是用于描述IP地址和域名的一个Java类; 常用方法如下: public static InetAddress getByName(String host):根据主机名获取InetAddress对象public String getHostName()…...
[python3]Excel解析库-calamine,10倍openpyxl性能
calamine 是一个用于读取多种电子表格格式(如 Excel、LibreOffice Calc 等)的 Python 库。它支持 .xls, .xlsx, .ods 和 .csv 文件格式,提供了简单易用的 API 来加载和处理电子表格数据。calamine 的一大特点是它的轻量级和高效性,…...
Clisoft SOS设置Server和Project
Clisoft SOS设置Server和Project 一、关于SOS Servers、Clients、Projects和Work Areas 以下三个图是官方文档中介绍的三种情况 图1:带有两个客户端的SOS服务器 图2:使用本地缓存服务器 图3:远程设计团队的缓存服务器 因为SOS软件需要…...
基于FPGA的出租车里程时间计费器
基于FPGA的出租车里程时间计费器 功能描述一、系统框图二、verilog代码里程增加模块时间增加模块计算价格模块上板视频演示 总结 功能描述 (1);里程计费功能:3公里以内起步价8元,超过3公里后每公里2元,其中…...
AnaConda下载PyTorch慢的解决办法
使用Conda下载比较慢,改为pip下载 复制下载链接到迅雷下载 激活虚拟环境,安装whl,即可安装成功 pip install D:\openai.wiki\ChatGLM2-6B\torch-2.4.1cu121-cp38-cp38-win_amd64.whl...
Hello 2025(A-C)
补题链接:Dashboard - Hello 2025 - Codeforces A. MEX Table 思路 除了含0的列和行其他的都是0,输出max(n,m)1即可 代码 #include<bits/stdc.h> using namespace std;#define vcoistnt ios_base::sync_with_stdio(false); cin.tie(NULL); co…...
Burpsuite20241102macM1版安装
1、安装jdk11 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" brew update brew install openjdk11 echo export PATH"/opt/homebrew/opt/openjdk11/bin:$PATH" >> ~/.zshrc source ~/.zshrc j…...
jenkins入门10--自动化构建
build periodically:设定类似cron周期性时间触发构建 * * * * * (五颗星,中间用空格隔开) 第一颗表示分钟,取值0~59 第二颗表示小时,取值0~23 第三颗表示一个月的第几天,取值1~31 第四颗表示第几月…...
Java基础概念
自动装箱 Integer i 10; //装箱 int n i; //拆箱 普通数据类型:直接在栈内存中分配空间,存储的是具体的值。包装类:作为对象在堆内存中分配空间。包装类实际上是对普通数据类型的封装,每个包装类都包含了对应的数据类…...
57.在 Vue 3 中使用 OpenLayers 点击选择 Feature 设置特定颜色
在 Web 开发中,地图应用是非常常见的需求,而 OpenLayers 是一个非常强大的地图库,它提供了丰富的地图操作功能。今天,我们将一起学习如何在 Vue 3 中结合 OpenLayers 使用点击事件来选择地图上的 Feature,并设置特定的…...
HTML——61. 单行文本框和密码输入框(主讲input元素的type属性)
<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>单行文本框和密码输入框</title></head><body><!--input元素的type属性:(必须要有)--> <!--单行文本框:1.type"text"2.可…...
h264之多视点mvc编码及解码过程(JMVC平台举例)
h264标准参考平台JMVC是针对MVC标准的,JMVC支持多视点编码、合流、多视点解码操作。可以利用JMVC生成h264 mvc码流和解码。 JMVC的下载地址是:jvet / JMVC GitLabH.264/AVC multi-view coding (MVC) extension JMVC reference softwarehttps://vcgit.hh…...
深度学习blog-深刻理解线性变换和矩阵
深度学习中避免不了矩阵运算,或者张量(其实是矩阵数组)运算。卷积是矩阵加、乘法,注意力也是一样。本质都一样,所谓注意力,卷积、滤波,是对不必了解数学的人说的,底层都是矩阵运算&a…...
C语言 扫雷程序设计
目录 1.main函数 2.菜单打印menu函数 3.游戏game函数 4.宏定义 5.界面初始化 6.打印界面 7.设置雷 8.统计排查坐标周围雷的个数 9.排查雷 10.总代码 test.c代码 game.h代码 game.c代码 结语: 一个简单的扫雷游戏,通过宏定义可以修改行列的…...
[笔记] Jenkins 安装与配置全攻略:Ubuntu 从零开始搭建持续集成环境
随着 DevOps 流程的普及,持续集成(CI)和持续交付(CD)已成为现代软件开发中不可或缺的一部分。Jenkins 作为一款开源的自动化服务器,广泛应用于 CI/CD 管道的构建与管理。它不仅支持多种编程语言和工具链&am…...
【51单片机零基础-chapter3:按键:独立按键|||附带常见C语句.逻辑运算符】
将unsigned char var0;看作沟通二进制和十进制的桥梁 var是8位,初始为0000 0000; 同时可以进行十进制的运算 逻辑运算 位运算 & 按位与(有0则0) | 按位或(有1则1) ~ 按位非 ^ 按位异或(相同则1,不同为0) <<按位左移 >>按位右移 位运算符解释: 0011 1100 <&…...
深入浅出:深层网络处理技术的教学指南
引言 在人工智能的浪潮中,深层网络处理技术(Deep Learning)无疑是最耀眼的明星之一。无论是图像识别、自然语言处理,还是语音识别,深层网络都展现出了强大的能力。然而,对于初学者来说,深层网络…...
深入浅出Node.js-1(node.js入门)
全新专栏带你快速掌握node.js Node.js入门 html,css,js 30年了 nodejs环境 09年出现 15年 nodejs为我们解决了2个方面的问题: 【锦上添花】让我们前端工程师拥有了后端开发能力(开接口,访问数据库) - 大公司BFF(5…...
Django的runserver
当年执行 python manage runserver命令时 1. 先执行 runserver 中的 handle方法 2. 执行 self.run()方法 3. 执行 self.inner_run() 3.1 inner_run 下 run方法的封装 3.1.1 接着看 handle 怎么来的 封装了一个方法 接着找返回函数 3.1.2在 basehttp 下 3.1.3 get_wsgi_appl…...
MySQL 存储引擎
InnoDB InnoDB是MySQL的默认存储引擎,自MySQL 5.5版本起开始使用。它提供了具有提交、回滚和崩溃恢复能力的事务安全(ACID兼容)存储引擎。 主要特性: 事务支持:完全支持ACID(原子性、一致性、隔离性、持久…...
React知识盲点——组件通信、性能优化、高级功能详解(大纲)
组件通信 React 组件通信详解 在 React 中,组件通信是一个核心概念,主要指的是如何让不同的组件共享和传递数据。React 提供了多种机制来实现组件间的数据传递和状态共享。以下是几种常见的组件通信方式,包括:父子组件通信&…...
Maven 详细配置:Maven 项目 POM 文件解读
Maven 是 Java 开发领域中广泛使用的项目管理和构建工具,通过其核心配置文件——POM(Project Object Model)文件,开发者能够定义项目的基本信息、依赖关系、插件配置以及构建生命周期等关键要素。POM 文件不仅是 Maven 项目的核心…...
selenium
pythonselenium selenium是一个第三方库,python有很多库; 1、什么是ui自动化? 通过模拟手工操作用户ui页面的方式,用代码去实现自动化操作和验证的行为。 2、ui自动化的优点? (1)解决重复性的功能测试…...
网络安全:设备原理与操作
设备型号概述 网络安全企业有哪些? 国外:思科,Juniper,惠普,3Com,。。。。 国内:华为,中性,锐捷,蓝盾,绿盟,山石网科,36…...
pytorch中nn.Conv2d详解及参数设置原则
文章目录 基础参数1. in_channels (输入通道数)2. out_channels (输出通道数)3. kernel_size (卷积核大小)4. stride (步幅)5. padding (填充)6. dilation (膨胀)7. groups (分组卷积)8. bias (偏置) 如何设置参数?1. **in_channels 和 out_channels(输入…...
select下拉框,首次进入页面没有显示value的情况
bug场景: 类似这种bug情况排查如下: 首先 理解含义 options就是存放键值对的,id就是key,对上了它就自动把label显示 而且如果你用来当作key和label的字段,与后端返回的不一致,还可以进行更改 其次 排查接…...
接口项目操作图-thinkphp6-rabbitmq
一、用户开户流程 用户首次需要联系商务开通账户,需要提供手机号及来访问的IP。开好户之后,平台方将提供用户访问的key值及header头部参数的公钥加密文件、body访问参数以及返回数据的公私钥加解密文件。 二、用户请求流程 用户将拿到的key值进行rsa公钥…...
thinkphp6.0常用设计模式实例
单例模式 (Singleton) 场景:确保一个类只有一个实例,并提供一个全局访问点。 实际业务:数据库连接、日志记录器、配置管理等。 ThinkPHP 6.0 实现: namespace app\common;class DatabaseConnection {private static $instance …...
微服务保护——Sentinel
什么是微服务保护? 微服务保护是一系列用于保障微服务架构稳定、可靠运行的策略与技术手段,在复杂的分布式微服务系统里,它能避免局部故障引发连锁反应,从而维持整个系统的可用性,主要涵盖以下几个关键部分:…...
php 多进程那点事,用 swoole 如何解决呢 ?
在 PHP 中,多进程的处理通常会遇到一些挑战,比如资源共享、进程间通信、性能优化等。Swoole 是一个高性能的协程和多进程框架,旨在为 PHP 提供异步、并发、协程等功能,解决了传统 PHP 环境中的多进程管理问题。通过使用 Swoole&am…...
STM32+ADC+DMA快速循环转换
测试平台:STM32F405RGT6 uint32_t AD_Buf[100]{0}; HAL_ADC_Start_DMA(&hadc2,(uint32_t *)AD_Buf,100); while(1) {printf("AD_Buf:%d\n",AD_Buf[0]); }...
移动电商的崛起与革新:以开源AI智能名片2+1链动模式S2B2C商城小程序为例的深度剖析
摘要:本文旨在探讨移动电商的崛起背景、特点及其对传统电商模式的革新影响,并以开源AI智能名片21链动模式S2B2C商城小程序为具体案例,深入分析其在移动电商领域的创新实践。随着移动互联网技术的飞速发展,移动电商已成为电商行业的…...
QT实现 端口扫描暂停和继续功能 3
上篇QT给端口扫描工程增加线程2-CSDN博客 为按钮pushButton_Stop添加clicked事件,功能为暂停扫描,并在暂停后显示继续按钮,点击继续按钮之后继续扫描 1.更新UI 添加继续按钮 点击转到槽则会自动声明 2. 更新 MainWindow.h 需要新增的部分…...
C_字符数组存储汉字字符串及其索引
字符串就是字符数组,可以定义一个char类型的数组来存储字符串。 如果要存储多个字符串则可以定义一个char类型的二维数组。 存储多个汉字字符串的话,可以考虑用char类型的二维数组。 不过要注意,一个汉字在内存中占用的字节数确实大于一个…...
Linux标准IOday1
1:思维导图 2:将 student.c这个练习题,改成链表后实现 头文件link.h #ifndef __STRUCT_H__ #define __STRUCT_H__ #include <stdio.h> #include <stdlib.h> typedef struct Student{char name[20];double math;double chinese;double english;double…...
SEO内容优化:如何通过用户需求赢得搜索引擎青睐?
在谷歌SEO优化中,内容一直是最重要的因素之一。但要想让内容真正发挥作用,关键在于满足用户需求,而不是简单地堆砌关键词。谷歌的算法越来越智能化,更注重用户体验和内容的实用性。 了解目标用户的需求。通过工具如Google Trends…...
API调用淘宝京东商品详情接口示例参考,json格式数据示例
以下是API调用淘宝和京东商品详情接口的JSON格式数据示例: 淘宝商品详情接口JSON数据示例 淘宝商品详情接口(通常称为item_get或类似的名称)是淘宝开放平台提供的一个API接口,允许开发者根据商品的ID(Item ID&#x…...
css实现垂直文本
效果 知识 writing-mode: <value>; 可选值 horizontal-tb: 默认值。文本从左到右(或从右到左)排列,然后从上到下。vertical-rl: 文本从上到下排列,然后从右到左。适用于垂直书写的方向,如日语和中文。vertica…...
【AI日记】25.01.07
【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】【读书与思考】 AI 参加:kaggle 比赛 Forecasting Sticker Sales 读书 书名:国家为什么会失败阅读原因:2024 年诺贝尔经济学奖得主的力作,之前我已经读过他另一…...
logback日志
一、使用两个以上spring环境变量做三目操作 <springProperty name"application_name" scope"context" source"spring.application.name"/><springProperty name"trace_app_name" scope"context" source"sprin…...