CoolFace
Apppublic

coreml-community/ControlNet-v1-1-Annotators-cpu

sourceHugging Facemitupdated 2y agoView on Hugging Face
15likes
poolers.py264 linesDownload Raw Back to modeling
1# Copyright (c) Facebook, Inc. and its affiliates.2import math3from typing import List, Optional4import torch5from torch import nn6from torchvision.ops import RoIPool7 8from annotator.oneformer.detectron2.layers import ROIAlign, ROIAlignRotated, cat, nonzero_tuple, shapes_to_tensor9from annotator.oneformer.detectron2.structures import Boxes10from annotator.oneformer.detectron2.utils.tracing import assert_fx_safe, is_fx_tracing11 12"""13To export ROIPooler to torchscript, in this file, variables that should be annotated with14`Union[List[Boxes], List[RotatedBoxes]]` are only annotated with `List[Boxes]`.15 16TODO: Correct these annotations when torchscript support `Union`.17https://github.com/pytorch/pytorch/issues/4141218"""19 20__all__ = ["ROIPooler"]21 22 23def assign_boxes_to_levels(24    box_lists: List[Boxes],25    min_level: int,26    max_level: int,27    canonical_box_size: int,28    canonical_level: int,29):30    """31    Map each box in `box_lists` to a feature map level index and return the assignment32    vector.33 34    Args:35        box_lists (list[Boxes] | list[RotatedBoxes]): A list of N Boxes or N RotatedBoxes,36            where N is the number of images in the batch.37        min_level (int): Smallest feature map level index. The input is considered index 0,38            the output of stage 1 is index 1, and so.39        max_level (int): Largest feature map level index.40        canonical_box_size (int): A canonical box size in pixels (sqrt(box area)).41        canonical_level (int): The feature map level index on which a canonically-sized box42            should be placed.43 44    Returns:45        A tensor of length M, where M is the total number of boxes aggregated over all46            N batch images. The memory layout corresponds to the concatenation of boxes47            from all images. Each element is the feature map index, as an offset from48            `self.min_level`, for the corresponding box (so value i means the box is at49            `self.min_level + i`).50    """51    box_sizes = torch.sqrt(cat([boxes.area() for boxes in box_lists]))52    # Eqn.(1) in FPN paper53    level_assignments = torch.floor(54        canonical_level + torch.log2(box_sizes / canonical_box_size + 1e-8)55    )56    # clamp level to (min, max), in case the box size is too large or too small57    # for the available feature maps58    level_assignments = torch.clamp(level_assignments, min=min_level, max=max_level)59    return level_assignments.to(torch.int64) - min_level60 61 62# script the module to avoid hardcoded device type63@torch.jit.script_if_tracing64def _convert_boxes_to_pooler_format(boxes: torch.Tensor, sizes: torch.Tensor) -> torch.Tensor:65    sizes = sizes.to(device=boxes.device)66    indices = torch.repeat_interleave(67        torch.arange(len(sizes), dtype=boxes.dtype, device=boxes.device), sizes68    )69    return cat([indices[:, None], boxes], dim=1)70 71 72def convert_boxes_to_pooler_format(box_lists: List[Boxes]):73    """74    Convert all boxes in `box_lists` to the low-level format used by ROI pooling ops75    (see description under Returns).76 77    Args:78        box_lists (list[Boxes] | list[RotatedBoxes]):79            A list of N Boxes or N RotatedBoxes, where N is the number of images in the batch.80 81    Returns:82        When input is list[Boxes]:83            A tensor of shape (M, 5), where M is the total number of boxes aggregated over all84            N batch images.85            The 5 columns are (batch index, x0, y0, x1, y1), where batch index86            is the index in [0, N) identifying which batch image the box with corners at87            (x0, y0, x1, y1) comes from.88        When input is list[RotatedBoxes]:89            A tensor of shape (M, 6), where M is the total number of boxes aggregated over all90            N batch images.91            The 6 columns are (batch index, x_ctr, y_ctr, width, height, angle_degrees),92            where batch index is the index in [0, N) identifying which batch image the93            rotated box (x_ctr, y_ctr, width, height, angle_degrees) comes from.94    """95    boxes = torch.cat([x.tensor for x in box_lists], dim=0)96    # __len__ returns Tensor in tracing.97    sizes = shapes_to_tensor([x.__len__() for x in box_lists])98    return _convert_boxes_to_pooler_format(boxes, sizes)99 100 101@torch.jit.script_if_tracing102def _create_zeros(103    batch_target: Optional[torch.Tensor],104    channels: int,105    height: int,106    width: int,107    like_tensor: torch.Tensor,108) -> torch.Tensor:109    batches = batch_target.shape[0] if batch_target is not None else 0110    sizes = (batches, channels, height, width)111    return torch.zeros(sizes, dtype=like_tensor.dtype, device=like_tensor.device)112 113 114class ROIPooler(nn.Module):115    """116    Region of interest feature map pooler that supports pooling from one or more117    feature maps.118    """119 120    def __init__(121        self,122        output_size,123        scales,124        sampling_ratio,125        pooler_type,126        canonical_box_size=224,127        canonical_level=4,128    ):129        """130        Args:131            output_size (int, tuple[int] or list[int]): output size of the pooled region,132                e.g., 14 x 14. If tuple or list is given, the length must be 2.133            scales (list[float]): The scale for each low-level pooling op relative to134                the input image. For a feature map with stride s relative to the input135                image, scale is defined as 1/s. The stride must be power of 2.136                When there are multiple scales, they must form a pyramid, i.e. they must be137                a monotically decreasing geometric sequence with a factor of 1/2.138            sampling_ratio (int): The `sampling_ratio` parameter for the ROIAlign op.139            pooler_type (string): Name of the type of pooling operation that should be applied.140                For instance, "ROIPool" or "ROIAlignV2".141            canonical_box_size (int): A canonical box size in pixels (sqrt(box area)). The default142                is heuristically defined as 224 pixels in the FPN paper (based on ImageNet143                pre-training).144            canonical_level (int): The feature map level index from which a canonically-sized box145                should be placed. The default is defined as level 4 (stride=16) in the FPN paper,146                i.e., a box of size 224x224 will be placed on the feature with stride=16.147                The box placement for all boxes will be determined from their sizes w.r.t148                canonical_box_size. For example, a box whose area is 4x that of a canonical box149                should be used to pool features from feature level ``canonical_level+1``.150 151                Note that the actual input feature maps given to this module may not have152                sufficiently many levels for the input boxes. If the boxes are too large or too153                small for the input feature maps, the closest level will be used.154        """155        super().__init__()156 157        if isinstance(output_size, int):158            output_size = (output_size, output_size)159        assert len(output_size) == 2160        assert isinstance(output_size[0], int) and isinstance(output_size[1], int)161        self.output_size = output_size162 163        if pooler_type == "ROIAlign":164            self.level_poolers = nn.ModuleList(165                ROIAlign(166                    output_size, spatial_scale=scale, sampling_ratio=sampling_ratio, aligned=False167                )168                for scale in scales169            )170        elif pooler_type == "ROIAlignV2":171            self.level_poolers = nn.ModuleList(172                ROIAlign(173                    output_size, spatial_scale=scale, sampling_ratio=sampling_ratio, aligned=True174                )175                for scale in scales176            )177        elif pooler_type == "ROIPool":178            self.level_poolers = nn.ModuleList(179                RoIPool(output_size, spatial_scale=scale) for scale in scales180            )181        elif pooler_type == "ROIAlignRotated":182            self.level_poolers = nn.ModuleList(183                ROIAlignRotated(output_size, spatial_scale=scale, sampling_ratio=sampling_ratio)184                for scale in scales185            )186        else:187            raise ValueError("Unknown pooler type: {}".format(pooler_type))188 189        # Map scale (defined as 1 / stride) to its feature map level under the190        # assumption that stride is a power of 2.191        min_level = -(math.log2(scales[0]))192        max_level = -(math.log2(scales[-1]))193        assert math.isclose(min_level, int(min_level)) and math.isclose(194            max_level, int(max_level)195        ), "Featuremap stride is not power of 2!"196        self.min_level = int(min_level)197        self.max_level = int(max_level)198        assert (199            len(scales) == self.max_level - self.min_level + 1200        ), "[ROIPooler] Sizes of input featuremaps do not form a pyramid!"201        assert 0 <= self.min_level and self.min_level <= self.max_level202        self.canonical_level = canonical_level203        assert canonical_box_size > 0204        self.canonical_box_size = canonical_box_size205 206    def forward(self, x: List[torch.Tensor], box_lists: List[Boxes]):207        """208        Args:209            x (list[Tensor]): A list of feature maps of NCHW shape, with scales matching those210                used to construct this module.211            box_lists (list[Boxes] | list[RotatedBoxes]):212                A list of N Boxes or N RotatedBoxes, where N is the number of images in the batch.213                The box coordinates are defined on the original image and214                will be scaled by the `scales` argument of :class:`ROIPooler`.215 216        Returns:217            Tensor:218                A tensor of shape (M, C, output_size, output_size) where M is the total number of219                boxes aggregated over all N batch images and C is the number of channels in `x`.220        """221        num_level_assignments = len(self.level_poolers)222 223        if not is_fx_tracing():224            torch._assert(225                isinstance(x, list) and isinstance(box_lists, list),226                "Arguments to pooler must be lists",227            )228        assert_fx_safe(229            len(x) == num_level_assignments,230            "unequal value, num_level_assignments={}, but x is list of {} Tensors".format(231                num_level_assignments, len(x)232            ),233        )234        assert_fx_safe(235            len(box_lists) == x[0].size(0),236            "unequal value, x[0] batch dim 0 is {}, but box_list has length {}".format(237                x[0].size(0), len(box_lists)238            ),239        )240        if len(box_lists) == 0:241            return _create_zeros(None, x[0].shape[1], *self.output_size, x[0])242 243        pooler_fmt_boxes = convert_boxes_to_pooler_format(box_lists)244 245        if num_level_assignments == 1:246            return self.level_poolers[0](x[0], pooler_fmt_boxes)247 248        level_assignments = assign_boxes_to_levels(249            box_lists, self.min_level, self.max_level, self.canonical_box_size, self.canonical_level250        )251 252        num_channels = x[0].shape[1]253        output_size = self.output_size[0]254 255        output = _create_zeros(pooler_fmt_boxes, num_channels, output_size, output_size, x[0])256 257        for level, pooler in enumerate(self.level_poolers):258            inds = nonzero_tuple(level_assignments == level)[0]259            pooler_fmt_boxes_level = pooler_fmt_boxes[inds]260            # Use index_put_ instead of advance indexing, to avoid pytorch/issues/49852261            output.index_put_((inds,), pooler(x[level], pooler_fmt_boxes_level))262 263        return output264