|
from math import sqrt |
|
|
|
import torch |
|
|
|
|
|
def gaussian2D(radius, sigma=1, dtype=torch.float32, device='cpu'): |
|
"""Generate 2D gaussian kernel. |
|
|
|
Args: |
|
radius (int): Radius of gaussian kernel. |
|
sigma (int): Sigma of gaussian function. Default: 1. |
|
dtype (torch.dtype): Dtype of gaussian tensor. Default: torch.float32. |
|
device (str): Device of gaussian tensor. Default: 'cpu'. |
|
|
|
Returns: |
|
h (Tensor): Gaussian kernel with a |
|
``(2 * radius + 1) * (2 * radius + 1)`` shape. |
|
""" |
|
x = torch.arange( |
|
-radius, radius + 1, dtype=dtype, device=device).view(1, -1) |
|
y = torch.arange( |
|
-radius, radius + 1, dtype=dtype, device=device).view(-1, 1) |
|
|
|
h = (-(x * x + y * y) / (2 * sigma * sigma)).exp() |
|
|
|
h[h < torch.finfo(h.dtype).eps * h.max()] = 0 |
|
return h |
|
|
|
|
|
def gen_gaussian_target(heatmap, center, radius, k=1): |
|
"""Generate 2D gaussian heatmap. |
|
|
|
Args: |
|
heatmap (Tensor): Input heatmap, the gaussian kernel will cover on |
|
it and maintain the max value. |
|
center (list[int]): Coord of gaussian kernel's center. |
|
radius (int): Radius of gaussian kernel. |
|
k (int): Coefficient of gaussian kernel. Default: 1. |
|
|
|
Returns: |
|
out_heatmap (Tensor): Updated heatmap covered by gaussian kernel. |
|
""" |
|
diameter = 2 * radius + 1 |
|
gaussian_kernel = gaussian2D( |
|
radius, sigma=diameter / 6, dtype=heatmap.dtype, device=heatmap.device) |
|
|
|
x, y = center |
|
|
|
height, width = heatmap.shape[:2] |
|
|
|
left, right = min(x, radius), min(width - x, radius + 1) |
|
top, bottom = min(y, radius), min(height - y, radius + 1) |
|
|
|
masked_heatmap = heatmap[y - top:y + bottom, x - left:x + right] |
|
masked_gaussian = gaussian_kernel[radius - top:radius + bottom, |
|
radius - left:radius + right] |
|
out_heatmap = heatmap |
|
torch.max( |
|
masked_heatmap, |
|
masked_gaussian * k, |
|
out=out_heatmap[y - top:y + bottom, x - left:x + right]) |
|
|
|
return out_heatmap |
|
|
|
|
|
def gaussian_radius(det_size, min_overlap): |
|
r"""Generate 2D gaussian radius. |
|
|
|
This function is modified from the `official github repo |
|
<https://github.com/princeton-vl/CornerNet-Lite/blob/master/core/sample/ |
|
utils.py#L65>`_. |
|
|
|
Given ``min_overlap``, radius could computed by a quadratic equation |
|
according to Vieta's formulas. |
|
|
|
There are 3 cases for computing gaussian radius, details are following: |
|
|
|
- Explanation of figure: ``lt`` and ``br`` indicates the left-top and |
|
bottom-right corner of ground truth box. ``x`` indicates the |
|
generated corner at the limited position when ``radius=r``. |
|
|
|
- Case1: one corner is inside the gt box and the other is outside. |
|
|
|
.. code:: text |
|
|
|
|< width >| |
|
|
|
lt-+----------+ - |
|
| | | ^ |
|
+--x----------+--+ |
|
| | | | |
|
| | | | height |
|
| | overlap | | |
|
| | | | |
|
| | | | v |
|
+--+---------br--+ - |
|
| | | |
|
+----------+--x |
|
|
|
To ensure IoU of generated box and gt box is larger than ``min_overlap``: |
|
|
|
.. math:: |
|
\cfrac{(w-r)*(h-r)}{w*h+(w+h)r-r^2} \ge {iou} \quad\Rightarrow\quad |
|
{r^2-(w+h)r+\cfrac{1-iou}{1+iou}*w*h} \ge 0 \\ |
|
{a} = 1,\quad{b} = {-(w+h)},\quad{c} = {\cfrac{1-iou}{1+iou}*w*h} |
|
{r} \le \cfrac{-b-\sqrt{b^2-4*a*c}}{2*a} |
|
|
|
- Case2: both two corners are inside the gt box. |
|
|
|
.. code:: text |
|
|
|
|< width >| |
|
|
|
lt-+----------+ - |
|
| | | ^ |
|
+--x-------+ | |
|
| | | | |
|
| |overlap| | height |
|
| | | | |
|
| +-------x--+ |
|
| | | v |
|
+----------+-br - |
|
|
|
To ensure IoU of generated box and gt box is larger than ``min_overlap``: |
|
|
|
.. math:: |
|
\cfrac{(w-2*r)*(h-2*r)}{w*h} \ge {iou} \quad\Rightarrow\quad |
|
{4r^2-2(w+h)r+(1-iou)*w*h} \ge 0 \\ |
|
{a} = 4,\quad {b} = {-2(w+h)},\quad {c} = {(1-iou)*w*h} |
|
{r} \le \cfrac{-b-\sqrt{b^2-4*a*c}}{2*a} |
|
|
|
- Case3: both two corners are outside the gt box. |
|
|
|
.. code:: text |
|
|
|
|< width >| |
|
|
|
x--+----------------+ |
|
| | | |
|
+-lt-------------+ | - |
|
| | | | ^ |
|
| | | | |
|
| | overlap | | height |
|
| | | | |
|
| | | | v |
|
| +------------br--+ - |
|
| | | |
|
+----------------+--x |
|
|
|
To ensure IoU of generated box and gt box is larger than ``min_overlap``: |
|
|
|
.. math:: |
|
\cfrac{w*h}{(w+2*r)*(h+2*r)} \ge {iou} \quad\Rightarrow\quad |
|
{4*iou*r^2+2*iou*(w+h)r+(iou-1)*w*h} \le 0 \\ |
|
{a} = {4*iou},\quad {b} = {2*iou*(w+h)},\quad {c} = {(iou-1)*w*h} \\ |
|
{r} \le \cfrac{-b+\sqrt{b^2-4*a*c}}{2*a} |
|
|
|
Args: |
|
det_size (list[int]): Shape of object. |
|
min_overlap (float): Min IoU with ground truth for boxes generated by |
|
keypoints inside the gaussian kernel. |
|
|
|
Returns: |
|
radius (int): Radius of gaussian kernel. |
|
""" |
|
height, width = det_size |
|
|
|
a1 = 1 |
|
b1 = (height + width) |
|
c1 = width * height * (1 - min_overlap) / (1 + min_overlap) |
|
sq1 = sqrt(b1**2 - 4 * a1 * c1) |
|
r1 = (b1 - sq1) / (2 * a1) |
|
|
|
a2 = 4 |
|
b2 = 2 * (height + width) |
|
c2 = (1 - min_overlap) * width * height |
|
sq2 = sqrt(b2**2 - 4 * a2 * c2) |
|
r2 = (b2 - sq2) / (2 * a2) |
|
|
|
a3 = 4 * min_overlap |
|
b3 = -2 * min_overlap * (height + width) |
|
c3 = (min_overlap - 1) * width * height |
|
sq3 = sqrt(b3**2 - 4 * a3 * c3) |
|
r3 = (b3 + sq3) / (2 * a3) |
|
return min(r1, r2, r3) |
|
|