计算目标检测和语义分割的PR

需求描述

  1. 实际工作中,相比于mAP项目更加关心的是特定阈值下的precision和recall结果;
  2. 由于本次的GT中除了目标框之外还存在多边形标注,为此,计算IoU的方式从框与框之间变成了mask之间
    本文的代码适用于MMDetection下的预测结果和COCO格式之间来计算PR结果,具体的实现过程如下:
  • 获取预测结果并保存到json文件中;
  • 解析预测结果和GT;
  • 根据image_id获取每张图的预测结果和GT;
  • 基于mask计算预测结果和GT之间的iou矩阵;
  • 根据iou矩阵得到对应的tp、fp和num_gt;
  • 迭代所有的图像得到所有的tp、fp和num_gt累加,根据公式计算precision和recall;

具体实现

获取预测结果

在MMDetection框架下,通常使用如下的命令来评估模型的结果:

bash tools/dist_test.sh configs/aaaa/gaotie_cascade_rcnn_r50_fpn_1x.py work_dirs/gaotie_cascade_rcnn_r50_fpn_1x/epoch_20.pth 8 --eval bbox

此时能获取到类似下图的mAP结果。
mAP)
而我们需要在某个过程把预测结果保存下,用于后续得到PR结果,具体可以在mmdet/datasets/coco.py的438行位置添加如下代码:

 try:import shutilcocoDt = cocoGt.loadRes(result_files[metric])shutil.copyfile(result_files[metric], "results.bbox.json")

这样我们就可以得到results.bbox.json文件,里面包含的是模型的预测结果,如下图所示。
在这里插入图片描述)

获取GT结果

由于标注时有两个格式:矩形框和多边形,因此在构建GT的coco格式文件时,对于矩形框会将其四个顶点作为多边形传入到segmentations字段,对于多边形会计算出外接矩形传入到bbox字段。
在这里插入图片描述)
为此,获取GT信息的脚本实现如下:

def construct_gt_results(gt_json_path):results = dict()bbox_results = dict()cocoGt = COCO(annotation_file=gt_json_path)# cat_ids = cocoGt.getCatIds()img_ids = cocoGt.getImgIds()for id in img_ids:anno_ids = cocoGt.getAnnIds(imgIds=[id])annotations = cocoGt.loadAnns(ids=anno_ids)for info in annotations:img_id = info["image_id"]if img_id not in results:results[img_id] = list()bbox_results[img_id] = list()bbox = info["bbox"]x1, y1, x2, y2 = bbox[0], bbox[1], bbox[0] + bbox[2], bbox[1] + bbox[3]# results[img_id].append([x1, y1, x2, y2])# mask = _poly2mask(info["segmentation"], img_h=1544, img_w=2064)results[img_id].append(info["segmentation"])bbox_results[img_id].append([x1, y1, x2, y2])return results, img_ids, cocoGt, bbox_results

输入GT的json文件路径,返回所有图像的分割结果,image_id,COCO对象和目标框结果(用于后续的可视化结果)。

获取预测结果

模型预测出来的结果都是目标框的形式,与上面一样,将目标框的四个顶点作为多边形的分割结果。具体解析脚本如下:

def construct_det_results(det_json_path):results = dict()bbox_results = dict()scores  = dict()with open(det_json_path) as f:json_data = json.load(f)for info in json_data:img_id = info["image_id"]if img_id not in results:results[img_id] = list()scores[img_id] = list()bbox_results[img_id] = list()bbox = info["bbox"]x1, y1, x2, y2 = bbox[0], bbox[1], bbox[0] + bbox[2], bbox[1] + bbox[3]segm = [[x1, y1, x2, y1, x2, y2, x1, y2]]# mask = _poly2mask(segm, img_h=1544, img_w=2064)score = info["score"]# results[img_id].append([x1, y1, x2, y2, score])results[img_id].append(segm)bbox_results[img_id].append([x1, y1, x2, y2])scores[img_id].append(score)return results, scores, bbox_results

输入的是预测结果的json文件路径,输出是所有图像分割结果、得分和目标框结果。

根据image_id计算单个图像的TP、FP结果

本步骤的具体内容如下:

  1. 根据置信度阈值对预测框进行筛选;
  2. 将所有的多边形转换为mask,用于后续计算IoU;
  3. 得到tp和fp;
  4. 可视化fp和fn结果;

