对Detectron2中Faster RCNN的结果绘制混淆矩阵(VOC数据集)

1、创建主文件

先在主目录下创建confusion_matrix.py, 如下所示,主要参考v5里面的代码;

2、修改源码

在 detectron2/evaluation/pascal_voc_evaluation.py 中添加代码,生成检测结果输出,即下面代码中的predict_txt;

# 在 class PascalVOCDetectionEvaluator 的 evaluate 方法中
with open(res_file_template.format(cls_name), "w") as f:
     f.write("\n".join(lines))
# 后面添加如下代码,以生成不同类别的检测结果,其实和上面代码是差不多的,
# 但测试完之后上面生成的文件会被删除
results= os.path.join("随便一个位置", "{}.txt")
with open(results.format(cls_name), "w") as f:
     f.write("\n".join(lines))
import os
import pickle
import warnings
import xml.etree.ElementTree as ET
from pathlib import Path
import numpy as np
import sys
from matplotlib import pyplot as plt
import torch
import tqdm


def parse_rec(filename):
    """ Parse a PASCAL VOC xml file """
    tree = ET.parse(filename)
    objects = []
    for obj in tree.findall("object"):
        obj_struct = {}
        obj_struct["name"] = obj.find("name").text
        obj_struct["pose"] = obj.find("pose").text
        obj_struct["truncated"] = int(obj.find("truncated").text)
        obj_struct["difficult"] = int(obj.find("difficult").text)
        bbox = obj.find("bndbox")
        obj_struct["bbox"] = [
            int(bbox.find("xmin").text),
            int(bbox.find("ymin").text),
            int(bbox.find("xmax").text),
            int(bbox.find("ymax").text),
        ]
        objects.append(obj_struct)

    return objects

def box_iou(box1, box2):
    # https://github.com/pytorch/vision/blob/master/torchvision/ops/boxes.py
    """
    Return intersection-over-union (Jaccard index) of boxes.
    Both sets of boxes are expected to be in (x1, y1, x2, y2) format.
    Arguments:
        box1 (Tensor[N, 4])
        box2 (Tensor[M, 4])
    Returns:
        iou (Tensor[N, M]): the NxM matrix containing the pairwise
            IoU values for every element in boxes1 and boxes2
    """

    def box_area(box):
        # box = 4xn
        return (box[2] - box[0]) * (box[3] - box[1])

    area1 = box_area(box1.T)
    area2 = box_area(box2.T)

    # inter(N,M) = (rb(N,M,2) - lt(N,M,2)).clamp(0).prod(2)
    inter = (torch.min(box1[:, None, 2:], box2[:, 2:]) - torch.max(box1[:, None, :2], box2[:, :2])).clamp(0).prod(2)
    return inter / (area1[:, None] + area2 - inter)  # iou = inter / (area1 + area2 - inter)

