Module 3_mxrcnn.lib.mx-rcnn.symdata.anchor

Expand source code
import numpy as np
from symdata.bbox import bbox_overlaps, bbox_transform


class AnchorGenerator:
    def __init__(self, feat_stride=16, anchor_scales=(8, 16, 32), anchor_ratios=(0.5, 1, 2)):
        self._num_anchors = len(anchor_scales) * len(anchor_ratios)
        self._feat_stride = feat_stride
        self._base_anchors = self._generate_base_anchors(feat_stride, np.array(anchor_scales), np.array(anchor_ratios))

    def generate(self, feat_height, feat_width):
        shift_x = np.arange(0, feat_width) * self._feat_stride
        shift_y = np.arange(0, feat_height) * self._feat_stride
        shift_x, shift_y = np.meshgrid(shift_x, shift_y)
        shifts = np.vstack((shift_x.ravel(), shift_y.ravel(), shift_x.ravel(), shift_y.ravel())).transpose()
        # add A anchors (1, A, 4) to
        # cell K shifts (K, 1, 4) to get
        # shift anchors (K, A, 4)
        # reshape to (K*A, 4) shifted anchors
        A = self._num_anchors
        K = shifts.shape[0]
        all_anchors = self._base_anchors.reshape((1, A, 4)) + shifts.reshape((1, K, 4)).transpose((1, 0, 2))
        all_anchors = all_anchors.reshape((K * A, 4))
        return all_anchors

    @staticmethod
    def _generate_base_anchors(base_size, scales, ratios):
        """
        Generate anchor (reference) windows by enumerating aspect ratios X
        scales wrt a reference (0, 0, 15, 15) window.
        """
        base_anchor = np.array([1, 1, base_size, base_size]) - 1
        ratio_anchors = AnchorGenerator._ratio_enum(base_anchor, ratios)
        anchors = np.vstack([AnchorGenerator._scale_enum(ratio_anchors[i, :], scales)
                             for i in range(ratio_anchors.shape[0])])
        return anchors

    @staticmethod
    def _whctrs(anchor):
        """
        Return width, height, x center, and y center for an anchor (window).
        """
        w = anchor[2] - anchor[0] + 1
        h = anchor[3] - anchor[1] + 1
        x_ctr = anchor[0] + 0.5 * (w - 1)
        y_ctr = anchor[1] + 0.5 * (h - 1)
        return w, h, x_ctr, y_ctr

    @staticmethod
    def _mkanchors(ws, hs, x_ctr, y_ctr):
        """
        Given a vector of widths (ws) and heights (hs) around a center
        (x_ctr, y_ctr), output a set of anchors (windows).
        """
        ws = ws[:, np.newaxis]
        hs = hs[:, np.newaxis]
        anchors = np.hstack((x_ctr - 0.5 * (ws - 1),
                             y_ctr - 0.5 * (hs - 1),
                             x_ctr + 0.5 * (ws - 1),
                             y_ctr + 0.5 * (hs - 1)))
        return anchors

    @staticmethod
    def _ratio_enum(anchor, ratios):
        """
        Enumerate a set of anchors for each aspect ratio wrt an anchor.
        """
        w, h, x_ctr, y_ctr = AnchorGenerator._whctrs(anchor)
        size = w * h
        size_ratios = size / ratios
        ws = np.round(np.sqrt(size_ratios))
        hs = np.round(ws * ratios)
        anchors = AnchorGenerator._mkanchors(ws, hs, x_ctr, y_ctr)
        return anchors

    @staticmethod
    def _scale_enum(anchor, scales):
        """
        Enumerate a set of anchors for each scale wrt an anchor.
        """
        w, h, x_ctr, y_ctr = AnchorGenerator._whctrs(anchor)
        ws = w * scales
        hs = h * scales
        anchors = AnchorGenerator._mkanchors(ws, hs, x_ctr, y_ctr)
        return anchors