将多边形转换为mask

    if img_id in det_results:# for dt in det_results[img_id]:for idx, score in enumerate(det_scores[img_id]):# score = dt[-1]if score > conf_thrs:mask = _poly2mask(det_results[img_id][idx], img_h=1544, img_w=2064)det_bboxes.append(mask)det_thrs_scores.append(score)plot_det_bboxes.append(det_tmp_bboxes[img_id][idx])if img_id in gt_results:     for segm in gt_results[img_id]:mask = _poly2mask(segm, img_h=1544, img_w=2064)   gt_bboxes.append(mask)plot_gt_bboxes = gt_tmp_bboxes[img_id]

通过_poly2mask函数可以将多边形转换为mask,_poly2mask函数的实现如下。

def _poly2mask(mask_ann, img_h, img_w):"""Private function to convert masks represented with polygon tobitmaps.Args:mask_ann (list | dict): Polygon mask annotation input.img_h (int): The height of output mask.img_w (int): The width of output mask.Returns:numpy.ndarray: The decode bitmap mask of shape (img_h, img_w)."""if isinstance(mask_ann, list):# polygon -- a single object might consist of multiple parts# we merge all parts into one mask rle coderles = maskUtils.frPyObjects(mask_ann, img_h, img_w)rle = maskUtils.merge(rles)elif isinstance(mask_ann['counts'], list):# uncompressed RLErle = maskUtils.frPyObjects(mask_ann, img_h, img_w)else:# rlerle = mask_annmask = maskUtils.decode(rle)return mask

计算单张图像的TP和FP

本文中使用tpfp_default函数实现该功能,具体实现如下:

def tpfp_default(det_bboxes,gt_bboxes,gt_bboxes_ignore=None,det_thrs_scores=None,iou_thr=0.5,area_ranges=None):"""Check if detected bboxes are true positive or false positive.Args:det_bbox (ndarray): Detected bboxes of this image, of shape (m, 5).gt_bboxes (ndarray): GT bboxes of this image, of shape (n, 4).gt_bboxes_ignore (ndarray): Ignored gt bboxes of this image,of shape (k, 4). Default: Noneiou_thr (float): IoU threshold to be considered as matched.Default: 0.5.area_ranges (list[tuple] | None): Range of bbox areas to be evaluated,in the format [(min1, max1), (min2, max2), ...]. Default: None.Returns:tuple[np.ndarray]: (tp, fp) whose elements are 0 and 1. The shape ofeach array is (num_scales, m)."""# an indicator of ignored gtsgt_ignore_inds = np.concatenate((np.zeros(gt_bboxes.shape[0], dtype=np.bool),np.ones(gt_bboxes_ignore.shape[0], dtype=np.bool)))# stack gt_bboxes and gt_bboxes_ignore for convenience# gt_bboxes = np.vstack((gt_bboxes, gt_bboxes_ignore))num_dets = det_bboxes.shape[0]num_gts = gt_bboxes.shape[0]if area_ranges is None:area_ranges = [(None, None)]num_scales = len(area_ranges)# tp and fp are of shape (num_scales, num_gts), each row is tp or fp of# a certain scaletp = np.zeros((num_scales, num_dets), dtype=np.float32)fp = np.zeros((num_scales, num_dets), dtype=np.float32)# if there is no gt bboxes in this image, then all det bboxes# within area range are false positivesif gt_bboxes.shape[0] == 0:if area_ranges == [(None, None)]:fp[...] = 1else:det_areas = (det_bboxes[:, 2] - det_bboxes[:, 0] + 1) * (det_bboxes[:, 3] - det_bboxes[:, 1] + 1)for i, (min_area, max_area) in enumerate(area_ranges):fp[i, (det_areas >= min_area) & (det_areas < max_area)] = 1return tp, fp# ious = bbox_overlaps(det_bboxes, gt_bboxes)# ious = mask_overlaps(det_bboxes, gt_bboxes)ious = mask_wraper(det_bboxes, gt_bboxes)# for each det, the max iou with all gtsious_max = ious.max(axis=1)# for each det, which gt overlaps most with itious_argmax = ious.argmax(axis=1)# sort all dets in descending order by scores# sort_inds = np.argsort(-det_bboxes[:, -1])sort_inds = np.argsort(-det_thrs_scores)for k, (min_area, max_area) in enumerate(area_ranges):gt_covered = np.zeros(num_gts, dtype=bool)# if no area range is specified, gt_area_ignore is all Falseif min_area is None:gt_area_ignore = np.zeros_like(gt_ignore_inds, dtype=bool)else:gt_areas = (gt_bboxes[:, 2] - gt_bboxes[:, 0] + 1) * (gt_bboxes[:, 3] - gt_bboxes[:, 1] + 1)gt_area_ignore = (gt_areas < min_area) | (gt_areas >= max_area)for i in sort_inds:if ious_max[i] >= iou_thr:matched_gt = ious_argmax[i]     # 得到对应的GT索引if not (gt_ignore_inds[matched_gt]or gt_area_ignore[matched_gt]):if not gt_covered[matched_gt]:gt_covered[matched_gt] = True   # GT占位tp[k, i] = 1            else:fp[k, i] = 1# otherwise ignore this detected bbox, tp = 0, fp = 0elif min_area is None:fp[k, i] = 1else:bbox = det_bboxes[i, :4]area = (bbox[2] - bbox[0] + 1) * (bbox[3] - bbox[1] + 1)if area >= min_area and area < max_area:fp[k, i] = 1return tp, fp

