Source code for pyEdgeEval.datasets.sbd

#!/usr/bin/env python3

from copy import deepcopy

import cv2
import numpy as np
from PIL import Image

# from skimage.io import imread

from pyEdgeEval.common.multi_label.evaluate_boundaries import (
    evaluate_boundaries_threshold,
)
from pyEdgeEval.common.utils import check_thresholds
from pyEdgeEval.utils import loadmat, sparse2numpy


[docs]def load_sbd_gt_cls_mat(path: str, new_loader: bool = False): """Load Per Class Ground Truth Annoation""" if new_loader: gt = loadmat(path, True)["GTcls"] boundaries = gt["Boundaries"] # list[csc_matrix] segmentation = gt["Segmentation"] # np.ndarray(h, w) present_categories = gt["CategoriesPresent"] # np.ndarray() assert len(segmentation.shape) == 2 h, w = segmentation.shape num_categories = len(boundaries) np_boundaries = np.zeros([num_categories, h, w], dtype=np.uint8) for cat in range(num_categories): np_boundaries[cat] = sparse2numpy(boundaries[cat]) else: gt = loadmat(path, False)["GTcls"][0, 0] boundaries = gt[0] # np.ndarray(np.ndarray(csc_matrix)) segmentation = gt[1] # np.ndarray(h, w) present_categories = gt[2].squeeze() # np.ndarray() assert len(segmentation.shape) == 2 h, w = segmentation.shape num_categories = len(boundaries) np_boundaries = np.zeros([num_categories, h, w], dtype=np.uint8) for cat in range(num_categories): np_boundaries[cat] = sparse2numpy(boundaries[cat][0]) # TODO: do checks for present categories? return np_boundaries, segmentation, present_categories
[docs]def load_sbd_gt_inst_mat(path: str, new_loader: bool = False): """Load Per Instance Ground Truth Annotation NOTE: seg and bdry is indexed by instances (not category) """ if new_loader: gt = loadmat(path, True)["GTinst"] segmentation = gt["Segmentation"] boundaries = gt["Boundaries"] categories = gt["Categories"] assert len(segmentation.shape) == 2 h, w = segmentation.shape num_categories = len(boundaries) np_boundaries = np.zeros([num_categories, h, w], dtype=np.uint8) for cat in range(num_categories): np_boundaries[cat] = sparse2numpy(boundaries[cat]) else: gt = loadmat(path, False)["GTinst"][0, 0] segmentation = gt[0] boundaries = gt[1] categories = gt[2].squeeze() assert len(segmentation.shape) == 2 h, w = segmentation.shape num_categories = len(boundaries) np_boundaries = np.zeros([num_categories, h, w], dtype=np.uint8) for cat in range(num_categories): np_boundaries[cat] = sparse2numpy(boundaries[cat][0]) return np_boundaries, segmentation, categories
[docs]def load_instance_insensitive_gt(cls_path: str, new_loader: bool = False): return load_sbd_gt_cls_mat(cls_path, new_loader)
[docs]def load_instance_sensitive_gt( cls_path: str, inst_path: str, new_loader: bool = False ): cls_bdry, cls_seg, present_cats = load_sbd_gt_cls_mat(cls_path, new_loader) inst_bdry, inst_seg, inst_cats = load_sbd_gt_inst_mat(inst_path, new_loader) for inst_cat in inst_cats: assert ( inst_cat in present_cats ), f"ERR: instance category of {inst_cat} not available in {present_cats}" # create a new bdry map and add instance boundary pixels new_bdry = deepcopy(cls_bdry) for i, inst_cat in enumerate(inst_cats): _inst_bdry = inst_bdry[i] # NOTE: inst_cat is indexed from 1 new_bdry[inst_cat - 1] = new_bdry[inst_cat - 1] | _inst_bdry return new_bdry, cls_seg, present_cats
def _evaluate_single( cls_path, inst_path, pred_path, category, scale, max_dist, thresholds, apply_thinning, apply_nms, kill_internal, skip_if_nonexistent, **kwargs, ): """Evaluate a single sample (sub-routine) NOTE: don't set defaults for easier debugging """ # checks and converts thresholds thresholds = check_thresholds(thresholds) if inst_path: # instance sensitive edge, seg, present_categories = load_instance_sensitive_gt( cls_path=cls_path, inst_path=inst_path, ) else: # instance insensitive edge, seg, present_categories = load_instance_insensitive_gt( cls_path=cls_path, ) # TODO: would be smart to return here using `present_categories` cat_idx = category - 1 cat_edge = edge[cat_idx, :, :] # rescale (h, w) = cat_edge.shape height, width = int(h * scale + 0.5), int(w * scale + 0.5) cat_edge = cv2.resize(cat_edge, (width, height), cv2.INTER_NEAREST) if kill_internal: seg = cv2.resize(seg, (width, height), cv2.INTER_NEAREST) # SBD explicitly starts from 1 (because of matlab) # 0 is the background cat_seg = seg == category else: cat_seg = None # load pred pred = Image.open(pred_path) pred = pred.resize((width, height), Image.Resampling.NEAREST) # pred = imread(pred_path) # pred = cv2.resize(pred, (width, height), cv2.INTER_NEAREST) pred = np.array(pred) pred = (pred / 255).astype(float) # evaluate multi-label boundaries count_r, sum_r, count_p, sum_p = evaluate_boundaries_threshold( thresholds=thresholds, pred=pred, gt=cat_edge, gt_seg=cat_seg, max_dist=max_dist, apply_thinning=apply_thinning, kill_internal=kill_internal, skip_if_nonexistent=skip_if_nonexistent, apply_nms=apply_nms, nms_kwargs=dict( r=1, s=5, m=1.01, half_prec=False, ), ) return count_r, sum_r, count_p, sum_p
[docs]def sbd_eval_single(kwargs): """Wrapper function to unpack all the kwargs""" return _evaluate_single(**kwargs)