class ConfusionMatrix:
        def __init__(self, nc, conf=0.75, iou_thres=0.5):
            self.matrix = np.zeros((nc + 1, nc + 1))
            self.nc = nc  # number of classes
            self.conf = conf
            self.iou_thres = iou_thres

        def process_batch(self, detections, labels):
            """
            Return intersection-over-union (Jaccard index) of boxes.
            Both sets of boxes are expected to be in (x1, y1, x2, y2) format.
            Arguments:
                detections (Array[N, 6]), x1, y1, x2, y2, conf, class
                labels (Array[M, 5]), class, x1, y1, x2, y2
            Returns:
                None, updates confusion matrix accordingly
            """
            detections = detections[detections[:, 4] > self.conf]
            gt_classes = labels[:, 0].int()
            detection_classes = detections[:, 5].int()
            iou = box_iou(labels[:, 1:], detections[:, :4])

            x = torch.where(iou > self.iou_thres)
            if x[0].shape[0]:
                matches = torch.cat((torch.stack(x, 1), iou[x[0], x[1]][:, None]), 1).cpu().numpy()
                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]]
            else:
                matches = np.zeros((0, 3))

            n = matches.shape[0] > 0
            m0, m1, _ = matches.transpose().astype(np.int16)
            for i, gc in enumerate(gt_classes):
                j = m0 == i
                if n and sum(j) == 1:
                    self.matrix[detection_classes[m1[j]], gc] += 1  # correct
                else:
                    self.matrix[self.nc, gc] += 1  # background FP

            if n:
                for i, dc in enumerate(detection_classes):
                    if not any(m1 == i):
                        self.matrix[dc, self.nc] += 1  # background FN

        def matrix(self):
            return self.matrix

        def tp_fp(self):
            tp = self.matrix.diagonal()  # true positives
            fp = self.matrix.sum(1) - tp  # false positives
            # fn = self.matrix.sum(0) - tp  # false negatives (missed detections)
            return tp[:-1], fp[:-1]  # remove background class

        def plot(self, normalize=True, save_dir='.', names=()):
            try:
                import seaborn as sn

                array = self.matrix / (
                    (self.matrix.sum(0).reshape(1, -1) + 1E-6) if normalize else 1)  # normalize columns
                array[array < 0.005] = np.nan  # don't annotate (would appear as 0.00)

                fig = plt.figure(figsize=(12, 9), tight_layout=True)
                sn.set(font_scale=1.2 if self.nc < 50 else 0.8)  # for label size
                labels = (0 < len(names) < 99) and len(names) == self.nc  # apply names to ticklabels
                with warnings.catch_warnings():
                    warnings.simplefilter('ignore')  # suppress empty matrix RuntimeWarning: All-NaN slice encountered

                    # sn.heatmap(array, annot=self.nc < 30, annot_kws={"size": 10}, cmap='Reds', fmt='.2f', square=True,
                    #            xticklabels=names + ['background'] if labels else "auto",
                    #            yticklabels=names + ['background'] if labels else "auto").set_facecolor((1, 1, 1))

                    sn.heatmap(array, cbar=True, annot=True, square=True, fmt=".2f", annot_kws={"size":10},
                               xticklabels=names + ['background'] if labels else "auto",
                               yticklabels=names + ['background'] if labels else "auto").set_facecolor((1, 1, 1))

                fig.axes[0].set_xlabel('True')
                fig.axes[0].set_ylabel('Predicted')
                fig.savefig(Path(save_dir) / 'confusion_matrix.jpg', dpi=2000)
                plt.show()
                plt.close()
            except Exception as e:
                print(f'WARNING: ConfusionMatrix plot failure: {e}')

        def print(self):
            for i in range(self.nc + 1):
                print(' '.join(map(str, self.matrix[i])))

# 修改自己的类别
VOC_CLASSES = ('',)
# 存放后面生成的 annots.pkl, 主要是标签信息(坐标,类别)
cachedir = ''
# 测试集前缀名文件
imagesetfile = 'ImageSets/Main/test.txt'
# xml文件夹
annopath = 'Annotations/{:s}.xml'
# 存放检测结果(坐标,类别,置信度)
predict_txt = 'datasets/VOC/{:s}.txt'


# first load gt 生成标签文件
if not os.path.isdir(cachedir):
    os.mkdir(cachedir)
cachefile = os.path.join(cachedir, "annots.pkl")

# read list of images
with open(imagesetfile, "r") as f:
    lines = f.readlines()
imagenames = [x.strip() for x in lines]

if not os.path.isfile(cachefile):
    # load annots
    recs = {}
    for i, imagename in enumerate(imagenames):
        recs[imagename] = parse_rec(annopath.format(imagename))
        if i % 100 == 0:
            print("Reading annotation for {:d}/{:d}".format(i + 1, len(imagenames)))
    # save
    print("Saving cached annotations to {:s}".format(cachefile))
    with open(cachefile, "wb") as f:
        pickle.dump(recs, f)
else:
    # load
    with open(cachefile, "rb") as f:
        recs = pickle.load(f)

# 注意修改置信度阈值和IoU阈值
confusion_matrix = ConfusionMatrix(nc=len(VOC_CLASSES), conf=0.25, iou_thres=0.5)
labels = []
for item in recs.values():
    labels.extend([[VOC_CLASSES.index(obj["name"])] + (obj["bbox"]) for obj in item])
labelsn = torch.tensor(labels)