过程是先获取预测框和GT框之间的IoU矩阵,然后按照置信度排序,将每个预测框分配给GT框得到tp和fp结果。

计算mask的IoU

IoU的定义都是一样的,计算公式如下:
在这里插入图片描述
基于mask计算IoU的实验也非常简单,代码如下:

def mask_overlaps(bboxes1, bboxes2, mode='iou'):assert mode in ['iou', 'iof']bboxes1 = bboxes1.astype(np.bool_)bboxes2 = bboxes2.astype(np.bool_)intersection = np.logical_and(bboxes1, bboxes2)union = np.logical_or(bboxes1, bboxes2)intersection_area = np.sum(intersection)union_area = np.sum(union)iou = intersection_area / union_areareturn iou

而计算预测框和GT之间的IoU矩阵实现如下:

def mask_wraper(bboxes1, bboxes2, mode='iou'):rows = bboxes1.shape[0]     # gtcols = bboxes2.shape[0]     # detious = np.zeros((rows, cols), dtype=np.float32)if rows * cols == 0:return iousfor i in range(rows):for j in range(cols):iou = mask_overlaps(bboxes1[i], bboxes2[j])ious[i, j] = ioureturn ious

至此,通过上述过程就能获取到单张图像的tp和fp结果。

可视化FP和FN结果

此外,我们需要分析模型的badcase,因此,可以将FP和FN的结果可视化出来,我这里是直接将存在问题的图像所有预测框和GT框都画出来了。

    if VIS and (fp > 0 or tp < gt):img_data, path = draw_bbox(img_id=img_id, cocoGt=cocoGt, det_bboxes=plot_det_bboxes, gt_bboxes=plot_gt_bboxes)if fp > 0:save_dir = os.path.join(VIS_ROOT, "tmp/FP/")os.makedirs(save_dir, exist_ok=True)cv2.imwrite(os.path.join(save_dir, os.path.basename(path)+".jpg"), img_data, [int(cv2.IMWRITE_JPEG_QUALITY), 30])if tp < gt:save_dir = os.path.join(VIS_ROOT, "tmp/FN/")os.makedirs(save_dir, exist_ok=True)cv2.imwrite(os.path.join(save_dir, os.path.basename(path)+".jpg"), img_data,[int(cv2.IMWRITE_JPEG_QUALITY), 30])

画框的实现如下:

def draw_bbox(img_id, cocoGt, det_bboxes, gt_bboxes):path = cocoGt.loadImgs(ids=[img_id])[0]["file_name"]img_path = os.path.join(IMG_ROOT, path)img_data = cv2.imread(img_path)for box in det_bboxes:# color_mask = (0, 0, 255)# color_mask = np.array([0, 0, 255], dtype=np.int8)# bbox_mask = box.astype(np.bool)cv2.rectangle(img_data, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (0, 0, 255), 3)# img_data[bbox_mask] = img_data[bbox_mask] * 0.5 + color_mask * 0.5for box in gt_bboxes:# color_mask = np.array([0, 255, 0], dtype=np.int8)# bbox_mask = box.astype(np.bool)# img_data[bbox_mask] = img_data[bbox_mask] * 0.5 + color_mask * 0.5cv2.rectangle(img_data, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (0, 255, 0), 3)return img_data, path

至此,我们实现了单张图像的所有业务逻辑。

多线程计算所有图像结果

通过multiprocessing启动一个进程池来加速结果计算。

def eval_multiprocessing(img_ids):from multiprocessing import Poolpool = Pool(processes=16)results = pool.map(eval_pr, img_ids)# 关闭进程池,表示不再接受新的任务pool.close()# 等待所有任务完成pool.join()return np.sum(np.array(results), axis=0)

计算PR结果

返回所有图像的TP和FP结果之后,就可以计算precision和recall值了。