class AnchorSampler:
    def __init__(self, allowed_border=0, batch_rois=256, fg_fraction=0.5, fg_overlap=0.7, bg_overlap=0.3):
        self._allowed_border = allowed_border
        self._num_batch = batch_rois
        self._num_fg = int(batch_rois * fg_fraction)
        self._fg_overlap = fg_overlap
        self._bg_overlap = bg_overlap

    def assign(self, anchors, gt_boxes, im_height, im_width):
        num_anchors = anchors.shape[0]

        # filter out padded gt_boxes
        valid_labels = np.where(gt_boxes[:, -1] > 0)[0]
        gt_boxes = gt_boxes[valid_labels]

        # filter out anchors outside the region
        inds_inside = np.where((anchors[:, 0] >= -self._allowed_border) &
                               (anchors[:, 2] < im_width + self._allowed_border) &
                               (anchors[:, 1] >= -self._allowed_border) &
                               (anchors[:, 3] < im_height + self._allowed_border))[0]
        anchors = anchors[inds_inside, :]
        num_valid = len(inds_inside)

        # label: 1 is positive, 0 is negative, -1 is dont care
        labels = np.ones((num_valid,), dtype=np.float32) * -1
        bbox_targets = np.zeros((num_valid, 4), dtype=np.float32)
        bbox_weights = np.zeros((num_valid, 4), dtype=np.float32)

        # sample for positive labels
        if gt_boxes.size > 0:
            # overlap between the anchors and the gt boxes
            # overlaps (ex, gt)
            overlaps = bbox_overlaps(anchors.astype(np.float), gt_boxes.astype(np.float))

            # fg anchors: anchor with highest overlap for each gt
            gt_max_overlaps = overlaps.max(axis=0)
            argmax_inds = np.where(overlaps == gt_max_overlaps)[0]
            labels[argmax_inds] = 1

            # fg anchors: anchor with overlap > iou thresh
            max_overlaps = overlaps.max(axis=1)
            labels[max_overlaps >= self._fg_overlap] = 1

            # bg anchors: anchor with overlap < iou thresh
            labels[max_overlaps < self._bg_overlap] = 0

            # sanity check
            fg_inds = np.where(labels == 1)[0]
            bg_inds = np.where(labels == 0)[0]
            assert len(np.intersect1d(fg_inds, bg_inds)) == 0

            # subsample positive anchors
            cur_fg = len(fg_inds)
            if cur_fg > self._num_fg:
                disable_inds = np.random.choice(fg_inds, size=(cur_fg - self._num_fg), replace=False)
                labels[disable_inds] = -1

            # subsample negative anchors
            cur_bg = len(bg_inds)
            max_neg = self._num_batch - min(self._num_fg, cur_fg)
            if cur_bg > max_neg:
                disable_inds = np.random.choice(bg_inds, size=(cur_bg - max_neg), replace=False)
                labels[disable_inds] = -1

            # assign to argmax overlap
            fg_inds = np.where(labels == 1)[0]
            argmax_overlaps = overlaps.argmax(axis=1)
            bbox_targets[fg_inds, :] = bbox_transform(anchors[fg_inds, :], gt_boxes[argmax_overlaps[fg_inds], :],
                                                      box_stds=(1.0, 1.0, 1.0, 1.0))

            # only fg anchors has bbox_targets
            bbox_weights[fg_inds, :] = 1
        else:
            # randomly draw bg anchors
            bg_inds = np.random.choice(np.arange(num_valid), size=self._num_batch, replace=False)
            labels[bg_inds] = 0

        all_labels = np.ones((num_anchors,), dtype=np.float32) * -1
        all_labels[inds_inside] = labels
        all_bbox_targets = np.zeros((num_anchors, 4), dtype=np.float32)
        all_bbox_targets[inds_inside, :] = bbox_targets
        all_bbox_weights = np.zeros((num_anchors, 4), dtype=np.float32)
        all_bbox_weights[inds_inside, :] = bbox_weights

        return all_labels, all_bbox_targets, all_bbox_weights

Classes

