Advertisement

感知模型召回率、精确率计算(终)

阅读量:

Python计算3D感知模型召回率、精确率

  • 步骤1:格式化数据
  • 步骤2:统计感知结果、标注数据中各类目标总数
  • 步驟3:统计各分类召回样本为正的个数
  • 步骤4:计算各分类召回率、精确率

上篇文章讲述目标框与感知框交并比计算方法,本文将详细说明召回率与精确率的计算方式。

步骤1:格式化数据

格式化数据分为两步,第一步:格式化标注数据,第二步:格式化感知结果,具体格式如下:

复制代码
    # 标注数据的格式为:
    # {
    #   '文件名': [{'class': value, 'center': [x, y, z], 'size': [l, w, h], 'rotation': value, 'used': False}, ...],
    #   '文件名': [{'class': value, 'center': [x, y, z], 'size': [l, w, h], 'rotation': value, 'used': False}, ...],
    # }
    def _format_mark_data(self):
       _format_mark_data = {}
       # mark_data 格式为 {"file_name": [["class", [x, y, z], [l, w , h], rotation], ...]}
       for file_name, targets in self.mark_data.items():
       bounding_boxes = []
       for data in targets:
           bounding_boxes.append({"class_name": data[0], "center": data[1], "size": data[2],
                                  "rotation": data[3], "used": False})
       _format_mark_data[file_name] = bounding_boxes
       return _format_mark_data
    
    # 感知结果格式为:
    # {
    #	"class": [
    #	    {
    #	        'file_name': 'file_name'
    #	        'confidence': value,
    #	        'center': [x, y, z],
    #	        'size': [l, w, h],
    #	        'rotation': value,
    #	    },
    #	    ...
    #	]
    #	...
    # }
    def _format_res_data(self):
    # res_data 格式为 {"file_name": [["class", [x, y, z], [l, w , h], rotation], ...]}
    _format_res_data = {}
    for file_name, targets in self.res_data.items():
        for target in targets:
            if target[0] not in _format_res_data.keys():
                _format_res_data[target[0]] = list()
            _format_res_data[target[0]].append(
                {
                    'file_name': file_name,
                    'confidence': target[4],
                    'center': target[1],
                    'size': target[2],
                    'rotation': target[3],
                    'used': False
                }
            )
    
    # 策略: 将置信度降序排列,确保置信度高的数据优先计算以降低错误率
    for class_name, boxes in _format_res_data.items():
        boxes.sort(key=lambda box: float(box['confidence']), reverse=True)
        _format_res_data[class_name] = boxes
    return _format_res_data

步骤2:统计感知结果、标注数据中各类目标总数

标注数据中分类目标的数量可以理解成:TP + FN
感知数据中分类目标的数量可以理解成:TP + FP

复制代码
    def _compute_target_counts(self):
    """
    统计标注数据、感知数据中各分类的数量
    :return: 返回格式为{“class”: count}
    """
    
    def _compute(target_data):
        _res = dict()
        for _value in target_data.values():
            for _target in _value:
                _class_name = _target[0]
                if _class_name in _res.keys():
                    _res[_class_name] += 1
                else:
                    _res[_class_name] = 1
        return _res
    return _compute(self.mark_data), _compute(self.res_data)

步驟3:统计各分类召回样本为正的个数

遍历步骤1中的结果,统计分类TP数量

复制代码
    def _compute_true_positive_counts(self, compute_format_mark_data, compute_format_res_data):
    """
    计算召回样本中为TP的个数
    :param compute_format_mark_data: 标注格式化数据
    :param compute_format_res_data: 感知格式化数据
    :return: 分类匹配为正样本的个数
    """
    _true_positive = {}
    
    # 步骤一: 遍历标注数据中所有出现过的分类, 若感知数据中未出现此分类,则分类tp个数为0
    # cate_map为标注类别与感知类别的映射关系
    _loop_values = set(self.cate_map.values()) if self.is_vision else self.cate_map.keys()
    for class_name in _loop_values:
        _true_positive[class_name] = 0
    
        # if class_name not in compute_format_res_data.keys():
        #     continue
    
        # 根据标注的key获取结果中可能存在的key列表,将所有感知结果取出
        _detections = list()
        if self.is_vision:
            _detections = compute_format_res_data.get(class_name, list())
        else:
            _res_keys_list = get_res_type_list_with_mark_type(class_name)
            for _key in _res_keys_list:
                _detections = _detections + compute_format_res_data.get(_key, list())
    
        # 步骤二: 取感知格式化结果中类别为class_name的数据,遍历数据与标注数据对比,统计tp个数
        for detection in _detections:
            # 取当前数据所在的文件名,通过文件名取出文件中所有标注数据
            _file_name = detection["file_name"]
    
            # 记录当前感知框与标注框匹配IOU最大值,分类相同IOU值最大则认为匹配成功
            _iou_max = -1  # 记录最佳匹配IOU值
    
            # 记录匹配成功的标注对象,需要通过此变量将used值设置为True
            _gt_match = -1  # 记录最佳匹配对象值
    
            # 遍历标注格式化结果,与当前感知框进行匹配
            for label in compute_format_mark_data[_file_name]:
                # 只有分类映射正确 且 used值为False时才进行匹配
    
                if label["class_name"] == class_name and not label['used']:
                    # 计算IOU, 传参格式 (center(x, y, z), box_size(l, w, h), heading_angle)
                    _, _iou_3d = box3d_iou(
                        (tuple(label["center"]), tuple(label["size"]), label["rotation"] / 180),
                        (tuple(detection["center"]), tuple(detection["size"]), detection["rotation"] / 180),
                        self.coordinate_axis
                    )
    
                    # 记录的最大IOU与当前IOU比较
                    if _iou_3d > _iou_max:
                        _iou_max = _iou_3d
                        _gt_match = label
    
            # 获取当前分类IOU阈值
            min_overlap = float(self.classes_iou.get(class_name, iou_default_value))  # 默认IOU值
    
            # 若IOU大于阈值且当前数据未被使用,则tp加1,used值设置为True
            if _iou_max >= min_overlap and not _gt_match['used']:
                _gt_match['used'] = True
                detection['used'] = True
                _true_positive[class_name] += 1
    return _true_positive

步骤4:计算各分类召回率、精确率

结合步骤2、步骤3的结果,计算分类召回率、精确率数值

复制代码
    _compute_res = dict()
    for key in set(self.cate_map.values()):
    _compute_res[key] = {
        "tp_count": _tp.get(key, 0),
        "recall_count": _res_data_count.get(key, 0),
        "mark_count": _mark_data_count.get(key, 0),
        "recall": "%.4f" % (_tp.get(key, 0) / _mark_data_count.get(key, 1)),  # 召回率    识别为正样本数量/正样本总数
        "precision": 0 if _res_data_count.get(key, 0) == 0 else "%.4f" % (_tp.get(key, 0) / _res_data_count.get(key, 0))
    }
    # _compute_res 为所有结果

全部评论 (0)

还没有任何评论哟~