gt, tp, fp = eval_multiprocessing(img_ids)
eps = np.finfo(np.float32).eps
recalls = tp / np.maximum(gt, eps)
precisions = tp / np.maximum((tp + fp), eps)print("conf_thrs:{:.3f} iou_thrs:{:.3f}, gt:{:d}, TP={:d}, FP={:d}, P={:.3f}, R={:.3f}".format(conf_thrs, iou_thrs, gt, tp, fp, precisions, recalls))

最后,也附上整个实现代码,方便后续复现或者参考。

from multiprocessing import Pool
import os
import numpy as np
import json
from pycocotools.coco import COCO
import cv2
from pycocotools import mask as maskUtilsdef bbox_overlaps(bboxes1, bboxes2, mode='iou'):"""Calculate the ious between each bbox of bboxes1 and bboxes2.Args:bboxes1(ndarray): shape (n, 4)bboxes2(ndarray): shape (k, 4)mode(str): iou (intersection over union) or iof (intersectionover foreground)Returns:ious(ndarray): shape (n, k)"""assert mode in ['iou', 'iof']bboxes1 = bboxes1.astype(np.float32)bboxes2 = bboxes2.astype(np.float32)rows = bboxes1.shape[0]cols = bboxes2.shape[0]ious = np.zeros((rows, cols), dtype=np.float32)if rows * cols == 0:return iousexchange = Falseif bboxes1.shape[0] > bboxes2.shape[0]:bboxes1, bboxes2 = bboxes2, bboxes1ious = np.zeros((cols, rows), dtype=np.float32)exchange = Truearea1 = (bboxes1[:, 2] - bboxes1[:, 0] + 1) * (bboxes1[:, 3] - bboxes1[:, 1] + 1)area2 = (bboxes2[:, 2] - bboxes2[:, 0] + 1) * (bboxes2[:, 3] - bboxes2[:, 1] + 1)for i in range(bboxes1.shape[0]):x_start = np.maximum(bboxes1[i, 0], bboxes2[:, 0])y_start = np.maximum(bboxes1[i, 1], bboxes2[:, 1])x_end = np.minimum(bboxes1[i, 2], bboxes2[:, 2])y_end = np.minimum(bboxes1[i, 3], bboxes2[:, 3])overlap = np.maximum(x_end - x_start + 1, 0) * np.maximum(y_end - y_start + 1, 0)if mode == 'iou':union = area1[i] + area2 - overlapelse:union = area1[i] if not exchange else area2ious[i, :] = overlap / unionif exchange:ious = ious.Treturn iousdef mask_wraper(bboxes1, bboxes2, mode='iou'):rows = bboxes1.shape[0]     # gtcols = bboxes2.shape[0]     # detious = np.zeros((rows, cols), dtype=np.float32)if rows * cols == 0:return iousfor i in range(rows):for j in range(cols):iou = mask_overlaps(bboxes1[i], bboxes2[j])ious[i, j] = ioureturn iousdef mask_overlaps(bboxes1, bboxes2, mode='iou'):assert mode in ['iou', 'iof']bboxes1 = bboxes1.astype(np.bool_)bboxes2 = bboxes2.astype(np.bool_)intersection = np.logical_and(bboxes1, bboxes2)union = np.logical_or(bboxes1, bboxes2)intersection_area = np.sum(intersection)union_area = np.sum(union)iou = intersection_area / union_areareturn ioudef tpfp_default(det_bboxes,gt_bboxes,gt_bboxes_ignore=None,det_thrs_scores=None,iou_thr=0.5,area_ranges=None):"""Check if detected bboxes are true positive or false positive.Args:det_bbox (ndarray): Detected bboxes of this image, of shape (m, 5).gt_bboxes (ndarray): GT bboxes of this image, of shape (n, 4).gt_bboxes_ignore (ndarray): Ignored gt bboxes of this image,of shape (k, 4). Default: Noneiou_thr (float): IoU threshold to be considered as matched.Default: 0.5.area_ranges (list[tuple] | None): Range of bbox areas to be evaluated,in the format [(min1, max1), (min2, max2), ...]. Default: None.Returns:tuple[np.ndarray]: (tp, fp) whose elements are 0 and 1. The shape ofeach array is (num_scales, m)."""# an indicator of ignored gtsgt_ignore_inds = np.concatenate((np.zeros(gt_bboxes.shape[0], dtype=np.bool),np.ones(gt_bboxes_ignore.shape[0], dtype=np.bool)))# stack gt_bboxes and gt_bboxes_ignore for convenience# gt_bboxes = np.vstack((gt_bboxes, gt_bboxes_ignore))num_dets = det_bboxes.shape[0]num_gts = gt_bboxes.shape[0]if area_ranges is None:area_ranges = [(None, None)]num_scales = len(area_ranges)# tp and fp are of shape (num_scales, num_gts), each row is tp or fp of# a certain scaletp = np.zeros((num_scales, num_dets), dtype=np.float32)fp = np.zeros((num_scales, num_dets), dtype=np.float32)# if there is no gt bboxes in this image, then all det bboxes# within area range are false positivesif gt_bboxes.shape[0] == 0:if area_ranges == [(None, None)]:fp[...] = 1else:det_areas = (det_bboxes[:, 2] - det_bboxes[:, 0] + 1) * (det_bboxes[:, 3] - det_bboxes[:, 1] + 1)for i, (min_area, max_area) in enumerate(area_ranges):fp[i, (det_areas >= min_area) & (det_areas < max_area)] = 1return tp, fp# ious = bbox_overlaps(det_bboxes, gt_bboxes)# ious = mask_overlaps(det_bboxes, gt_bboxes)ious = mask_wraper(det_bboxes, gt_bboxes)# for each det, the max iou with all gtsious_max = ious.max(axis=1)# for each det, which gt overlaps most with itious_argmax = ious.argmax(axis=1)# sort all dets in descending order by scores# sort_inds = np.argsort(-det_bboxes[:, -1])sort_inds = np.argsort(-det_thrs_scores)for k, (min_area, max_area) in enumerate(area_ranges):gt_covered = np.zeros(num_gts, dtype=bool)# if no area range is specified, gt_area_ignore is all Falseif min_area is None:gt_area_ignore = np.zeros_like(gt_ignore_inds, dtype=bool)else:gt_areas = (gt_bboxes[:, 2] - gt_bboxes[:, 0] + 1) * (gt_bboxes[:, 3] - gt_bboxes[:, 1] + 1)gt_area_ignore = (gt_areas < min_area) | (gt_areas >= max_area)for i in sort_inds:if ious_max[i] >= iou_thr:matched_gt = ious_argmax[i]     # 得到对应的GT索引if not (gt_ignore_inds[matched_gt]or gt_area_ignore[matched_gt]):if not gt_covered[matched_gt]:gt_covered[matched_gt] = True   # GT占位tp[k, i] = 1            else:fp[k, i] = 1# otherwise ignore this detected bbox, tp = 0, fp = 0elif min_area is None:fp[k, i] = 1else:bbox = det_bboxes[i, :4]area = (bbox[2] - bbox[0] + 1) * (bbox[3] - bbox[1] + 1)if area >= min_area and area < max_area:fp[k, i] = 1return tp, fpdef _poly2mask(mask_ann, img_h, img_w):"""Private function to convert masks represented with polygon tobitmaps.Args:mask_ann (list | dict): Polygon mask annotation input.img_h (int): The height of output mask.img_w (int): The width of output mask.Returns:numpy.ndarray: The decode bitmap mask of shape (img_h, img_w)."""if isinstance(mask_ann, list):# polygon -- a single object might consist of multiple parts# we merge all parts into one mask rle coderles = maskUtils.frPyObjects(mask_ann, img_h, img_w)rle = maskUtils.merge(rles)elif isinstance(mask_ann['counts'], list):# uncompressed RLErle = maskUtils.frPyObjects(mask_ann, img_h, img_w)else:# rlerle = mask_annmask = maskUtils.decode(rle)return maskdef construct_det_results(det_json_path):results = dict()bbox_results = dict()scores  = dict()with open(det_json_path) as f:json_data = json.load(f)for info in json_data:img_id = info["image_id"]if img_id not in results:results[img_id] = list()scores[img_id] = list()bbox_results[img_id] = list()bbox = info["bbox"]x1, y1, x2, y2 = bbox[0], bbox[1], bbox[0] + bbox[2], bbox[1] + bbox[3]segm = [[x1, y1, x2, y1, x2, y2, x1, y2]]# mask = _poly2mask(segm, img_h=1544, img_w=2064)score = info["score"]# results[img_id].append([x1, y1, x2, y2, score])results[img_id].append(segm)bbox_results[img_id].append([x1, y1, x2, y2])scores[img_id].append(score)return results, scores, bbox_resultsdef construct_gt_results(gt_json_path):results = dict()bbox_results = dict()cocoGt = COCO(annotation_file=gt_json_path)# cat_ids = cocoGt.getCatIds()img_ids = cocoGt.getImgIds()for id in img_ids:anno_ids = cocoGt.getAnnIds(imgIds=[id])annotations = cocoGt.loadAnns(ids=anno_ids)for info in annotations:img_id = info["image_id"]if img_id not in results:results[img_id] = list()bbox_results[img_id] = list()bbox = info["bbox"]x1, y1, x2, y2 = bbox[0], bbox[1], bbox[0] + bbox[2], bbox[1] + bbox[3]# results[img_id].append([x1, y1, x2, y2])# mask = _poly2mask(info["segmentation"], img_h=1544, img_w=2064)results[img_id].append(info["segmentation"])bbox_results[img_id].append([x1, y1, x2, y2])return results, img_ids, cocoGt, bbox_resultsdef draw_bbox(img_id, cocoGt, det_bboxes, gt_bboxes):path = cocoGt.loadImgs(ids=[img_id])[0]["file_name"]img_path = os.path.join(IMG_ROOT, path)img_data = cv2.imread(img_path)for box in det_bboxes:# color_mask = (0, 0, 255)# color_mask = np.array([0, 0, 255], dtype=np.int8)# bbox_mask = box.astype(np.bool)cv2.rectangle(img_data, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (0, 0, 255), 3)# img_data[bbox_mask] = img_data[bbox_mask] * 0.5 + color_mask * 0.5for box in gt_bboxes:# color_mask = np.array([0, 255, 0], dtype=np.int8)# bbox_mask = box.astype(np.bool)# img_data[bbox_mask] = img_data[bbox_mask] * 0.5 + color_mask * 0.5cv2.rectangle(img_data, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (0, 255, 0), 3)return img_data, pathdef eval_pr(img_id):tp, fp, gt = 0, 0, 0gt_bboxes, gt_ignore = [], []det_bboxes = list()gt_bboxes = list()det_thrs_scores = list()plot_det_bboxes = list()plot_gt_bboxes  = list()if img_id in det_results:# for dt in det_results[img_id]:for idx, score in enumerate(det_scores[img_id]):# score = dt[-1]if score > conf_thrs:mask = _poly2mask(det_results[img_id][idx], img_h=1544, img_w=2064)det_bboxes.append(mask)det_thrs_scores.append(score)plot_det_bboxes.append(det_tmp_bboxes[img_id][idx])if img_id in gt_results:     for segm in gt_results[img_id]:mask = _poly2mask(segm, img_h=1544, img_w=2064)   gt_bboxes.append(mask)plot_gt_bboxes = gt_tmp_bboxes[img_id]det_bboxes = np.array(det_bboxes)gt_bboxes = np.array(gt_bboxes)det_thrs_scores = np.array(det_thrs_scores)gt_ignore = np.array(gt_ignore).reshape(-1, 4)if len(gt_bboxes) > 0:if len(det_bboxes) == 0:tp, fp = 0, 0 else:tp, fp = tpfp_default(det_bboxes, gt_bboxes, gt_ignore, det_thrs_scores, iou_thrs)tp, fp = np.sum(tp == 1), np.sum(fp == 1)gt = len(gt_bboxes)else:fp = len(det_bboxes)if VIS and (fp > 0 or tp < gt):img_data, path = draw_bbox(img_id=img_id, cocoGt=cocoGt, det_bboxes=plot_det_bboxes, gt_bboxes=plot_gt_bboxes)if fp > 0:save_dir = os.path.join(VIS_ROOT, "tmp/FP/")os.makedirs(save_dir, exist_ok=True)cv2.imwrite(os.path.join(save_dir, os.path.basename(path)+".jpg"), img_data, [int(cv2.IMWRITE_JPEG_QUALITY), 30])if tp < gt:save_dir = os.path.join(VIS_ROOT, "tmp/FN/")os.makedirs(save_dir, exist_ok=True)cv2.imwrite(os.path.join(save_dir, os.path.basename(path)+".jpg"), img_data,[int(cv2.IMWRITE_JPEG_QUALITY), 30])return gt, tp, fpdef eval_multiprocessing(img_ids):from multiprocessing import Poolpool = Pool(processes=16)results = pool.map(eval_pr, img_ids)# 关闭进程池,表示不再接受新的任务pool.close()# 等待所有任务完成pool.join()return np.sum(np.array(results), axis=0)if __name__ == '__main__':VIS = 1IMG_ROOT = "gaotie_data"VIS_ROOT = 'badcase-vis-test-2/'conf_thrs = 0.5iou_thrs  = 0.001det_json_path = "results.bbox.json"gt_json_path  = "datasets/gaotie_test_data/annotations/test5_seg_removed.json"det_results, det_scores, det_tmp_bboxes = construct_det_results(det_json_path)gt_results, img_ids, cocoGt, gt_tmp_bboxes  = construct_gt_results(gt_json_path)gt, tp, fp = eval_multiprocessing(img_ids)eps = np.finfo(np.float32).epsrecalls = tp / np.maximum(gt, eps)precisions = tp / np.maximum((tp + fp), eps)print("conf_thrs:{:.3f} iou_thrs:{:.3f}, gt:{:d}, TP={:d}, FP={:d}, P={:.3f}, R={:.3f}".format(conf_thrs, iou_thrs, gt, tp, fp, precisions, recalls))