class AnchorGenerator (feat_stride=16, anchor_scales=(8, 16, 32), anchor_ratios=(0.5, 1, 2))
Expand source code
class AnchorGenerator:
    def __init__(self, feat_stride=16, anchor_scales=(8, 16, 32), anchor_ratios=(0.5, 1, 2)):
        self._num_anchors = len(anchor_scales) * len(anchor_ratios)
        self._feat_stride = feat_stride
        self._base_anchors = self._generate_base_anchors(feat_stride, np.array(anchor_scales), np.array(anchor_ratios))

    def generate(self, feat_height, feat_width):
        shift_x = np.arange(0, feat_width) * self._feat_stride
        shift_y = np.arange(0, feat_height) * self._feat_stride
        shift_x, shift_y = np.meshgrid(shift_x, shift_y)
        shifts = np.vstack((shift_x.ravel(), shift_y.ravel(), shift_x.ravel(), shift_y.ravel())).transpose()
        # add A anchors (1, A, 4) to
        # cell K shifts (K, 1, 4) to get
        # shift anchors (K, A, 4)
        # reshape to (K*A, 4) shifted anchors
        A = self._num_anchors
        K = shifts.shape[0]
        all_anchors = self._base_anchors.reshape((1, A, 4)) + shifts.reshape((1, K, 4)).transpose((1, 0, 2))
        all_anchors = all_anchors.reshape((K * A, 4))
        return all_anchors

    @staticmethod
    def _generate_base_anchors(base_size, scales, ratios):
        """
        Generate anchor (reference) windows by enumerating aspect ratios X
        scales wrt a reference (0, 0, 15, 15) window.
        """
        base_anchor = np.array([1, 1, base_size, base_size]) - 1
        ratio_anchors = AnchorGenerator._ratio_enum(base_anchor, ratios)
        anchors = np.vstack([AnchorGenerator._scale_enum(ratio_anchors[i, :], scales)
                             for i in range(ratio_anchors.shape[0])])
        return anchors

    @staticmethod
    def _whctrs(anchor):
        """
        Return width, height, x center, and y center for an anchor (window).
        """
        w = anchor[2] - anchor[0] + 1
        h = anchor[3] - anchor[1] + 1
        x_ctr = anchor[0] + 0.5 * (w - 1)
        y_ctr = anchor[1] + 0.5 * (h - 1)
        return w, h, x_ctr, y_ctr

    @staticmethod
    def _mkanchors(ws, hs, x_ctr, y_ctr):
        """
        Given a vector of widths (ws) and heights (hs) around a center
        (x_ctr, y_ctr), output a set of anchors (windows).
        """
        ws = ws[:, np.newaxis]
        hs = hs[:, np.newaxis]
        anchors = np.hstack((x_ctr - 0.5 * (ws - 1),
                             y_ctr - 0.5 * (hs - 1),
                             x_ctr + 0.5 * (ws - 1),
                             y_ctr + 0.5 * (hs - 1)))
        return anchors

    @staticmethod
    def _ratio_enum(anchor, ratios):
        """
        Enumerate a set of anchors for each aspect ratio wrt an anchor.
        """
        w, h, x_ctr, y_ctr = AnchorGenerator._whctrs(anchor)
        size = w * h
        size_ratios = size / ratios
        ws = np.round(np.sqrt(size_ratios))
        hs = np.round(ws * ratios)
        anchors = AnchorGenerator._mkanchors(ws, hs, x_ctr, y_ctr)
        return anchors

    @staticmethod
    def _scale_enum(anchor, scales):
        """
        Enumerate a set of anchors for each scale wrt an anchor.
        """
        w, h, x_ctr, y_ctr = AnchorGenerator._whctrs(anchor)
        ws = w * scales
        hs = h * scales
        anchors = AnchorGenerator._mkanchors(ws, hs, x_ctr, y_ctr)
        return anchors

Methods

def generate(self, feat_height, feat_width)
Expand source code
def generate(self, feat_height, feat_width):
    shift_x = np.arange(0, feat_width) * self._feat_stride
    shift_y = np.arange(0, feat_height) * self._feat_stride
    shift_x, shift_y = np.meshgrid(shift_x, shift_y)
    shifts = np.vstack((shift_x.ravel(), shift_y.ravel(), shift_x.ravel(), shift_y.ravel())).transpose()
    # add A anchors (1, A, 4) to
    # cell K shifts (K, 1, 4) to get
    # shift anchors (K, A, 4)
    # reshape to (K*A, 4) shifted anchors
    A = self._num_anchors
    K = shifts.shape[0]
    all_anchors = self._base_anchors.reshape((1, A, 4)) + shifts.reshape((1, K, 4)).transpose((1, 0, 2))
    all_anchors = all_anchors.reshape((K * A, 4))
    return all_anchors
