【SSD】之 DefaultBoxes

from math import sqrt
import itertools
import numpy as np
import torch


class DefaultBoxes(object):
    def __init__(self):
        self.fig_size = 300   # 输入网络的图像大小 300
        self.feat_size = [38, 19, 10, 5, 3, 1]  # 每个预测层的feature map尺寸: [38, 19, 10, 5, 3, 1]
        self.scales = [21, 45, 99, 153, 207, 261, 315]  # 每个特征层上预测的default box的scale: [21, 45, 99, 153, 207, 261, 315]
        self.aspect_ratios = [[2], [2, 3], [2, 3], [2, 3], [2], [2]]  # 每个预测特征层上预测的default box的ratios: [[2], [2, 3], [2, 3], [2, 3], [2], [2]]

        # According to https://github.com/weiliu89/caffe
        # Calculation method slightly different from paper
        self.steps = [8, 16, 32, 64, 100, 300]    # 每个特征层上的一个cell在原图上的跨度: [8, 16, 32, 64, 100, 300]
        fk = self.fig_size / np.array(self.steps)  # 计算每层特征层的fk

        self.scale_xy_ = 0.1
        self.scale_wh_ = 0.2

        self.default_boxes = []
        # 遍历每层特征层,计算default box
        for idx, sfeat in enumerate(self.feat_size):
            # 先计算两个 高宽比为 1:1 的 default box 的 高和宽 (相对高宽 0~1)
            sk1 = self.scales[idx] / self.fig_size  # scale转为相对值[0-1]
            sk2 = self.scales[idx + 1] / self.fig_size  # scale转为相对值[0-1]
            sk3 = sqrt(sk1 * sk2)
            all_sizes = [(sk1, sk1), (sk3, sk3)]

            # 再将剩下 高宽比为 1/2、2、1/3、3 的 default box 的宽和高添加到 all_sizes 中
            for alpha in self.aspect_ratios[idx]:
                w, h = sk1 * sqrt(alpha), sk1 / sqrt(alpha)
                all_sizes.append((w, h))
                all_sizes.append((h, w))

            # 计算特征图上的每个像素 映射到原图上的位置(default box 的中心坐标)
            for w, h in all_sizes:
                for i, j in itertools.product(range(sfeat), repeat=2):  # i -> 行(y), j -> 列(x)
                    # 计算每个default box的中心坐标。
                    # 这么设计是为了保证 default box 的中心点都均匀的落在原图像中,
                    # 想象一下最小的 1x1 的 feature map,它对应的 default box 的中心点应该落在原图像的中心 (0.5, 0.5)
                    cx, cy = (j + 0.5) / fk[idx], (i + 0.5) / fk[idx]
                    self.default_boxes.append((cx, cy, w, h))

        # 将default_boxes 转为 tensor格式
        self.dboxes = torch.as_tensor(self.default_boxes, dtype=torch.float32)
        self.dboxes.clamp_(min=0, max=1)  # 将坐标(x, y, w, h)都限制在0-1之间

        # 将(x, y, w, h)转换成(xmin, ymin, xmax, ymax),方便后续计算IoU(匹配正负样本)
        # ltrb is left top coordinate and right bottom coordinate
        self.dboxes_ltrb = self.dboxes.clone()
        self.dboxes_ltrb[:, 0] = self.dboxes[:, 0] - 0.5 * self.dboxes[:, 2]   # xmin
        self.dboxes_ltrb[:, 1] = self.dboxes[:, 1] - 0.5 * self.dboxes[:, 3]   # ymin
        self.dboxes_ltrb[:, 2] = self.dboxes[:, 0] + 0.5 * self.dboxes[:, 2]   # xmax
        self.dboxes_ltrb[:, 3] = self.dboxes[:, 1] + 0.5 * self.dboxes[:, 3]   # ymax

    @property
    def scale_xy(self):
        return self.scale_xy_

    @property
    def scale_wh(self):
        return self.scale_wh_

    def __call__(self, order='ltrb'):
        # 根据需求返回对应格式的default box
        if order == 'ltrb':
            return self.dboxes_ltrb

        if order == 'xywh':
            return self.dboxes
        
        
dboxes = DefaultBoxes()   # default box 的shape 为 [8732, 4]

猜你喜欢

转载自blog.csdn.net/weixin_37804469/article/details/128967140
ssd