总结

本文针对目标检测任务中GT存在多边形情况下给出了如下计算数据集的PR结果,基于mask来计算IoU,与语义分割计算IoU的思路一致,最后也给出了所有的实现代码作为参考。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/215611.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

gittee使用教学

一、git简介 Git是一个开源的分布式版本控制系统&#xff0c;用于敏捷高效的处理任何大小项目的版本管理。 核心功能&#xff1a; 项目的版本管理 团队协同开发 二、准备工作 1、下载 Git 2、除了选择安装位置以外&#xff0c;其他都无脑安装 3、检查一下安装情况 win…

C语言-每日刷题练习

[蓝桥杯 2013 省 B] 翻硬币 题目背景 小明正在玩一个“翻硬币”的游戏。 题目描述 桌上放着排成一排的若干硬币。我们用 * 表示正面&#xff0c;用 o 表示反面&#xff08;是小写字母&#xff0c;不是零&#xff09;&#xff0c;比如可能情形是 **oo***oooo&#xff0c;如果…

【从零开始学习JVM | 第三篇】类的生命周期(高频面试)

前言&#xff1a; 在Java编程中&#xff0c;类的生命周期是指类从被加载到内存中开始&#xff0c;到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。 在本文中&#xff0c;我们将深入探讨类的生命周期&#xff0c;从类加载到…