class AnchorSampler (allowed_border=0, batch_rois=256, fg_fraction=0.5, fg_overlap=0.7, bg_overlap=0.3)
Expand source code
class AnchorSampler:
    def __init__(self, allowed_border=0, batch_rois=256, fg_fraction=0.5, fg_overlap=0.7, bg_overlap=0.3):
        self._allowed_border = allowed_border
        self._num_batch = batch_rois
        self._num_fg = int(batch_rois * fg_fraction)
        self._fg_overlap = fg_overlap
        self._bg_overlap = bg_overlap

    def assign(self, anchors, gt_boxes, im_height, im_width):
        num_anchors = anchors.shape[0]

        # filter out padded gt_boxes
        valid_labels = np.where(gt_boxes[:, -1] > 0)[0]
        gt_boxes = gt_boxes[valid_labels]

        # filter out anchors outside the region
        inds_inside = np.where((anchors[:, 0] >= -self._allowed_border) &
                               (anchors[:, 2] < im_width + self._allowed_border) &
                               (anchors[:, 1] >= -self._allowed_border) &
                               (anchors[:, 3] < im_height + self._allowed_border))[0]
        anchors = anchors[inds_inside, :]
        num_valid = len(inds_inside)

        # label: 1 is positive, 0 is negative, -1 is dont care
        labels = np.ones((num_valid,), dtype=np.float32) * -1
        bbox_targets = np.zeros((num_valid, 4), dtype=np.float32)
        bbox_weights = np.zeros((num_valid, 4), dtype=np.float32)

        # sample for positive labels
        if gt_boxes.size > 0:
            # overlap between the anchors and the gt boxes
            # overlaps (ex, gt)
            overlaps = bbox_overlaps(anchors.astype(np.float), gt_boxes.astype(np.float))

            # fg anchors: anchor with highest overlap for each gt
            gt_max_overlaps = overlaps.max(axis=0)
            argmax_inds = np.where(overlaps == gt_max_overlaps)[0]
            labels[argmax_inds] = 1

            # fg anchors: anchor with overlap > iou thresh
            max_overlaps = overlaps.max(axis=1)
            labels[max_overlaps >= self._fg_overlap] = 1

            # bg anchors: anchor with overlap < iou thresh
            labels[max_overlaps < self._bg_overlap] = 0

            # sanity check
            fg_inds = np.where(labels == 1)[0]
            bg_inds = np.where(labels == 0)[0]
            assert len(np.intersect1d(fg_inds, bg_inds)) == 0

            # subsample positive anchors
            cur_fg = len(fg_inds)
            if cur_fg > self._num_fg:
                disable_inds = np.random.choice(fg_inds, size=(cur_fg - self._num_fg), replace=False)
                labels[disable_inds] = -1

            # subsample negative anchors
            cur_bg = len(bg_inds)
            max_neg = self._num_batch - min(self._num_fg, cur_fg)
            if cur_bg > max_neg:
                disable_inds = np.random.choice(bg_inds, size=(cur_bg - max_neg), replace=False)
                labels[disable_inds] = -1

            # assign to argmax overlap
            fg_inds = np.where(labels == 1)[0]
            argmax_overlaps = overlaps.argmax(axis=1)
            bbox_targets[fg_inds, :] = bbox_transform(anchors[fg_inds, :], gt_boxes[argmax_overlaps[fg_inds], :],
                                                      box_stds=(1.0, 1.0, 1.0, 1.0))

            # only fg anchors has bbox_targets
            bbox_weights[fg_inds, :] = 1
        else:
            # randomly draw bg anchors
            bg_inds = np.random.choice(np.arange(num_valid), size=self._num_batch, replace=False)
            labels[bg_inds] = 0

        all_labels = np.ones((num_anchors,), dtype=np.float32) * -1
        all_labels[inds_inside] = labels
        all_bbox_targets = np.zeros((num_anchors, 4), dtype=np.float32)
        all_bbox_targets[inds_inside, :] = bbox_targets
        all_bbox_weights = np.zeros((num_anchors, 4), dtype=np.float32)
        all_bbox_weights[inds_inside, :] = bbox_weights

        return all_labels, all_bbox_targets, all_bbox_weights

