-
Notifications
You must be signed in to change notification settings - Fork 402
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rewrites anchor reference generation and adds tests for it
- Loading branch information
Showing
2 changed files
with
119 additions
and
92 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,105 +1,42 @@ | ||
# -------------------------------------------------------- | ||
# Faster R-CNN | ||
# Copyright (c) 2015 Microsoft | ||
# Licensed under The MIT License [see LICENSE for details] | ||
# Written by Ross Girshick and Sean Bell | ||
# -------------------------------------------------------- | ||
|
||
import numpy as np | ||
|
||
# Verify that we compute the same anchors as Shaoqing's matlab implementation: | ||
# | ||
# >> load output/rpn_cachedir/faster_rcnn_VOC2007_ZF_stage1_rpn/anchors.mat | ||
# >> anchors | ||
# | ||
# anchors = | ||
# | ||
# -83 -39 100 56 | ||
# -175 -87 192 104 | ||
# -359 -183 376 200 | ||
# -55 -55 72 72 | ||
# -119 -119 136 136 | ||
# -247 -247 264 264 | ||
# -35 -79 52 96 | ||
# -79 -167 96 184 | ||
# -167 -343 184 360 | ||
|
||
# array([[ -83., -39., 100., 56.], | ||
# [-175., -87., 192., 104.], | ||
# [-359., -183., 376., 200.], | ||
# [ -55., -55., 72., 72.], | ||
# [-119., -119., 136., 136.], | ||
# [-247., -247., 264., 264.], | ||
# [ -35., -79., 52., 96.], | ||
# [ -79., -167., 96., 184.], | ||
# [-167., -343., 184., 360.]]) | ||
def generate_anchors_reference(base_size, aspect_ratios, scales): | ||
"""Generate base anchor to be used as reference of generating all anchors. | ||
Anchors vary only in width and height. Using the base_size and the | ||
different ratios we can calculate the wanted widths and heights. | ||
def generate_anchors_reference(base_size, ratios, scales): | ||
""" | ||
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 = _ratio_enum(base_anchor, ratios) | ||
anchors = np.vstack([_scale_enum(ratio_anchors[i, :], scales) | ||
for i in range(ratio_anchors.shape[0])]) | ||
return anchors | ||
|
||
|
||
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 | ||
Scales apply to area of object. | ||
Args: | ||
base_size (int): Base size of the base anchor (square). | ||
aspect_ratios: Ratios to use to generate different anchors. The ratio | ||
is the value of height / width. | ||
scales: Scaling ratios applied to area. | ||
def _mkanchors(ws, hs, x_ctr, y_ctr): | ||
Returns: | ||
anchors: Numpy array with shape (total_aspect_ratios * total_scales, 4) | ||
with the corner points of the reference base anchors using the | ||
convention (x_min, y_min, x_max, y_max). | ||
""" | ||
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 | ||
scales_grid, aspect_ratios_grid = np.meshgrid(scales, aspect_ratios) | ||
base_scales = scales_grid.reshape(-1) | ||
base_aspect_ratios = aspect_ratios_grid.reshape(-1) | ||
|
||
aspect_ratio_sqrts = np.sqrt(base_aspect_ratios) | ||
heights = base_scales * aspect_ratio_sqrts * base_size | ||
widths = base_scales / aspect_ratio_sqrts * base_size | ||
|
||
def _ratio_enum(anchor, ratios): | ||
""" | ||
Enumerate a set of anchors for each aspect ratio wrt an anchor. | ||
""" | ||
w, h, x_ctr, y_ctr = _whctrs(anchor) | ||
size = w * h | ||
size_ratios = size / ratios | ||
ws = np.sqrt(size_ratios) # np.round(np.sqrt(size_ratios)) | ||
hs = ws * ratios # np.round(ws * ratios) | ||
anchors = _mkanchors(ws, hs, x_ctr, y_ctr) | ||
return anchors | ||
# Center point has the same X, Y value. | ||
center_xy = (base_size - 1) / 2.0 | ||
|
||
# Create anchor reference. | ||
anchors = np.column_stack([ | ||
center_xy - (widths - 1) / 2, | ||
center_xy - (heights - 1) / 2, | ||
center_xy + (widths - 1) / 2, | ||
center_xy + (heights - 1) / 2, | ||
]) | ||
|
||
def _scale_enum(anchor, scales): | ||
""" | ||
Enumerate a set of anchors for each scale wrt an anchor. | ||
""" | ||
w, h, x_ctr, y_ctr = _whctrs(anchor) | ||
ws = w * scales | ||
hs = h * scales | ||
anchors = _mkanchors(ws, hs, x_ctr, y_ctr) | ||
return anchors | ||
|
||
|
||
if __name__ == '__main__': | ||
import time | ||
t = time.time() | ||
a = generate_anchors_reference( | ||
base_size=16, ratios=[0.5, 1, 2], scales=2**np.arange(3, 6) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import numpy as np | ||
import tensorflow as tf | ||
|
||
from luminoth.utils.anchors import generate_anchors_reference, generate_anchors_reference_old | ||
|
||
|
||
class AnchorsTest(tf.test.TestCase): | ||
|
||
def _get_widths_heights(self, anchor_reference): | ||
return np.column_stack(( | ||
(anchor_reference[:, 2] - anchor_reference[:, 0] + 1), | ||
(anchor_reference[:, 3] - anchor_reference[:, 1] + 1) | ||
)) | ||
|
||
def testAnchorReference(self): | ||
# Test simple case with one aspect ratio and one scale. | ||
base_size = 256 | ||
aspect_ratios = [1.] | ||
scales = [1.] | ||
anchor_reference = generate_anchors_reference( | ||
base_size=base_size, | ||
aspect_ratios=aspect_ratios, | ||
scales=scales | ||
) | ||
|
||
# Should return a single anchor. | ||
self.assertEqual(anchor_reference.shape, (1, 4)) | ||
self.assertAllEqual( | ||
anchor_reference[0], [0, 0, base_size - 1, base_size - 1] | ||
) | ||
|
||
# Test with fixed ratio and different scales. | ||
scales = np.array([0.5, 1., 2., 4.]) | ||
anchor_reference = generate_anchors_reference( | ||
base_size=base_size, | ||
aspect_ratios=aspect_ratios, | ||
scales=scales | ||
) | ||
|
||
# Check that we have the correct number of anchors. | ||
self.assertEqual(anchor_reference.shape, (4, 4)) | ||
width_heights = self._get_widths_heights(anchor_reference) | ||
# Check that anchors are squares (aspect_ratio = [1.0]). | ||
self.assertTrue((width_heights[:, 0] == width_heights[:, 1]).all()) | ||
# Check that widths are consistent with scales times base_size. | ||
self.assertAllEqual(width_heights[:, 0], base_size * scales) | ||
# Check exact values. | ||
self.assertAllEqual( | ||
anchor_reference, | ||
np.array([[64., 64., 191., 191.], | ||
[0., 0., 255., 255.], | ||
[-128., -128., 383., 383.], | ||
[-384., -384., 639., 639.]]) | ||
) | ||
|
||
# Test with different ratios and scales. | ||
scales = np.array([0.5, 1., 2.]) | ||
aspect_ratios = np.array([0.5, 1., 2.]) | ||
anchor_reference = generate_anchors_reference( | ||
base_size=base_size, | ||
aspect_ratios=aspect_ratios, | ||
scales=scales | ||
) | ||
|
||
# Check we have the correct number of anchors. | ||
self.assertEqual( | ||
anchor_reference.shape, (len(scales) * len(aspect_ratios), 4) | ||
) | ||
|
||
width_heights = self._get_widths_heights(anchor_reference) | ||
|
||
# Check ratios of height / widths | ||
anchor_ratios = width_heights[:, 1] / width_heights[:, 0] | ||
# Check scales (applied to ) | ||
anchor_scales = np.sqrt( | ||
(width_heights[:, 1] * width_heights[:, 0]) / (base_size ** 2) | ||
) | ||
|
||
# Test that all ratios are used in the correct order. | ||
self.assertAllClose( | ||
anchor_ratios, [0.5, 0.5, 0.5, 1., 1., 1., 2., 2., 2.] | ||
) | ||
# Test that all scales are used in the correct order. | ||
self.assertAllClose( | ||
anchor_scales, [0.5, 1., 2., 0.5, 1., 2., 0.5, 1., 2.] | ||
) | ||
|
||
|
||
if __name__ == '__main__': | ||
tf.test.main() |