Docker Container(容器)——6

目录&#xff1a; 什么是容器&#xff1f;容器生活案例&#xff1f;为什么需要容器&#xff1f;容器的生命周期 容器 OOM容器异常退出容器暂停容器命令清单容器命令详解 docker createdocker rundocker psdocker logsdocker attachdocker execdocker startdocker stopdocker r…

用Sketch for Mac轻松创作无限可能的矢量绘图

在如今的数码时代&#xff0c;矢量绘图软件成为了许多设计师和创意爱好者的必备工具。而在众多的矢量绘图软件中&#xff0c;Sketch for Mac无疑是最受欢迎的一款。它以其简洁易用的界面和强大的功能&#xff0c;让用户能够轻松创作出无限可能的矢量图形。 首先&#xff0c;Sk…

Jmeter入门

一、下载jmeter 官网下载 下载之后解压&#xff0c;在目录/bin下面找到jmeter.bat双击之后即可启动Jmeter。 二、使用 如下左图&#xff0c;选择语言为中文&#xff0c;可以修改测试计划的名称。如下右图&#xff0c;添加线程组 添加线程组 添加http请求 路径传参方式 …

vue3-自定义组件的使用及传值!!!

1.在vue项目中创建一个自定义组件&#xff08;大多数页面中相同的样式&#xff0c;将其封装到组件中&#xff0c;可重复使用&#xff09; 2.将公共组件引入到你想使用的页面中 结果显示如下&#xff1a; 3.为公共组件传值 4.公共组件接收值&#xff0c;显示在组件上 注意事项&a…