Methods

def assign(self, anchors, gt_boxes, im_height, im_width)
Expand source code
def assign(self, anchors, gt_boxes, im_height, im_width):
    num_anchors = anchors.shape[0]

    # filter out padded gt_boxes
    valid_labels = np.where(gt_boxes[:, -1] > 0)[0]
    gt_boxes = gt_boxes[valid_labels]

    # filter out anchors outside the region
    inds_inside = np.where((anchors[:, 0] >= -self._allowed_border) &
                           (anchors[:, 2] < im_width + self._allowed_border) &
                           (anchors[:, 1] >= -self._allowed_border) &
                           (anchors[:, 3] < im_height + self._allowed_border))[0]
    anchors = anchors[inds_inside, :]
    num_valid = len(inds_inside)

    # label: 1 is positive, 0 is negative, -1 is dont care
    labels = np.ones((num_valid,), dtype=np.float32) * -1
    bbox_targets = np.zeros((num_valid, 4), dtype=np.float32)
    bbox_weights = np.zeros((num_valid, 4), dtype=np.float32)

    # sample for positive labels
    if gt_boxes.size > 0:
        # overlap between the anchors and the gt boxes
        # overlaps (ex, gt)
        overlaps = bbox_overlaps(anchors.astype(np.float), gt_boxes.astype(np.float))

        # fg anchors: anchor with highest overlap for each gt
        gt_max_overlaps = overlaps.max(axis=0)
        argmax_inds = np.where(overlaps == gt_max_overlaps)[0]
        labels[argmax_inds] = 1

        # fg anchors: anchor with overlap > iou thresh
        max_overlaps = overlaps.max(axis=1)
        labels[max_overlaps >= self._fg_overlap] = 1

        # bg anchors: anchor with overlap < iou thresh
        labels[max_overlaps < self._bg_overlap] = 0

        # sanity check
        fg_inds = np.where(labels == 1)[0]
        bg_inds = np.where(labels == 0)[0]
        assert len(np.intersect1d(fg_inds, bg_inds)) == 0

        # subsample positive anchors
        cur_fg = len(fg_inds)
        if cur_fg > self._num_fg:
            disable_inds = np.random.choice(fg_inds, size=(cur_fg - self._num_fg), replace=False)
            labels[disable_inds] = -1

        # subsample negative anchors
        cur_bg = len(bg_inds)
        max_neg = self._num_batch - min(self._num_fg, cur_fg)
        if cur_bg > max_neg:
            disable_inds = np.random.choice(bg_inds, size=(cur_bg - max_neg), replace=False)
            labels[disable_inds] = -1

        # assign to argmax overlap
        fg_inds = np.where(labels == 1)[0]
        argmax_overlaps = overlaps.argmax(axis=1)
        bbox_targets[fg_inds, :] = bbox_transform(anchors[fg_inds, :], gt_boxes[argmax_overlaps[fg_inds], :],
                                                  box_stds=(1.0, 1.0, 1.0, 1.0))

        # only fg anchors has bbox_targets
        bbox_weights[fg_inds, :] = 1
    else:
        # randomly draw bg anchors
        bg_inds = np.random.choice(np.arange(num_valid), size=self._num_batch, replace=False)
        labels[bg_inds] = 0

    all_labels = np.ones((num_anchors,), dtype=np.float32) * -1
    all_labels[inds_inside] = labels
    all_bbox_targets = np.zeros((num_anchors, 4), dtype=np.float32)
    all_bbox_targets[inds_inside, :] = bbox_targets
    all_bbox_weights = np.zeros((num_anchors, 4), dtype=np.float32)
    all_bbox_weights[inds_inside, :] = bbox_weights

    return all_labels, all_bbox_targets, all_bbox_weights