pred = []
for i, cls in enumerate(VOC_CLASSES):  #
    predict_file = predict_txt.format(cls)  # 该文件中所有的框均被预测为该cls类别
    with open(predict_file, "r") as f:
        lines = f.readlines()
    splitlines = [x.strip().split(" ") for x in lines]
    pred += [[float(line[2]), float(line[3]), float(line[4]), float(line[5]), float(line[1]), i] for line in tqdm.tqdm(splitlines)]

predn = torch.tensor(pred)

"""
Arguments:
        predn (Array[N, 6]), x1, y1, x2, y2, conf, class
        labelsn (Array[M, 5]), class, x1, y1, x2, y2
    Returns:
        None, updates confusion matrix accordingly
"""
confusion_matrix.process_batch(predn, labelsn)
print(confusion_matrix.matrix)
confusion_matrix.plot(save_dir="./", names=list(VOC_CLASSES))


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Faster R-CNN是一种目标检测算法,可以用于训练VOC数据集。训练过程需要先准备好VOC数据集,包括图片和标注文件。然后,需要使用Faster R-CNN的代码库进行训练,可以选择使用已经训练好的模型进行fine-tune,也可以从头开始训练。训练过程需要设置好一些参数,如学习率、迭代次数等。最后,训练好的模型可以用于目标检测任务。 ### 回答2: Faster R-CNN是一种目标检测算法,其核心是使用深度学习技术对图像的物体进行识别和检测。在训练过程VOC数据集是一种常用的数据集,它包含了多种物体的图像数据和标注信息,可用于训练目标检测模型。 首先,需要对VOC数据集进行预处理。具体来说,需要将数据集划分为训练集、验证集和测试集,并将图像数据和对应的标注信息进行处理,转化为模型可以处理的格式。这个过程需要使用相关的工具和软件,如Pascal VOC tools等。 接下来,需要选择适合的深度学习框架和算法,如TensorFlow等,并进行相关的配置。然后,可以使用上述工具和软件进行训练。在训练过程,首先需要确定模型的结构和超参数,如网络层数、学习率等。然后,需要处理训练数据,并将其输入到模型进行训练。 在训练过程,需要不断调整超参数和模型结构,优化模型性能。同时,还需要进行模型的验证和测试,确认模型的准确性和可靠性。 总体而言,Faster R-CNN训练VOC数据集是一个复杂的过程,需要细致地设计和调整模型,并针对特定的任务进行不断迭代和优化。只有在充分的准备和细致的实验设计下,才能获得稳定的高性能检测模型。 ### 回答3: Faster R-CNN是一种基于深度学习的目标检测算法,可以对图像的不同物体进行准确的识别和定位,并给出其在图像的位置和类别。在Faster R-CNN,利用了RPN网络对图像进行区域提议,然后通过分类和回归网络对提议区域进行检测,从而实现目标检测。 在Faster R-CNN的训练VOC数据集是经典的物体识别和检测数据集之一,包含了20个不同类别的物体,每个类别的训练数据和测试数据均有多个样本。训练Faster R-CNN时,需要将VOC数据集转换成特定的格式,通常采用Pascal VOC或者COCO格式,然后通过类似于fine-tuning的方式对模型进行训练。 具体地说,Faster R-CNN的训练流程可以分为以下几个步骤: 1. 数据准备和预处理:将VOC数据集转换成Pascal VOC或者COCO格式,并进行数据增强和预处理,如随机裁剪、缩放、旋转等操作,从而增加样本的多样性和模型的鲁棒性。 2. 网络初始化和参数设置:初始化Faster R-CNN网络,并设置相关的超参数和优化器,如学习率、迭代次数、损失函数等。 3. 区域提议训练:利用RPN网络对图像进行区域提议,然后通过IoU计算和NMS筛选,对提议区域进行优化,从而得到最终的候选区域。 4. 物体分类和回归训练:针对候选区域,利用分类和回归网络进行检测和修正,从而获得检测结果和物体位置信息。 5. 模型评估和调优:通过测试数据集对模型进行评估和调优,如调整超参数、选择不同的优化器等,从而获得更加准确和高效的检测模型。 以上就是Faster R-CNN训练VOC数据集的基本流程和步骤。需要注意的是,训练过程需要耗费大量的计算资源和时间,对硬件环境和数据集的选择和优化十分重要。此外,也需要不断地尝试和调整算法参数和模型架构,从而获得更加优秀和高效的目标检测结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值