wappalyzer基于插件的网站开发技术解析工具

一、wappalyzer 解释&#xff1a;这是一款强大的工具&#xff0c;其主要能提供一种快速、可靠地检测网站所使用技术栈的方法&#xff0c;也就说说&#xff0c;服务器发来的信息都会被它剖析&#xff0c;然后分析出前端的技术栈&#xff0c;有时后端所使用的技术栈如果网页特征…

串口通信(1)-硬件知识

本文讲解串口通信的硬件知识。让读者快速了解硬件知识&#xff0c;为下一步编写代码做基础。 目录 一、概述 二、串口通信分类 2.1信息的传送方向进行分类 2.2同步通信和异步通信 三、串口协议 3.1 RS232 3.1.1 电气特性 3.1.2 连接器的机械特性 3.1.3 连接类型 3.1…

2023全国职业院校技能大赛信息安全管理与评估正式赛(模块三CTF)

全国职业院校技能大赛高等职业教育组信息安全管理与评估 \任务书\ 模块三 网络安全渗透、理论技能与职业素养 极安云科专注技能竞赛&#xff0c;包含网络建设与运维和信息安全管理与评估两大赛项&#xff0c;及各大CTF&#xff0c;基于两大赛项提供全面的系统性培训&#xf…

Pipenv环境配置+Pytest运行

