The box-to-box transform defined in R-CNN. The transformation is parameterized by 4 deltas: (dx, dy, dw, dh). The transformation scales the box's width and height by exp(dw), exp(dh) and shifts a box's center by the offset (dx * width, dy * height).
| 14 | |
| 15 | @torch.jit.script |
| 16 | class Box2BoxTransform(object): |
| 17 | """ |
| 18 | The box-to-box transform defined in R-CNN. The transformation is parameterized |
| 19 | by 4 deltas: (dx, dy, dw, dh). The transformation scales the box's width and height |
| 20 | by exp(dw), exp(dh) and shifts a box's center by the offset (dx * width, dy * height). |
| 21 | """ |
| 22 | |
| 23 | def __init__( |
| 24 | self, weights: Tuple[float, float, float, float], scale_clamp: float = _DEFAULT_SCALE_CLAMP |
| 25 | ): |
| 26 | """ |
| 27 | Args: |
| 28 | weights (4-element tuple): Scaling factors that are applied to the |
| 29 | (dx, dy, dw, dh) deltas. In Fast R-CNN, these were originally set |
| 30 | such that the deltas have unit variance; now they are treated as |
| 31 | hyperparameters of the system. |
| 32 | scale_clamp (float): When predicting deltas, the predicted box scaling |
| 33 | factors (dw and dh) are clamped such that they are <= scale_clamp. |
| 34 | """ |
| 35 | self.weights = weights |
| 36 | self.scale_clamp = scale_clamp |
| 37 | |
| 38 | def get_deltas(self, src_boxes, target_boxes): |
| 39 | """ |
| 40 | Get box regression transformation deltas (dx, dy, dw, dh) that can be used |
| 41 | to transform the `src_boxes` into the `target_boxes`. That is, the relation |
| 42 | ``target_boxes == self.apply_deltas(deltas, src_boxes)`` is true (unless |
| 43 | any delta is too large and is clamped). |
| 44 | |
| 45 | Args: |
| 46 | src_boxes (Tensor): source boxes, e.g., object proposals |
| 47 | target_boxes (Tensor): target of the transformation, e.g., ground-truth |
| 48 | boxes. |
| 49 | """ |
| 50 | assert isinstance(src_boxes, torch.Tensor), type(src_boxes) |
| 51 | assert isinstance(target_boxes, torch.Tensor), type(target_boxes) |
| 52 | |
| 53 | src_widths = src_boxes[:, 2] - src_boxes[:, 0] |
| 54 | src_heights = src_boxes[:, 3] - src_boxes[:, 1] |
| 55 | src_ctr_x = src_boxes[:, 0] + 0.5 * src_widths |
| 56 | src_ctr_y = src_boxes[:, 1] + 0.5 * src_heights |
| 57 | |
| 58 | target_widths = target_boxes[:, 2] - target_boxes[:, 0] |
| 59 | target_heights = target_boxes[:, 3] - target_boxes[:, 1] |
| 60 | target_ctr_x = target_boxes[:, 0] + 0.5 * target_widths |
| 61 | target_ctr_y = target_boxes[:, 1] + 0.5 * target_heights |
| 62 | |
| 63 | wx, wy, ww, wh = self.weights |
| 64 | dx = wx * (target_ctr_x - src_ctr_x) / src_widths |
| 65 | dy = wy * (target_ctr_y - src_ctr_y) / src_heights |
| 66 | dw = ww * torch.log(target_widths / src_widths) |
| 67 | dh = wh * torch.log(target_heights / src_heights) |
| 68 | |
| 69 | deltas = torch.stack((dx, dy, dw, dh), dim=1) |
| 70 | assert (src_widths > 0).all().item(), "Input boxes to Box2BoxTransform are not valid!" |
| 71 | return deltas |
| 72 | |
| 73 | def apply_deltas(self, deltas, boxes): |
no outgoing calls