# Copyright (c) Facebook, Inc. and its affiliates. # All rights reserved. # # This source code is licensed under the license found in the # LICENSE file in the root directory of this source tree. # This is a modified version of cocoeval.py where we also have the densepose evaluation. __author__ = "tsungyi" import copy import datetime import logging import numpy as np import pickle import time from collections import defaultdict from enum import Enum from typing import Any, Dict, Tuple import scipy.spatial.distance as ssd import torch import torch.nn.functional as F from pycocotools import mask as maskUtils from scipy.io import loadmat from scipy.ndimage import zoom as spzoom from detectron2.utils.file_io import PathManager from densepose.converters.chart_output_to_chart_result import resample_uv_tensors_to_bbox from densepose.converters.segm_to_mask import ( resample_coarse_segm_tensor_to_bbox, resample_fine_and_coarse_segm_tensors_to_bbox, ) from densepose.modeling.cse.utils import squared_euclidean_distance_matrix from densepose.structures import DensePoseDataRelative from densepose.structures.mesh import create_mesh logger = logging.getLogger(__name__) class DensePoseEvalMode(str, Enum): # use both masks and geodesic distances (GPS * IOU) to compute scores GPSM = "gpsm" # use only geodesic distances (GPS) to compute scores GPS = "gps" # use only masks (IOU) to compute scores IOU = "iou" class DensePoseDataMode(str, Enum): # use estimated IUV data (default mode) IUV_DT = "iuvdt" # use ground truth IUV data IUV_GT = "iuvgt" # use ground truth labels I and set UV to 0 I_GT_UV_0 = "igtuv0" # use ground truth labels I and estimated UV coordinates I_GT_UV_DT = "igtuvdt" # use estimated labels I and set UV to 0 I_DT_UV_0 = "idtuv0" class DensePoseCocoEval: # Interface for evaluating detection on the Microsoft COCO dataset. # # The usage for CocoEval is as follows: # cocoGt=..., cocoDt=... # load dataset and results # E = CocoEval(cocoGt,cocoDt); # initialize CocoEval object # E.params.recThrs = ...; # set parameters as desired # E.evaluate(); # run per image evaluation # E.accumulate(); # accumulate per image results # E.summarize(); # display summary metrics of results # For example usage see evalDemo.m and http://mscoco.org/. # # The evaluation parameters are as follows (defaults in brackets): # imgIds - [all] N img ids to use for evaluation # catIds - [all] K cat ids to use for evaluation # iouThrs - [.5:.05:.95] T=10 IoU thresholds for evaluation # recThrs - [0:.01:1] R=101 recall thresholds for evaluation # areaRng - [...] A=4 object area ranges for evaluation # maxDets - [1 10 100] M=3 thresholds on max detections per image # iouType - ['segm'] set iouType to 'segm', 'bbox', 'keypoints' or 'densepose' # iouType replaced the now DEPRECATED useSegm parameter. # useCats - [1] if true use category labels for evaluation # Note: if useCats=0 category labels are ignored as in proposal scoring. # Note: multiple areaRngs [Ax2] and maxDets [Mx1] can be specified. # # evaluate(): evaluates detections on every image and every category and # concats the results into the "evalImgs" with fields: # dtIds - [1xD] id for each of the D detections (dt) # gtIds - [1xG] id for each of the G ground truths (gt) # dtMatches - [TxD] matching gt id at each IoU or 0 # gtMatches - [TxG] matching dt id at each IoU or 0 # dtScores - [1xD] confidence of each dt # gtIgnore - [1xG] ignore flag for each gt # dtIgnore - [TxD] ignore flag for each dt at each IoU # # accumulate(): accumulates the per-image, per-category evaluation # results in "evalImgs" into the dictionary "eval" with fields: # params - parameters used for evaluation # date - date evaluation was performed # counts - [T,R,K,A,M] parameter dimensions (see above) # precision - [TxRxKxAxM] precision for every evaluation setting # recall - [TxKxAxM] max recall for every evaluation setting # Note: precision and recall==-1 for settings with no gt objects. # # See also coco, mask, pycocoDemo, pycocoEvalDemo # # Microsoft COCO Toolbox. version 2.0 # Data, paper, and tutorials available at: http://mscoco.org/ # Code written by Piotr Dollar and Tsung-Yi Lin, 2015. # Licensed under the Simplified BSD License [see coco/license.txt] def __init__( self, cocoGt=None, cocoDt=None, iouType: str = "densepose", multi_storage=None, embedder=None, dpEvalMode: DensePoseEvalMode = DensePoseEvalMode.GPS, dpDataMode: DensePoseDataMode = DensePoseDataMode.IUV_DT, ): """ Initialize CocoEval using coco APIs for gt and dt :param cocoGt: coco object with ground truth annotations :param cocoDt: coco object with detection results :return: None """ self.cocoGt = cocoGt # ground truth COCO API self.cocoDt = cocoDt # detections COCO API self.multi_storage = multi_storage self.embedder = embedder self._dpEvalMode = dpEvalMode self._dpDataMode = dpDataMode self.evalImgs = defaultdict(list) # per-image per-category eval results [KxAxI] self.eval = {} # accumulated evaluation results self._gts = defaultdict(list) # gt for evaluation self._dts = defaultdict(list) # dt for evaluation self.params = Params(iouType=iouType) # parameters self._paramsEval = {} # parameters for evaluation self.stats = [] # result summarization self.ious = {} # ious between all gts and dts if cocoGt is not None: self.params.imgIds = sorted(cocoGt.getImgIds()) self.params.catIds = sorted(cocoGt.getCatIds()) self.ignoreThrBB = 0.7 self.ignoreThrUV = 0.9 def _loadGEval(self): smpl_subdiv_fpath = PathManager.get_local_path( "https://dl.fbaipublicfiles.com/densepose/data/SMPL_subdiv.mat" ) pdist_transform_fpath = PathManager.get_local_path( "https://dl.fbaipublicfiles.com/densepose/data/SMPL_SUBDIV_TRANSFORM.mat" ) pdist_matrix_fpath = PathManager.get_local_path( "https://dl.fbaipublicfiles.com/densepose/data/Pdist_matrix.pkl", timeout_sec=120 ) SMPL_subdiv = loadmat(smpl_subdiv_fpath) self.PDIST_transform = loadmat(pdist_transform_fpath) self.PDIST_transform = self.PDIST_transform["index"].squeeze() UV = np.array([SMPL_subdiv["U_subdiv"], SMPL_subdiv["V_subdiv"]]).squeeze() ClosestVertInds = np.arange(UV.shape[1]) + 1 self.Part_UVs = [] self.Part_ClosestVertInds = [] for i in np.arange(24): self.Part_UVs.append(UV[:, SMPL_subdiv["Part_ID_subdiv"].squeeze() == (i + 1)]) self.Part_ClosestVertInds.append( ClosestVertInds[SMPL_subdiv["Part_ID_subdiv"].squeeze() == (i + 1)] ) with open(pdist_matrix_fpath, "rb") as hFile: arrays = pickle.load(hFile, encoding="latin1") self.Pdist_matrix = arrays["Pdist_matrix"] self.Part_ids = np.array(SMPL_subdiv["Part_ID_subdiv"].squeeze()) # Mean geodesic distances for parts. self.Mean_Distances = np.array([0, 0.351, 0.107, 0.126, 0.237, 0.173, 0.142, 0.128, 0.150]) # Coarse Part labels. self.CoarseParts = np.array( [0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8] ) def _prepare(self): """ Prepare ._gts and ._dts for evaluation based on params :return: None """ def _toMask(anns, coco): # modify ann['segmentation'] by reference for ann in anns: # safeguard for invalid segmentation annotation; # annotations containing empty lists exist in the posetrack # dataset. This is not a correct segmentation annotation # in terms of COCO format; we need to deal with it somehow segm = ann["segmentation"] if type(segm) == list and len(segm) == 0: ann["segmentation"] = None continue rle = coco.annToRLE(ann) ann["segmentation"] = rle def _getIgnoreRegion(iid, coco): img = coco.imgs[iid] if "ignore_regions_x" not in img.keys(): return None if len(img["ignore_regions_x"]) == 0: return None rgns_merged = [ [v for xy in zip(region_x, region_y) for v in xy] for region_x, region_y in zip(img["ignore_regions_x"], img["ignore_regions_y"]) ] rles = maskUtils.frPyObjects(rgns_merged, img["height"], img["width"]) rle = maskUtils.merge(rles) return maskUtils.decode(rle) def _checkIgnore(dt, iregion): if iregion is None: return True bb = np.array(dt["bbox"]).astype(int) x1, y1, x2, y2 = bb[0], bb[1], bb[0] + bb[2], bb[1] + bb[3] x2 = min([x2, iregion.shape[1]]) y2 = min([y2, iregion.shape[0]]) if bb[2] * bb[3] == 0: return False crop_iregion = iregion[y1:y2, x1:x2] if crop_iregion.sum() == 0: return True if "densepose" not in dt.keys(): # filtering boxes return crop_iregion.sum() / bb[2] / bb[3] < self.ignoreThrBB # filtering UVs ignoremask = np.require(crop_iregion, requirements=["F"]) mask = self._extract_mask(dt) uvmask = np.require(np.asarray(mask > 0), dtype=np.uint8, requirements=["F"]) uvmask_ = maskUtils.encode(uvmask) ignoremask_ = maskUtils.encode(ignoremask) uviou = maskUtils.iou([uvmask_], [ignoremask_], [1])[0] return uviou < self.ignoreThrUV p = self.params if p.useCats: gts = self.cocoGt.loadAnns(self.cocoGt.getAnnIds(imgIds=p.imgIds, catIds=p.catIds)) dts = self.cocoDt.loadAnns(self.cocoDt.getAnnIds(imgIds=p.imgIds, catIds=p.catIds)) else: gts = self.cocoGt.loadAnns(self.cocoGt.getAnnIds(imgIds=p.imgIds)) dts = self.cocoDt.loadAnns(self.cocoDt.getAnnIds(imgIds=p.imgIds)) imns = self.cocoGt.loadImgs(p.imgIds) self.size_mapping = {} for im in imns: self.size_mapping[im["id"]] = [im["height"], im["width"]] # if iouType == 'uv', add point gt annotations if p.iouType == "densepose": self._loadGEval() # convert ground truth to mask if iouType == 'segm' if p.iouType == "segm": _toMask(gts, self.cocoGt) _toMask(dts, self.cocoDt) # set ignore flag for gt in gts: gt["ignore"] = gt["ignore"] if "ignore" in gt else 0 gt["ignore"] = "iscrowd" in gt and gt["iscrowd"] if p.iouType == "keypoints": gt["ignore"] = (gt["num_keypoints"] == 0) or gt["ignore"] if p.iouType == "densepose": gt["ignore"] = ("dp_x" in gt) == 0 if p.iouType == "segm": gt["ignore"] = gt["segmentation"] is None self._gts = defaultdict(list) # gt for evaluation self._dts = defaultdict(list) # dt for evaluation self._igrgns = defaultdict(list) for gt in gts: iid = gt["image_id"] if iid not in self._igrgns.keys(): self._igrgns[iid] = _getIgnoreRegion(iid, self.cocoGt) if _checkIgnore(gt, self._igrgns[iid]): self._gts[iid, gt["category_id"]].append(gt) for dt in dts: iid = dt["image_id"] if (iid not in self._igrgns) or _checkIgnore(dt, self._igrgns[iid]): self._dts[iid, dt["category_id"]].append(dt) self.evalImgs = defaultdict(list) # per-image per-category evaluation results self.eval = {} # accumulated evaluation results def evaluate(self): """ Run per image evaluation on given images and store results (a list of dict) in self.evalImgs :return: None """ tic = time.time() logger.info("Running per image DensePose evaluation... {}".format(self.params.iouType)) p = self.params # add backward compatibility if useSegm is specified in params if p.useSegm is not None: p.iouType = "segm" if p.useSegm == 1 else "bbox" logger.info("useSegm (deprecated) is not None. Running DensePose evaluation") p.imgIds = list(np.unique(p.imgIds)) if p.useCats: p.catIds = list(np.unique(p.catIds)) p.maxDets = sorted(p.maxDets) self.params = p self._prepare() # loop through images, area range, max detection number catIds = p.catIds if p.useCats else [-1] if p.iouType in ["segm", "bbox"]: computeIoU = self.computeIoU elif p.iouType == "keypoints": computeIoU = self.computeOks elif p.iouType == "densepose": computeIoU = self.computeOgps if self._dpEvalMode in {DensePoseEvalMode.GPSM, DensePoseEvalMode.IOU}: self.real_ious = { (imgId, catId): self.computeDPIoU(imgId, catId) for imgId in p.imgIds for catId in catIds } self.ious = { (imgId, catId): computeIoU(imgId, catId) for imgId in p.imgIds for catId in catIds } evaluateImg = self.evaluateImg maxDet = p.maxDets[-1] self.evalImgs = [ evaluateImg(imgId, catId, areaRng, maxDet) for catId in catIds for areaRng in p.areaRng for imgId in p.imgIds ] self._paramsEval = copy.deepcopy(self.params) toc = time.time() logger.info("DensePose evaluation DONE (t={:0.2f}s).".format(toc - tic)) def getDensePoseMask(self, polys): maskGen = np.zeros([256, 256]) stop = min(len(polys) + 1, 15) for i in range(1, stop): if polys[i - 1]: currentMask = maskUtils.decode(polys[i - 1]) maskGen[currentMask > 0] = i return maskGen def _generate_rlemask_on_image(self, mask, imgId, data): bbox_xywh = np.array(data["bbox"]) x, y, w, h = bbox_xywh im_h, im_w = self.size_mapping[imgId] im_mask = np.zeros((im_h, im_w), dtype=np.uint8) if mask is not None: x0 = max(int(x), 0) x1 = min(int(x + w), im_w, int(x) + mask.shape[1]) y0 = max(int(y), 0) y1 = min(int(y + h), im_h, int(y) + mask.shape[0]) y = int(y) x = int(x) im_mask[y0:y1, x0:x1] = mask[y0 - y : y1 - y, x0 - x : x1 - x] im_mask = np.require(np.asarray(im_mask > 0), dtype=np.uint8, requirements=["F"]) rle_mask = maskUtils.encode(np.array(im_mask[:, :, np.newaxis], order="F"))[0] return rle_mask def computeDPIoU(self, imgId, catId): p = self.params if p.useCats: gt = self._gts[imgId, catId] dt = self._dts[imgId, catId] else: gt = [_ for cId in p.catIds for _ in self._gts[imgId, cId]] dt = [_ for cId in p.catIds for _ in self._dts[imgId, cId]] if len(gt) == 0 and len(dt) == 0: return [] inds = np.argsort([-d["score"] for d in dt], kind="mergesort") dt = [dt[i] for i in inds] if len(dt) > p.maxDets[-1]: dt = dt[0 : p.maxDets[-1]] gtmasks = [] for g in gt: if DensePoseDataRelative.S_KEY in g: # convert DensePose mask to a binary mask mask = np.minimum(self.getDensePoseMask(g[DensePoseDataRelative.S_KEY]), 1.0) _, _, w, h = g["bbox"] scale_x = float(max(w, 1)) / mask.shape[1] scale_y = float(max(h, 1)) / mask.shape[0] mask = spzoom(mask, (scale_y, scale_x), order=1, prefilter=False) mask = np.array(mask > 0.5, dtype=np.uint8) rle_mask = self._generate_rlemask_on_image(mask, imgId, g) elif "segmentation" in g: segmentation = g["segmentation"] if isinstance(segmentation, list) and segmentation: # polygons im_h, im_w = self.size_mapping[imgId] rles = maskUtils.frPyObjects(segmentation, im_h, im_w) rle_mask = maskUtils.merge(rles) elif isinstance(segmentation, dict): if isinstance(segmentation["counts"], list): # uncompressed RLE im_h, im_w = self.size_mapping[imgId] rle_mask = maskUtils.frPyObjects(segmentation, im_h, im_w) else: # compressed RLE rle_mask = segmentation else: rle_mask = self._generate_rlemask_on_image(None, imgId, g) else: rle_mask = self._generate_rlemask_on_image(None, imgId, g) gtmasks.append(rle_mask) dtmasks = [] for d in dt: mask = self._extract_mask(d) mask = np.require(np.asarray(mask > 0), dtype=np.uint8, requirements=["F"]) rle_mask = self._generate_rlemask_on_image(mask, imgId, d) dtmasks.append(rle_mask) # compute iou between each dt and gt region iscrowd = [int(o.get("iscrowd", 0)) for o in gt] iousDP = maskUtils.iou(dtmasks, gtmasks, iscrowd) return iousDP def computeIoU(self, imgId, catId): p = self.params if p.useCats: gt = self._gts[imgId, catId] dt = self._dts[imgId, catId] else: gt = [_ for cId in p.catIds for _ in self._gts[imgId, cId]] dt = [_ for cId in p.catIds for _ in self._dts[imgId, cId]] if len(gt) == 0 and len(dt) == 0: return [] inds = np.argsort([-d["score"] for d in dt], kind="mergesort") dt = [dt[i] for i in inds] if len(dt) > p.maxDets[-1]: dt = dt[0 : p.maxDets[-1]] if p.iouType == "segm": g = [g["segmentation"] for g in gt if g["segmentation"] is not None] d = [d["segmentation"] for d in dt if d["segmentation"] is not None] elif p.iouType == "bbox": g = [g["bbox"] for g in gt] d = [d["bbox"] for d in dt] else: raise Exception("unknown iouType for iou computation") # compute iou between each dt and gt region iscrowd = [int(o.get("iscrowd", 0)) for o in gt] ious = maskUtils.iou(d, g, iscrowd) return ious def computeOks(self, imgId, catId): p = self.params # dimension here should be Nxm gts = self._gts[imgId, catId] dts = self._dts[imgId, catId] inds = np.argsort([-d["score"] for d in dts], kind="mergesort") dts = [dts[i] for i in inds] if len(dts) > p.maxDets[-1]: dts = dts[0 : p.maxDets[-1]] # if len(gts) == 0 and len(dts) == 0: if len(gts) == 0 or len(dts) == 0: return [] ious = np.zeros((len(dts), len(gts))) sigmas = ( np.array( [ 0.26, 0.25, 0.25, 0.35, 0.35, 0.79, 0.79, 0.72, 0.72, 0.62, 0.62, 1.07, 1.07, 0.87, 0.87, 0.89, 0.89, ] ) / 10.0 ) vars = (sigmas * 2) ** 2 k = len(sigmas) # compute oks between each detection and ground truth object for j, gt in enumerate(gts): # create bounds for ignore regions(double the gt bbox) g = np.array(gt["keypoints"]) xg = g[0::3] yg = g[1::3] vg = g[2::3] k1 = np.count_nonzero(vg > 0) bb = gt["bbox"] x0 = bb[0] - bb[2] x1 = bb[0] + bb[2] * 2 y0 = bb[1] - bb[3] y1 = bb[1] + bb[3] * 2 for i, dt in enumerate(dts): d = np.array(dt["keypoints"]) xd = d[0::3] yd = d[1::3] if k1 > 0: # measure the per-keypoint distance if keypoints visible dx = xd - xg dy = yd - yg else: # measure minimum distance to keypoints in (x0,y0) & (x1,y1) z = np.zeros(k) dx = np.max((z, x0 - xd), axis=0) + np.max((z, xd - x1), axis=0) dy = np.max((z, y0 - yd), axis=0) + np.max((z, yd - y1), axis=0) e = (dx**2 + dy**2) / vars / (gt["area"] + np.spacing(1)) / 2 if k1 > 0: e = e[vg > 0] ious[i, j] = np.sum(np.exp(-e)) / e.shape[0] return ious def _extract_mask(self, dt: Dict[str, Any]) -> np.ndarray: if "densepose" in dt: densepose_results_quantized = dt["densepose"] return densepose_results_quantized.labels_uv_uint8[0].numpy() elif "cse_mask" in dt: return dt["cse_mask"] elif "coarse_segm" in dt: dy = max(int(dt["bbox"][3]), 1) dx = max(int(dt["bbox"][2]), 1) return ( F.interpolate( dt["coarse_segm"].unsqueeze(0), (dy, dx), mode="bilinear", align_corners=False, ) .squeeze(0) .argmax(0) .numpy() .astype(np.uint8) ) elif "record_id" in dt: assert ( self.multi_storage is not None ), f"Storage record id encountered in a detection {dt}, but no storage provided!" record = self.multi_storage.get(dt["rank"], dt["record_id"]) coarse_segm = record["coarse_segm"] dy = max(int(dt["bbox"][3]), 1) dx = max(int(dt["bbox"][2]), 1) return ( F.interpolate( coarse_segm.unsqueeze(0), (dy, dx), mode="bilinear", align_corners=False, ) .squeeze(0) .argmax(0) .numpy() .astype(np.uint8) ) else: raise Exception(f"No mask data in the detection: {dt}") raise ValueError('The prediction dict needs to contain either "densepose" or "cse_mask"') def _extract_iuv( self, densepose_data: np.ndarray, py: np.ndarray, px: np.ndarray, gt: Dict[str, Any] ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: """ Extract arrays of I, U and V values at given points as numpy arrays given the data mode stored in self._dpDataMode """ if self._dpDataMode == DensePoseDataMode.IUV_DT: # estimated labels and UV (default) ipoints = densepose_data[0, py, px] upoints = densepose_data[1, py, px] / 255.0 # convert from uint8 by /255. vpoints = densepose_data[2, py, px] / 255.0 elif self._dpDataMode == DensePoseDataMode.IUV_GT: # ground truth ipoints = np.array(gt["dp_I"]) upoints = np.array(gt["dp_U"]) vpoints = np.array(gt["dp_V"]) elif self._dpDataMode == DensePoseDataMode.I_GT_UV_0: # ground truth labels, UV = 0 ipoints = np.array(gt["dp_I"]) upoints = upoints * 0.0 vpoints = vpoints * 0.0 elif self._dpDataMode == DensePoseDataMode.I_GT_UV_DT: # ground truth labels, estimated UV ipoints = np.array(gt["dp_I"]) upoints = densepose_data[1, py, px] / 255.0 # convert from uint8 by /255. vpoints = densepose_data[2, py, px] / 255.0 elif self._dpDataMode == DensePoseDataMode.I_DT_UV_0: # estimated labels, UV = 0 ipoints = densepose_data[0, py, px] upoints = upoints * 0.0 vpoints = vpoints * 0.0 else: raise ValueError(f"Unknown data mode: {self._dpDataMode}") return ipoints, upoints, vpoints def computeOgps_single_pair(self, dt, gt, py, px, pt_mask): if "densepose" in dt: ipoints, upoints, vpoints = self.extract_iuv_from_quantized(dt, gt, py, px, pt_mask) return self.computeOgps_single_pair_iuv(dt, gt, ipoints, upoints, vpoints) elif "u" in dt: ipoints, upoints, vpoints = self.extract_iuv_from_raw(dt, gt, py, px, pt_mask) return self.computeOgps_single_pair_iuv(dt, gt, ipoints, upoints, vpoints) elif "record_id" in dt: assert ( self.multi_storage is not None ), f"Storage record id encountered in detection {dt}, but no storage provided!" record = self.multi_storage.get(dt["rank"], dt["record_id"]) record["bbox"] = dt["bbox"] if "u" in record: ipoints, upoints, vpoints = self.extract_iuv_from_raw(record, gt, py, px, pt_mask) return self.computeOgps_single_pair_iuv(dt, gt, ipoints, upoints, vpoints) elif "embedding" in record: return self.computeOgps_single_pair_cse( dt, gt, py, px, pt_mask, record["coarse_segm"], record["embedding"], record["bbox"], ) else: raise Exception(f"Unknown record format: {record}") elif "embedding" in dt: return self.computeOgps_single_pair_cse( dt, gt, py, px, pt_mask, dt["coarse_segm"], dt["embedding"], dt["bbox"] ) raise Exception(f"Unknown detection format: {dt}") def extract_iuv_from_quantized(self, dt, gt, py, px, pt_mask): densepose_results_quantized = dt["densepose"] ipoints, upoints, vpoints = self._extract_iuv( densepose_results_quantized.labels_uv_uint8.numpy(), py, px, gt ) ipoints[pt_mask == -1] = 0 return ipoints, upoints, vpoints def extract_iuv_from_raw(self, dt, gt, py, px, pt_mask): labels_dt = resample_fine_and_coarse_segm_tensors_to_bbox( dt["fine_segm"].unsqueeze(0), dt["coarse_segm"].unsqueeze(0), dt["bbox"], ) uv = resample_uv_tensors_to_bbox( dt["u"].unsqueeze(0), dt["v"].unsqueeze(0), labels_dt.squeeze(0), dt["bbox"] ) labels_uv_uint8 = torch.cat((labels_dt.byte(), (uv * 255).clamp(0, 255).byte())) ipoints, upoints, vpoints = self._extract_iuv(labels_uv_uint8.numpy(), py, px, gt) ipoints[pt_mask == -1] = 0 return ipoints, upoints, vpoints def computeOgps_single_pair_iuv(self, dt, gt, ipoints, upoints, vpoints): cVertsGT, ClosestVertsGTTransformed = self.findAllClosestVertsGT(gt) cVerts = self.findAllClosestVertsUV(upoints, vpoints, ipoints) # Get pairwise geodesic distances between gt and estimated mesh points. dist = self.getDistancesUV(ClosestVertsGTTransformed, cVerts) # Compute the Ogps measure. # Find the mean geodesic normalization distance for # each GT point, based on which part it is on. Current_Mean_Distances = self.Mean_Distances[ self.CoarseParts[self.Part_ids[cVertsGT[cVertsGT > 0].astype(int) - 1]] ] return dist, Current_Mean_Distances def computeOgps_single_pair_cse( self, dt, gt, py, px, pt_mask, coarse_segm, embedding, bbox_xywh_abs ): # 0-based mesh vertex indices cVertsGT = torch.as_tensor(gt["dp_vertex"], dtype=torch.int64) # label for each pixel of the bbox, [H, W] tensor of long labels_dt = resample_coarse_segm_tensor_to_bbox( coarse_segm.unsqueeze(0), bbox_xywh_abs ).squeeze(0) x, y, w, h = bbox_xywh_abs # embedding for each pixel of the bbox, [D, H, W] tensor of float32 embedding = F.interpolate( embedding.unsqueeze(0), (int(h), int(w)), mode="bilinear", align_corners=False ).squeeze(0) # valid locations py, px py_pt = torch.from_numpy(py[pt_mask > -1]) px_pt = torch.from_numpy(px[pt_mask > -1]) cVerts = torch.ones_like(cVertsGT) * -1 cVerts[pt_mask > -1] = self.findClosestVertsCse( embedding, py_pt, px_pt, labels_dt, gt["ref_model"] ) # Get pairwise geodesic distances between gt and estimated mesh points. dist = self.getDistancesCse(cVertsGT, cVerts, gt["ref_model"]) # normalize distances if (gt["ref_model"] == "smpl_27554") and ("dp_I" in gt): Current_Mean_Distances = self.Mean_Distances[ self.CoarseParts[np.array(gt["dp_I"], dtype=int)] ] else: Current_Mean_Distances = 0.255 return dist, Current_Mean_Distances def computeOgps(self, imgId, catId): p = self.params # dimension here should be Nxm g = self._gts[imgId, catId] d = self._dts[imgId, catId] inds = np.argsort([-d_["score"] for d_ in d], kind="mergesort") d = [d[i] for i in inds] if len(d) > p.maxDets[-1]: d = d[0 : p.maxDets[-1]] # if len(gts) == 0 and len(dts) == 0: if len(g) == 0 or len(d) == 0: return [] ious = np.zeros((len(d), len(g))) # compute opgs between each detection and ground truth object # sigma = self.sigma #0.255 # dist = 0.3m corresponds to ogps = 0.5 # 1 # dist = 0.3m corresponds to ogps = 0.96 # 1.45 # dist = 1.7m (person height) corresponds to ogps = 0.5) for j, gt in enumerate(g): if not gt["ignore"]: g_ = gt["bbox"] for i, dt in enumerate(d): # dy = int(dt["bbox"][3]) dx = int(dt["bbox"][2]) dp_x = np.array(gt["dp_x"]) * g_[2] / 255.0 dp_y = np.array(gt["dp_y"]) * g_[3] / 255.0 py = (dp_y + g_[1] - dt["bbox"][1]).astype(int) px = (dp_x + g_[0] - dt["bbox"][0]).astype(int) # pts = np.zeros(len(px)) pts[px >= dx] = -1 pts[py >= dy] = -1 pts[px < 0] = -1 pts[py < 0] = -1 if len(pts) < 1: ogps = 0.0 elif np.max(pts) == -1: ogps = 0.0 else: px[pts == -1] = 0 py[pts == -1] = 0 dists_between_matches, dist_norm_coeffs = self.computeOgps_single_pair( dt, gt, py, px, pts ) # Compute gps ogps_values = np.exp( -(dists_between_matches**2) / (2 * (dist_norm_coeffs**2)) ) # ogps = np.mean(ogps_values) if len(ogps_values) > 0 else 0.0 ious[i, j] = ogps gbb = [gt["bbox"] for gt in g] dbb = [dt["bbox"] for dt in d] # compute iou between each dt and gt region iscrowd = [int(o.get("iscrowd", 0)) for o in g] ious_bb = maskUtils.iou(dbb, gbb, iscrowd) return ious, ious_bb def evaluateImg(self, imgId, catId, aRng, maxDet): """ perform evaluation for single category and image :return: dict (single image results) """ p = self.params if p.useCats: gt = self._gts[imgId, catId] dt = self._dts[imgId, catId] else: gt = [_ for cId in p.catIds for _ in self._gts[imgId, cId]] dt = [_ for cId in p.catIds for _ in self._dts[imgId, cId]] if len(gt) == 0 and len(dt) == 0: return None for g in gt: # g['_ignore'] = g['ignore'] if g["ignore"] or (g["area"] < aRng[0] or g["area"] > aRng[1]): g["_ignore"] = True else: g["_ignore"] = False # sort dt highest score first, sort gt ignore last gtind = np.argsort([g["_ignore"] for g in gt], kind="mergesort") gt = [gt[i] for i in gtind] dtind = np.argsort([-d["score"] for d in dt], kind="mergesort") dt = [dt[i] for i in dtind[0:maxDet]] iscrowd = [int(o.get("iscrowd", 0)) for o in gt] # load computed ious if p.iouType == "densepose": # print('Checking the length', len(self.ious[imgId, catId])) # if len(self.ious[imgId, catId]) == 0: # print(self.ious[imgId, catId]) ious = ( self.ious[imgId, catId][0][:, gtind] if len(self.ious[imgId, catId]) > 0 else self.ious[imgId, catId] ) ioubs = ( self.ious[imgId, catId][1][:, gtind] if len(self.ious[imgId, catId]) > 0 else self.ious[imgId, catId] ) if self._dpEvalMode in {DensePoseEvalMode.GPSM, DensePoseEvalMode.IOU}: iousM = ( self.real_ious[imgId, catId][:, gtind] if len(self.real_ious[imgId, catId]) > 0 else self.real_ious[imgId, catId] ) else: ious = ( self.ious[imgId, catId][:, gtind] if len(self.ious[imgId, catId]) > 0 else self.ious[imgId, catId] ) T = len(p.iouThrs) G = len(gt) D = len(dt) gtm = np.zeros((T, G)) dtm = np.zeros((T, D)) gtIg = np.array([g["_ignore"] for g in gt]) dtIg = np.zeros((T, D)) if np.all(gtIg) and p.iouType == "densepose": dtIg = np.logical_or(dtIg, True) if len(ious) > 0: # and not p.iouType == 'densepose': for tind, t in enumerate(p.iouThrs): for dind, d in enumerate(dt): # information about best match so far (m=-1 -> unmatched) iou = min([t, 1 - 1e-10]) m = -1 for gind, _g in enumerate(gt): # if this gt already matched, and not a crowd, continue if gtm[tind, gind] > 0 and not iscrowd[gind]: continue # if dt matched to reg gt, and on ignore gt, stop if m > -1 and gtIg[m] == 0 and gtIg[gind] == 1: break if p.iouType == "densepose": if self._dpEvalMode == DensePoseEvalMode.GPSM: new_iou = np.sqrt(iousM[dind, gind] * ious[dind, gind]) elif self._dpEvalMode == DensePoseEvalMode.IOU: new_iou = iousM[dind, gind] elif self._dpEvalMode == DensePoseEvalMode.GPS: new_iou = ious[dind, gind] else: new_iou = ious[dind, gind] if new_iou < iou: continue if new_iou == 0.0: continue # if match successful and best so far, store appropriately iou = new_iou m = gind # if match made store id of match for both dt and gt if m == -1: continue dtIg[tind, dind] = gtIg[m] dtm[tind, dind] = gt[m]["id"] gtm[tind, m] = d["id"] if p.iouType == "densepose": if not len(ioubs) == 0: for dind, d in enumerate(dt): # information about best match so far (m=-1 -> unmatched) if dtm[tind, dind] == 0: ioub = 0.8 m = -1 for gind, _g in enumerate(gt): # if this gt already matched, and not a crowd, continue if gtm[tind, gind] > 0 and not iscrowd[gind]: continue # continue to next gt unless better match made if ioubs[dind, gind] < ioub: continue # if match successful and best so far, store appropriately ioub = ioubs[dind, gind] m = gind # if match made store id of match for both dt and gt if m > -1: dtIg[:, dind] = gtIg[m] if gtIg[m]: dtm[tind, dind] = gt[m]["id"] gtm[tind, m] = d["id"] # set unmatched detections outside of area range to ignore a = np.array([d["area"] < aRng[0] or d["area"] > aRng[1] for d in dt]).reshape((1, len(dt))) dtIg = np.logical_or(dtIg, np.logical_and(dtm == 0, np.repeat(a, T, 0))) # store results for given image and category # print('Done with the function', len(self.ious[imgId, catId])) return { "image_id": imgId, "category_id": catId, "aRng": aRng, "maxDet": maxDet, "dtIds": [d["id"] for d in dt], "gtIds": [g["id"] for g in gt], "dtMatches": dtm, "gtMatches": gtm, "dtScores": [d["score"] for d in dt], "gtIgnore": gtIg, "dtIgnore": dtIg, } def accumulate(self, p=None): """ Accumulate per image evaluation results and store the result in self.eval :param p: input params for evaluation :return: None """ logger.info("Accumulating evaluation results...") tic = time.time() if not self.evalImgs: logger.info("Please run evaluate() first") # allows input customized parameters if p is None: p = self.params p.catIds = p.catIds if p.useCats == 1 else [-1] T = len(p.iouThrs) R = len(p.recThrs) K = len(p.catIds) if p.useCats else 1 A = len(p.areaRng) M = len(p.maxDets) precision = -(np.ones((T, R, K, A, M))) # -1 for the precision of absent categories recall = -(np.ones((T, K, A, M))) # create dictionary for future indexing logger.info("Categories: {}".format(p.catIds)) _pe = self._paramsEval catIds = _pe.catIds if _pe.useCats else [-1] setK = set(catIds) setA = set(map(tuple, _pe.areaRng)) setM = set(_pe.maxDets) setI = set(_pe.imgIds) # get inds to evaluate k_list = [n for n, k in enumerate(p.catIds) if k in setK] m_list = [m for n, m in enumerate(p.maxDets) if m in setM] a_list = [n for n, a in enumerate(map(lambda x: tuple(x), p.areaRng)) if a in setA] i_list = [n for n, i in enumerate(p.imgIds) if i in setI] I0 = len(_pe.imgIds) A0 = len(_pe.areaRng) # retrieve E at each category, area range, and max number of detections for k, k0 in enumerate(k_list): Nk = k0 * A0 * I0 for a, a0 in enumerate(a_list): Na = a0 * I0 for m, maxDet in enumerate(m_list): E = [self.evalImgs[Nk + Na + i] for i in i_list] E = [e for e in E if e is not None] if len(E) == 0: continue dtScores = np.concatenate([e["dtScores"][0:maxDet] for e in E]) # different sorting method generates slightly different results. # mergesort is used to be consistent as Matlab implementation. inds = np.argsort(-dtScores, kind="mergesort") dtm = np.concatenate([e["dtMatches"][:, 0:maxDet] for e in E], axis=1)[:, inds] dtIg = np.concatenate([e["dtIgnore"][:, 0:maxDet] for e in E], axis=1)[:, inds] gtIg = np.concatenate([e["gtIgnore"] for e in E]) npig = np.count_nonzero(gtIg == 0) if npig == 0: continue tps = np.logical_and(dtm, np.logical_not(dtIg)) fps = np.logical_and(np.logical_not(dtm), np.logical_not(dtIg)) tp_sum = np.cumsum(tps, axis=1).astype(dtype=float) fp_sum = np.cumsum(fps, axis=1).astype(dtype=float) for t, (tp, fp) in enumerate(zip(tp_sum, fp_sum)): tp = np.array(tp) fp = np.array(fp) nd = len(tp) rc = tp / npig pr = tp / (fp + tp + np.spacing(1)) q = np.zeros((R,)) if nd: recall[t, k, a, m] = rc[-1] else: recall[t, k, a, m] = 0 # numpy is slow without cython optimization for accessing elements # use python array gets significant speed improvement pr = pr.tolist() q = q.tolist() for i in range(nd - 1, 0, -1): if pr[i] > pr[i - 1]: pr[i - 1] = pr[i] inds = np.searchsorted(rc, p.recThrs, side="left") try: for ri, pi in enumerate(inds): q[ri] = pr[pi] except Exception: pass precision[t, :, k, a, m] = np.array(q) logger.info( "Final: max precision {}, min precision {}".format(np.max(precision), np.min(precision)) ) self.eval = { "params": p, "counts": [T, R, K, A, M], "date": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "precision": precision, "recall": recall, } toc = time.time() logger.info("DONE (t={:0.2f}s).".format(toc - tic)) def summarize(self): """ Compute and display summary metrics for evaluation results. Note this function can *only* be applied on the default parameter setting """ def _summarize(ap=1, iouThr=None, areaRng="all", maxDets=100): p = self.params iStr = " {:<18} {} @[ {}={:<9} | area={:>6s} | maxDets={:>3d} ] = {:0.3f}" titleStr = "Average Precision" if ap == 1 else "Average Recall" typeStr = "(AP)" if ap == 1 else "(AR)" measure = "IoU" if self.params.iouType == "keypoints": measure = "OKS" elif self.params.iouType == "densepose": measure = "OGPS" iouStr = ( "{:0.2f}:{:0.2f}".format(p.iouThrs[0], p.iouThrs[-1]) if iouThr is None else "{:0.2f}".format(iouThr) ) aind = [i for i, aRng in enumerate(p.areaRngLbl) if aRng == areaRng] mind = [i for i, mDet in enumerate(p.maxDets) if mDet == maxDets] if ap == 1: # dimension of precision: [TxRxKxAxM] s = self.eval["precision"] # IoU if iouThr is not None: t = np.where(np.abs(iouThr - p.iouThrs) < 0.001)[0] s = s[t] s = s[:, :, :, aind, mind] else: # dimension of recall: [TxKxAxM] s = self.eval["recall"] if iouThr is not None: t = np.where(np.abs(iouThr - p.iouThrs) < 0.001)[0] s = s[t] s = s[:, :, aind, mind] if len(s[s > -1]) == 0: mean_s = -1 else: mean_s = np.mean(s[s > -1]) logger.info(iStr.format(titleStr, typeStr, measure, iouStr, areaRng, maxDets, mean_s)) return mean_s def _summarizeDets(): stats = np.zeros((12,)) stats[0] = _summarize(1) stats[1] = _summarize(1, iouThr=0.5, maxDets=self.params.maxDets[2]) stats[2] = _summarize(1, iouThr=0.75, maxDets=self.params.maxDets[2]) stats[3] = _summarize(1, areaRng="small", maxDets=self.params.maxDets[2]) stats[4] = _summarize(1, areaRng="medium", maxDets=self.params.maxDets[2]) stats[5] = _summarize(1, areaRng="large", maxDets=self.params.maxDets[2]) stats[6] = _summarize(0, maxDets=self.params.maxDets[0]) stats[7] = _summarize(0, maxDets=self.params.maxDets[1]) stats[8] = _summarize(0, maxDets=self.params.maxDets[2]) stats[9] = _summarize(0, areaRng="small", maxDets=self.params.maxDets[2]) stats[10] = _summarize(0, areaRng="medium", maxDets=self.params.maxDets[2]) stats[11] = _summarize(0, areaRng="large", maxDets=self.params.maxDets[2]) return stats def _summarizeKps(): stats = np.zeros((10,)) stats[0] = _summarize(1, maxDets=20) stats[1] = _summarize(1, maxDets=20, iouThr=0.5) stats[2] = _summarize(1, maxDets=20, iouThr=0.75) stats[3] = _summarize(1, maxDets=20, areaRng="medium") stats[4] = _summarize(1, maxDets=20, areaRng="large") stats[5] = _summarize(0, maxDets=20) stats[6] = _summarize(0, maxDets=20, iouThr=0.5) stats[7] = _summarize(0, maxDets=20, iouThr=0.75) stats[8] = _summarize(0, maxDets=20, areaRng="medium") stats[9] = _summarize(0, maxDets=20, areaRng="large") return stats def _summarizeUvs(): stats = [_summarize(1, maxDets=self.params.maxDets[0])] min_threshold = self.params.iouThrs.min() if min_threshold <= 0.201: stats += [_summarize(1, maxDets=self.params.maxDets[0], iouThr=0.2)] if min_threshold <= 0.301: stats += [_summarize(1, maxDets=self.params.maxDets[0], iouThr=0.3)] if min_threshold <= 0.401: stats += [_summarize(1, maxDets=self.params.maxDets[0], iouThr=0.4)] stats += [ _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.5), _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.75), _summarize(1, maxDets=self.params.maxDets[0], areaRng="medium"), _summarize(1, maxDets=self.params.maxDets[0], areaRng="large"), _summarize(0, maxDets=self.params.maxDets[0]), _summarize(0, maxDets=self.params.maxDets[0], iouThr=0.5), _summarize(0, maxDets=self.params.maxDets[0], iouThr=0.75), _summarize(0, maxDets=self.params.maxDets[0], areaRng="medium"), _summarize(0, maxDets=self.params.maxDets[0], areaRng="large"), ] return np.array(stats) def _summarizeUvsOld(): stats = np.zeros((18,)) stats[0] = _summarize(1, maxDets=self.params.maxDets[0]) stats[1] = _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.5) stats[2] = _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.55) stats[3] = _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.60) stats[4] = _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.65) stats[5] = _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.70) stats[6] = _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.75) stats[7] = _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.80) stats[8] = _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.85) stats[9] = _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.90) stats[10] = _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.95) stats[11] = _summarize(1, maxDets=self.params.maxDets[0], areaRng="medium") stats[12] = _summarize(1, maxDets=self.params.maxDets[0], areaRng="large") stats[13] = _summarize(0, maxDets=self.params.maxDets[0]) stats[14] = _summarize(0, maxDets=self.params.maxDets[0], iouThr=0.5) stats[15] = _summarize(0, maxDets=self.params.maxDets[0], iouThr=0.75) stats[16] = _summarize(0, maxDets=self.params.maxDets[0], areaRng="medium") stats[17] = _summarize(0, maxDets=self.params.maxDets[0], areaRng="large") return stats if not self.eval: raise Exception("Please run accumulate() first") iouType = self.params.iouType if iouType in ["segm", "bbox"]: summarize = _summarizeDets elif iouType in ["keypoints"]: summarize = _summarizeKps elif iouType in ["densepose"]: summarize = _summarizeUvs self.stats = summarize() def __str__(self): self.summarize() # ================ functions for dense pose ============================== def findAllClosestVertsUV(self, U_points, V_points, Index_points): ClosestVerts = np.ones(Index_points.shape) * -1 for i in np.arange(24): # if (i + 1) in Index_points: UVs = np.array( [U_points[Index_points == (i + 1)], V_points[Index_points == (i + 1)]] ) Current_Part_UVs = self.Part_UVs[i] Current_Part_ClosestVertInds = self.Part_ClosestVertInds[i] D = ssd.cdist(Current_Part_UVs.transpose(), UVs.transpose()).squeeze() ClosestVerts[Index_points == (i + 1)] = Current_Part_ClosestVertInds[ np.argmin(D, axis=0) ] ClosestVertsTransformed = self.PDIST_transform[ClosestVerts.astype(int) - 1] ClosestVertsTransformed[ClosestVerts < 0] = 0 return ClosestVertsTransformed def findClosestVertsCse(self, embedding, py, px, mask, mesh_name): mesh_vertex_embeddings = self.embedder(mesh_name) pixel_embeddings = embedding[:, py, px].t().to(device="cuda") mask_vals = mask[py, px] edm = squared_euclidean_distance_matrix(pixel_embeddings, mesh_vertex_embeddings) vertex_indices = edm.argmin(dim=1).cpu() vertex_indices[mask_vals <= 0] = -1 return vertex_indices def findAllClosestVertsGT(self, gt): # I_gt = np.array(gt["dp_I"]) U_gt = np.array(gt["dp_U"]) V_gt = np.array(gt["dp_V"]) # # print(I_gt) # ClosestVertsGT = np.ones(I_gt.shape) * -1 for i in np.arange(24): if (i + 1) in I_gt: UVs = np.array([U_gt[I_gt == (i + 1)], V_gt[I_gt == (i + 1)]]) Current_Part_UVs = self.Part_UVs[i] Current_Part_ClosestVertInds = self.Part_ClosestVertInds[i] D = ssd.cdist(Current_Part_UVs.transpose(), UVs.transpose()).squeeze() ClosestVertsGT[I_gt == (i + 1)] = Current_Part_ClosestVertInds[np.argmin(D, axis=0)] # ClosestVertsGTTransformed = self.PDIST_transform[ClosestVertsGT.astype(int) - 1] ClosestVertsGTTransformed[ClosestVertsGT < 0] = 0 return ClosestVertsGT, ClosestVertsGTTransformed def getDistancesCse(self, cVertsGT, cVerts, mesh_name): geodists_vertices = torch.ones_like(cVertsGT) * float("inf") selected = (cVertsGT >= 0) * (cVerts >= 0) mesh = create_mesh(mesh_name, "cpu") geodists_vertices[selected] = mesh.geodists[cVertsGT[selected], cVerts[selected]] return geodists_vertices.numpy() def getDistancesUV(self, cVertsGT, cVerts): # n = 27554 dists = [] for d in range(len(cVertsGT)): if cVertsGT[d] > 0: if cVerts[d] > 0: i = cVertsGT[d] - 1 j = cVerts[d] - 1 if j == i: dists.append(0) elif j > i: ccc = i i = j j = ccc i = n - i - 1 j = n - j - 1 k = (n * (n - 1) / 2) - (n - i) * ((n - i) - 1) / 2 + j - i - 1 k = (n * n - n) / 2 - k - 1 dists.append(self.Pdist_matrix[int(k)][0]) else: i = n - i - 1 j = n - j - 1 k = (n * (n - 1) / 2) - (n - i) * ((n - i) - 1) / 2 + j - i - 1 k = (n * n - n) / 2 - k - 1 dists.append(self.Pdist_matrix[int(k)][0]) else: dists.append(np.inf) return np.atleast_1d(np.array(dists).squeeze()) class Params: """ Params for coco evaluation api """ def setDetParams(self): self.imgIds = [] self.catIds = [] # np.arange causes trouble. the data point on arange is slightly larger than the true value self.iouThrs = np.linspace(0.5, 0.95, int(np.round((0.95 - 0.5) / 0.05)) + 1, endpoint=True) self.recThrs = np.linspace(0.0, 1.00, int(np.round((1.00 - 0.0) / 0.01)) + 1, endpoint=True) self.maxDets = [1, 10, 100] self.areaRng = [ [0**2, 1e5**2], [0**2, 32**2], [32**2, 96**2], [96**2, 1e5**2], ] self.areaRngLbl = ["all", "small", "medium", "large"] self.useCats = 1 def setKpParams(self): self.imgIds = [] self.catIds = [] # np.arange causes trouble. the data point on arange is slightly larger than the true value self.iouThrs = np.linspace(0.5, 0.95, np.round((0.95 - 0.5) / 0.05) + 1, endpoint=True) self.recThrs = np.linspace(0.0, 1.00, np.round((1.00 - 0.0) / 0.01) + 1, endpoint=True) self.maxDets = [20] self.areaRng = [[0**2, 1e5**2], [32**2, 96**2], [96**2, 1e5**2]] self.areaRngLbl = ["all", "medium", "large"] self.useCats = 1 def setUvParams(self): self.imgIds = [] self.catIds = [] self.iouThrs = np.linspace(0.5, 0.95, int(np.round((0.95 - 0.5) / 0.05)) + 1, endpoint=True) self.recThrs = np.linspace(0.0, 1.00, int(np.round((1.00 - 0.0) / 0.01)) + 1, endpoint=True) self.maxDets = [20] self.areaRng = [[0**2, 1e5**2], [32**2, 96**2], [96**2, 1e5**2]] self.areaRngLbl = ["all", "medium", "large"] self.useCats = 1 def __init__(self, iouType="segm"): if iouType == "segm" or iouType == "bbox": self.setDetParams() elif iouType == "keypoints": self.setKpParams() elif iouType == "densepose": self.setUvParams() else: raise Exception("iouType not supported") self.iouType = iouType # useSegm is deprecated self.useSegm = None