环境配置 使用Pipenv进行虚拟环境管理&#xff0c;Pipfile为依赖模块管理文件。 安装pipenv&#xff1a;brew install pipenv根项目根目录下执行命令创建虚拟环境&#xff1a; pipenv install在Pycharm中指定项目运行的虚拟环境 &#xff1a;File->Settings->Project:-…

有趣的数学 数学建模入门三 数学建模入门示例两例 利用微积分求解

一、入门示例1 1、问题描述 某宾馆有150间客房&#xff0c;经过一段时间的经营&#xff0c;该宾馆经理得到一些数据&#xff1a;如果每间客房定价为200元&#xff0c;入住率为55&#xff05;&#xff1b;定价为180元&#xff0c;入住率为65&#xff05;&#xff1b;定价为160元…

第 5 部分 — LLM中红队的深入分析:数学和实证方法

一、说明 大型语言模型 (LLM) 领域正在迅速发展&#xff0c;需要强大的红队策略来确保其安全性和可靠性。 红队是一种模拟对抗性攻击来识别漏洞的方法&#xff0c;需要对理论基础和实际应用有深入的了解。在这个分析中&#xff0c;我深入研究了复杂的数学模型&#xff0c;并提供…

【k8s】使用Finalizers控制k8s资源删除

文章目录 词汇表基本删除操作Finalizers是什么&#xff1f;Owner References又是什么&#xff1f;强制删除命名空间参考 你有没有在使用k8s过程中遇到过这种情况: 通过kubectl delete指令删除一些资源时&#xff0c;一直处于Terminating状态。 这是为什么呢&#xff1f; 本文将…

自下而上-存储全栈(TiDB/RockDB/SPDK/fuse/ceph/NVMe/ext4)存储技术专家成长路线

数字化时代的到来带来了大规模数据的产生&#xff0c;各行各业都面临着数据爆炸的挑战。 随着云计算、物联网、人工智能等新兴技术的发展&#xff0c;对存储技术的需求也越来越多样化。不同应用场景对存储的容量、性能、可靠性和成本等方面都有不同的要求。具备存储技术知识和技…

关系型数据库-SQLite介绍

优点&#xff1a; 1>sqlite占用的内存和cpu资源较少 2>源代码开源&#xff0c;完全免费 3>检索速度上十几兆、几十兆的数据库sqlite很快&#xff0c;但是上G的时候最慢 4>管理简单&#xff0c;几乎无需管理。灵巧、快速和可靠性高 5>功能简…

JVM 性能调优

概述篇 面试题 讲讲你理解的性能评价及测试指标&#xff1f;&#xff08;瓜子&#xff09; 生产环境中的问题 生产环境发生了内存溢出该如何处理&#xff1f;生产环境应该给服务器分配多少内存合适&#xff1f;如何对垃圾回收器的性能进行调优&#xff1f;生产环境CPU负载飙高…

为什么近期白酒市场股票暴跌?2024年中高端酒企发展如何撬动市场?

为什么近期白酒市场股票暴跌&#xff1f;2024年中高端酒企发展如何撬动市场&#xff1f; 近期白酒市场股票暴跌的原因主要有两个方面&#xff1a;一是宏观经济环境的不景气&#xff0c;导致投资者对白酒行业的未来发展持谨慎态度&#xff1b;二是白酒市场竞争激烈&#xff0c;龙…

喜报|电巢科技获批教育部第三期供需对接就业育人项目100项!

项目获批 近日&#xff0c;教育部公布了第三期供需对接就业育人项目立项名单&#xff0c;电巢科技获批此次供需对接就业育人项目100项&#xff0c;其中包括定向人才培养培训项目40项、就业实习基地项目40项、人力资源提升项目20项。 在教育部高校学生司指导下&#xff0c;电巢…

Rust 学习

Rust 官网&#xff1a;https://www.rust-lang.org/zh-CN/ 1、Rust 简介 Rust 是一门注重安全&#xff08;safety&#xff09;、速度&#xff08;speed&#xff09;和并发&#xff08;concurrency&#xff09;的现代系统编程语言。Rust 通过内存安全来实现以上目标&#xff0c;但…