Save feature information in attributes for consistency

This commit is contained in:
Johannes Schönberger
2014-01-19 09:38:36 -05:00
parent f6ba14b494
commit 336f0ca266
10 changed files with 171 additions and 114 deletions
+10 -6
View File
@@ -37,13 +37,17 @@ keypoints3 = corner_peaks(corner_harris(img3), min_distance=5)
extractor = BRIEF()
descriptors1, mask1 = extractor.extract(img1, keypoints1)
descriptors2, mask2 = extractor.extract(img2, keypoints2)
descriptors3, mask3 = extractor.extract(img3, keypoints3)
extractor.extract(img1, keypoints1)
keypoints1 = keypoints1[extractor.mask_]
descriptors1 = extractor.descriptors_
keypoints1 = keypoints1[mask1]
keypoints2 = keypoints2[mask2]
keypoints3 = keypoints3[mask3]
extractor.extract(img2, keypoints2)
keypoints2 = keypoints2[extractor.mask_]
descriptors2 = extractor.descriptors_
extractor.extract(img3, keypoints3)
keypoints3 = keypoints3[extractor.mask_]
descriptors3 = extractor.descriptors_
matches12 = match_descriptors(descriptors1, descriptors2, cross_check=True)
matches13 = match_descriptors(descriptors1, descriptors3, cross_check=True)
+8 -6
View File
@@ -21,21 +21,23 @@ tform = tf.AffineTransform(scale=(1.5, 1.5), rotation=0.5,
img2 = tf.warp(img1, tform)
detector = CenSurE()
keypoints1, scales1 = detector.detect(img1)
keypoints2, scales2 = detector.detect(img2)
fig, ax = plt.subplots(nrows=1, ncols=2)
plt.gray()
detector.detect(img1)
ax[0].imshow(img1)
ax[0].axis('off')
ax[0].scatter(keypoints1[:, 1], keypoints1[:, 0], 2 ** scales1,
facecolors='none', edgecolors='r')
ax[0].scatter(detector.keypoints_[:, 1], detector.keypoints_[:, 0],
2 ** detector.scales_, facecolors='none', edgecolors='r')
detector.detect(img2)
ax[1].imshow(img2)
ax[1].axis('off')
ax[1].scatter(keypoints2[:, 1], keypoints2[:, 0], 2 ** scales2,
facecolors='none', edgecolors='r')
ax[1].scatter(detector.keypoints_[:, 1], detector.keypoints_[:, 0],
2 ** detector.scales_, facecolors='none', edgecolors='r')
plt.show()
+12 -3
View File
@@ -27,9 +27,18 @@ tform = tf.AffineTransform(scale=(1.3, 1.1), rotation=0.5,
img3 = tf.warp(img1, tform)
descriptor_extractor = ORB(n_keypoints=200)
keypoints1, descriptors1 = descriptor_extractor.detect_and_extract(img1)
keypoints2, descriptors2 = descriptor_extractor.detect_and_extract(img2)
keypoints3, descriptors3 = descriptor_extractor.detect_and_extract(img3)
descriptor_extractor.detect_and_extract(img1)
keypoints1 = descriptor_extractor.keypoints_
descriptors1 = descriptor_extractor.descriptors_
descriptor_extractor.detect_and_extract(img2)
keypoints2 = descriptor_extractor.keypoints_
descriptors2 = descriptor_extractor.descriptors_
descriptor_extractor.detect_and_extract(img3)
keypoints3 = descriptor_extractor.keypoints_
descriptors3 = descriptor_extractor.descriptors_
matches12 = match_descriptors(descriptors1, descriptors2, cross_check=True)
matches13 = match_descriptors(descriptors1, descriptors3, cross_check=True)
+20 -18
View File
@@ -74,21 +74,23 @@ class BRIEF(DescriptorExtractor):
[0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=int32)
>>> keypoints1 = corner_peaks(corner_harris(square1), min_distance=1)
>>> keypoints2 = corner_peaks(corner_harris(square2), min_distance=1)
>>> extractor = BRIEF(patch_size=5)
>>> descs1, _ = extractor.extract(square1, keypoints1)
>>> descs2, _ = extractor.extract(square2, keypoints2)
>>> matches = match_descriptors(descs1, descs2)
>>> extractor1 = BRIEF(patch_size=5)
>>> extractor2 = BRIEF(patch_size=5)
>>> extractor1.extract(square1, keypoints1)
>>> extractor2.extract(square2, keypoints2)
>>> matches = match_descriptors(extractor1.descriptors_,
... extractor2.descriptors_)
>>> matches
array([[0, 0],
[1, 1],
[2, 2],
[3, 3]])
>>> keypoints1[matches[:, 0]]
>>> extractor1.keypoints_[matches[:, 0]]
array([[2, 2],
[2, 5],
[5, 2],
[5, 5]])
>>> keypoints2[matches[:, 1]]
>>> extractor2.keypoints_[matches[:, 1]]
array([[2, 2],
[2, 6],
[6, 2],
@@ -119,15 +121,15 @@ class BRIEF(DescriptorExtractor):
keypoints : (N, 2) array
Keypoint coordinates as ``(row, col)``.
Returns
-------
descriptors : (Q, `descriptor_size`) array of dtype bool
Attributes
----------
descriptors_ : (Q, `descriptor_size`) array of dtype bool
2D ndarray of binary descriptors of size `descriptor_size` for Q
keypoints after filtering out border keypoints with value at an
index ``(i, j)`` either being ``True`` or ``False`` representing
the outcome of the intensity comparison for i-th keypoint on j-th
decision pixel-pair. It is ``Q == np.sum(mask)``.
mask : (N, ) array of dtype bool
mask_ : (N, ) array of dtype bool
Mask indicating whether a keypoint has been filtered out
(``False``) or is described in the `descriptors` array (``True``).
@@ -163,14 +165,14 @@ class BRIEF(DescriptorExtractor):
# Removing keypoints that are within (patch_size / 2) distance from the
# image border
mask = _mask_border_keypoints(image.shape, keypoints, patch_size // 2)
self.mask_ = _mask_border_keypoints(image.shape, keypoints,
patch_size // 2)
keypoints = np.array(keypoints[mask, :], dtype=np.intp, order='C',
copy=False)
keypoints = np.array(keypoints[self.mask_, :], dtype=np.intp,
order='C', copy=False)
descriptors = np.zeros((keypoints.shape[0], desc_size),
dtype=bool, order='C')
self.descriptors_ = np.zeros((keypoints.shape[0], desc_size),
dtype=bool, order='C')
_brief_loop(image, descriptors.view(np.uint8), keypoints, pos1, pos2)
return descriptors, mask
_brief_loop(image, self.descriptors_.view(np.uint8), keypoints,
pos1, pos2)
+11 -7
View File
@@ -157,8 +157,9 @@ class CenSurE(FeatureDetector):
>>> from skimage.color import rgb2gray
>>> from skimage.feature import CenSurE
>>> img = rgb2gray(lena()[100:300, 100:300])
>>> keypoints, scales = CenSurE().detect(img)
>>> keypoints
>>> censure = CenSurE()
>>> censure.detect(img)
>>> censure.keypoints_
array([[ 71, 148],
[ 77, 186],
[ 78, 189],
@@ -174,7 +175,7 @@ class CenSurE(FeatureDetector):
[171, 29],
[179, 20],
[194, 65]])
>>> scales
>>> censure.scales_
array([2, 4, 2, 3, 4, 2, 2, 3, 4, 6, 3, 2, 3, 4, 2])
"""
@@ -204,8 +205,8 @@ class CenSurE(FeatureDetector):
image : 2D ndarray
Input image.
Returns
-------
Attributes
----------
keypoints : (N, 2) array
Keypoint coordinates as ``(row, col)``.
scales : (N, ) array
@@ -257,7 +258,9 @@ class CenSurE(FeatureDetector):
scales = scales + self.min_scale + 1
if self.mode == 'dob':
return keypoints, scales
self.keypoints_ = keypoints
self.scales_ = scales
return
cumulative_mask = np.zeros(keypoints.shape[0], dtype=np.bool)
@@ -276,4 +279,5 @@ class CenSurE(FeatureDetector):
_mask_border_keypoints(image.shape, keypoints, c)
& (scales == i))
return keypoints[cumulative_mask], scales[cumulative_mask]
self.keypoints_ = keypoints[cumulative_mask]
self.scales_ = scales[cumulative_mask]
+58 -32
View File
@@ -69,23 +69,25 @@ class ORB(FeatureDetector, DescriptorExtractor):
>>> square = np.random.rand(20, 20)
>>> img1[40:60, 40:60] = square
>>> img2[53:73, 53:73] = square
>>> detector_extractor = ORB(n_keypoints=5)
>>> keypoints1, descriptors1 = detector_extractor.detect_and_extract(img1)
>>> keypoints2, descriptors2 = detector_extractor.detect_and_extract(img2)
>>> matches = match_descriptors(descriptors1, descriptors2)
>>> detector_extractor1 = ORB(n_keypoints=5)
>>> detector_extractor2 = ORB(n_keypoints=5)
>>> detector_extractor1.detect_and_extract(img1)
>>> detector_extractor2.detect_and_extract(img2)
>>> matches = match_descriptors(detector_extractor1.descriptors_,
... detector_extractor2.descriptors_)
>>> matches
array([[0, 0],
[1, 1],
[2, 2],
[3, 3],
[4, 4]])
>>> keypoints1[matches[:, 0]]
>>> detector_extractor1.keypoints_[matches[:, 0]]
array([[ 42., 40.],
[ 47., 58.],
[ 44., 40.],
[ 59., 42.],
[ 45., 44.]])
>>> keypoints2[matches[:, 1]]
>>> detector_extractor2.keypoints_[matches[:, 1]]
array([[ 55., 53.],
[ 60., 71.],
[ 57., 53.],
@@ -140,15 +142,15 @@ class ORB(FeatureDetector, DescriptorExtractor):
image : 2D array
Input image.
Returns
-------
keypoints : (N, 2) array
Attributes
----------
keypoints_ : (N, 2) array
Keypoint coordinates as ``(row, col)``.
scales : (N, ) array
scales_ : (N, ) array
Corresponding scales.
orientations : (N, ) array
orientations_ : (N, ) array
Corresponding orientations in radians.
responses : (N, ) array
responses_ : (N, ) array
Corresponding Harris corner responses.
"""
@@ -157,7 +159,7 @@ class ORB(FeatureDetector, DescriptorExtractor):
keypoints_list = []
orientations_list = []
octave_list = []
scales_list = []
responses_list = []
for octave in range(len(pyramid)):
@@ -169,22 +171,27 @@ class ORB(FeatureDetector, DescriptorExtractor):
keypoints_list.append(keypoints * self.downscale ** octave)
orientations_list.append(orientations)
octave_list.append(self.downscale ** octave
scales_list.append(self.downscale ** octave
* np.ones(keypoints.shape[0], dtype=np.intp))
responses_list.append(responses)
keypoints = np.vstack(keypoints_list)
orientations = np.hstack(orientations_list)
scales = np.hstack(octave_list)
scales = np.hstack(scales_list)
responses = np.hstack(responses_list)
if keypoints.shape[0] < self.n_keypoints:
return keypoints, scales, orientations, responses
self.keypoints_ = keypoints
self.scales_ = scales
self.orientations_ = orientations
self.responses_ = responses
else:
# Choose best n_keypoints according to Harris corner response
best_indices = responses.argsort()[::-1][:self.n_keypoints]
return (keypoints[best_indices], scales[best_indices],
orientations[best_indices], responses[best_indices])
self.keypoints_ = keypoints[best_indices]
self.scales_ = scales[best_indices]
self.orientations_ = orientations[best_indices]
self.responses_ = responses[best_indices]
def _extract_octave(self, octave_image, keypoints, orientations):
mask = _mask_border_keypoints(octave_image.shape, keypoints,
@@ -217,15 +224,15 @@ class ORB(FeatureDetector, DescriptorExtractor):
orientations : (N, ) array
Corresponding orientations in radians.
Returns
-------
descriptors : (Q, `descriptor_size`) array of dtype bool
Attributes
----------
descriptors_ : (Q, `descriptor_size`) array of dtype bool
2D array of binary descriptors of size `descriptor_size` for Q
keypoints after filtering out border keypoints with value at an
index ``(i, j)`` either being ``True`` or ``False`` representing
the outcome of the intensity comparison for i-th keypoint on j-th
decision pixel-pair. It is ``Q == np.sum(mask)``.
mask : (N, ) array of dtype bool
mask_ : (N, ) array of dtype bool
Mask indicating whether a keypoint has been filtered out
(``False``) or is described in the `descriptors` array (``True``).
@@ -260,10 +267,8 @@ class ORB(FeatureDetector, DescriptorExtractor):
descriptors_list.append(descriptors)
mask_list.append(mask)
descriptors = np.vstack(descriptors_list).view(np.bool)
mask = np.hstack(mask_list)
return descriptors, mask
self.descriptors_ = np.vstack(descriptors_list).view(np.bool)
self.mask_ = np.hstack(mask_list)
def detect_and_extract(self, image):
"""Detect oriented FAST keypoints and extract rBRIEF descriptors.
@@ -276,11 +281,17 @@ class ORB(FeatureDetector, DescriptorExtractor):
image : 2D array
Input image.
Returns
-------
keypoints : (Q, 2) array
Attributes
----------
keypoints_ : (N, 2) array
Keypoint coordinates as ``(row, col)``.
descriptors : (Q, `descriptor_size`) array of dtype bool
scales_ : (N, ) array
Corresponding scales.
orientations_ : (N, ) array
Corresponding orientations in radians.
responses_ : (N, ) array
Corresponding Harris corner responses.
descriptors_ : (Q, `descriptor_size`) array of dtype bool
2D array of binary descriptors of size `descriptor_size` for Q
keypoints after filtering out border keypoints with value at an
index ``(i, j)`` either being ``True`` or ``False`` representing
@@ -293,6 +304,8 @@ class ORB(FeatureDetector, DescriptorExtractor):
keypoints_list = []
responses_list = []
scales_list = []
orientations_list = []
descriptors_list = []
for octave in range(len(pyramid)):
@@ -313,15 +326,28 @@ class ORB(FeatureDetector, DescriptorExtractor):
keypoints_list.append(keypoints[mask] * self.downscale ** octave)
responses_list.append(responses[mask])
orientations_list.append(orientations[mask])
scales_list.append(self.downscale ** octave
* np.ones(keypoints.shape[0], dtype=np.intp))
descriptors_list.append(descriptors)
keypoints = np.vstack(keypoints_list)
responses = np.hstack(responses_list)
scales = np.hstack(scales_list)
orientations = np.hstack(orientations_list)
descriptors = np.vstack(descriptors_list).view(np.bool)
if keypoints.shape[0] < self.n_keypoints:
return keypoints, descriptors
self.keypoints_ = keypoints
self.scales_ = scales
self.orientations_ = orientations
self.responses_ = responses
self.descriptors_ = descriptors
else:
# Choose best n_keypoints according to Harris corner response
best_indices = responses.argsort()[::-1][:self.n_keypoints]
return keypoints[best_indices], descriptors[best_indices]
self.keypoints_ = keypoints[best_indices]
self.scales_ = scales[best_indices]
self.orientations_ = orientations[best_indices]
self.responses_ = responses[best_indices]
self.descriptors_ = descriptors[best_indices]
+7 -7
View File
@@ -21,7 +21,7 @@ def test_normal_mode():
extractor = BRIEF(descriptor_size=8, sigma=2)
descriptors, mask = extractor.extract(img, keypoints[:8])
extractor.extract(img, keypoints[:8])
expected = np.array([[ True, False, True, False, True, True, False, False],
[False, False, False, False, True, False, False, False],
@@ -32,7 +32,7 @@ def test_normal_mode():
[False, True, True, True, False, False, True, False],
[False, False, False, False, True, False, False, False]], dtype=bool)
assert_array_equal(descriptors, expected)
assert_array_equal(extractor.descriptors_, expected)
def test_uniform_mode():
@@ -43,7 +43,7 @@ def test_uniform_mode():
extractor = BRIEF(descriptor_size=8, sigma=2, mode='uniform')
descriptors, mask = extractor.extract(img, keypoints[:8])
extractor.extract(img, keypoints[:8])
expected = np.array([[ True, False, True, False, False, True, False, False],
[False, True, False, False, True, True, True, True],
@@ -54,7 +54,7 @@ def test_uniform_mode():
[False, False, True, True, False, False, True, True],
[ True, True, False, False, False, False, False, False]], dtype=bool)
assert_array_equal(descriptors, expected)
assert_array_equal(extractor.descriptors_, expected)
def test_unsupported_mode():
@@ -66,10 +66,10 @@ def test_border():
keypoints = np.array([[1, 1], [20, 20], [50, 50], [80, 80]])
extractor = BRIEF(patch_size=41)
descs, mask = extractor.extract(img, keypoints)
extractor.extract(img, keypoints)
assert descs.shape[0] == 3
assert_array_equal(mask, (False, True, True, True))
assert extractor.descriptors_.shape[0] == 3
assert_array_equal(extractor.mask_, (False, True, True, True))
if __name__ == '__main__':
+10 -10
View File
@@ -28,7 +28,7 @@ def test_keypoints_censure_moon_image_dob():
"""Verify the actual Censure keypoints and their corresponding scale with
the expected values for DoB filter."""
detector = CenSurE()
keypoints, scales = detector.detect(img)
detector.detect(img)
expected_keypoints = np.array([[ 21, 497],
[ 36, 46],
[119, 350],
@@ -40,8 +40,8 @@ def test_keypoints_censure_moon_image_dob():
[467, 260]])
expected_scales = np.array([3, 4, 4, 2, 2, 3, 2, 2, 2])
assert_array_equal(expected_keypoints, keypoints)
assert_array_equal(expected_scales, scales)
assert_array_equal(expected_keypoints, detector.keypoints_)
assert_array_equal(expected_scales, detector.scales_)
def test_keypoints_censure_moon_image_octagon():
@@ -49,7 +49,7 @@ def test_keypoints_censure_moon_image_octagon():
the expected values for Octagon filter."""
detector = CenSurE(mode='octagon')
keypoints, scales = detector.detect(img)
detector.detect(img)
expected_keypoints = np.array([[ 21, 496],
[ 35, 46],
[287, 250],
@@ -58,15 +58,15 @@ def test_keypoints_censure_moon_image_octagon():
expected_scales = np.array([3, 4, 2, 2, 2])
assert_array_equal(expected_keypoints, keypoints)
assert_array_equal(expected_scales, scales)
assert_array_equal(expected_keypoints, detector.keypoints_)
assert_array_equal(expected_scales, detector.scales_)
def test_keypoints_censure_moon_image_star():
"""Verify the actual Censure keypoints and their corresponding scale with
the expected values for STAR filter."""
detector = CenSurE(mode='star')
keypoints, scales = detector.detect(img)
detector.detect(img)
expected_keypoints = np.array([[ 21, 497],
[ 36, 46],
[117, 356],
@@ -78,10 +78,10 @@ def test_keypoints_censure_moon_image_star():
[463, 116],
[467, 260]])
expected_scale = np.array([3, 3, 6, 2, 3, 2, 3, 5, 2, 2])
expected_scales = np.array([3, 3, 6, 2, 3, 2, 3, 5, 2, 2])
assert_array_equal(expected_keypoints, keypoints)
assert_array_equal(expected_scale, scales)
assert_array_equal(expected_keypoints, detector.keypoints_)
assert_array_equal(expected_scales, detector.scales_)
if __name__ == '__main__':
+29 -25
View File
@@ -10,7 +10,7 @@ img = rgb2gray(lena())
def test_keypoints_orb_desired_no_of_keypoints():
detector_extractor = ORB(n_keypoints=10, fast_n=12, fast_threshold=0.20)
keypoints, scales, orientations, responses = detector_extractor.detect(img)
detector_extractor.detect(img)
exp_rows = np.array([ 435. , 435.6 , 376. , 455. , 434.88, 269. ,
375.6 , 310.8 , 413. , 311.04])
@@ -29,22 +29,23 @@ def test_keypoints_orb_desired_no_of_keypoints():
0.39154173, 0.39084861, 0.39063076,
0.37602487])
assert_almost_equal(exp_rows, keypoints[:, 0])
assert_almost_equal(exp_cols, keypoints[:, 1])
assert_almost_equal(exp_scales, scales)
assert_almost_equal(exp_response, responses)
assert_almost_equal(exp_orientations, np.rad2deg(orientations), 5)
assert_almost_equal(exp_rows, detector_extractor.keypoints_[:, 0])
assert_almost_equal(exp_cols, detector_extractor.keypoints_[:, 1])
assert_almost_equal(exp_scales, detector_extractor.scales_)
assert_almost_equal(exp_response, detector_extractor.responses_)
assert_almost_equal(exp_orientations,
np.rad2deg(detector_extractor.orientations_), 5)
keypoints, _ = detector_extractor.detect_and_extract(img)
assert_almost_equal(exp_rows, keypoints[:, 0])
assert_almost_equal(exp_cols, keypoints[:, 1])
detector_extractor.detect_and_extract(img)
assert_almost_equal(exp_rows, detector_extractor.keypoints_[:, 0])
assert_almost_equal(exp_cols, detector_extractor.keypoints_[:, 1])
def test_keypoints_orb_less_than_desired_no_of_keypoints():
img = rgb2gray(lena())
detector_extractor = ORB(n_keypoints=15, fast_n=12,
fast_threshold=0.33, downscale=2, n_scales=2)
keypoints, scales, orientations, responses = detector_extractor.detect(img)
detector_extractor.detect(img)
exp_rows = np.array([ 67., 247., 269., 413., 435., 230., 264.,
330., 372.])
@@ -61,15 +62,16 @@ def test_keypoints_orb_less_than_desired_no_of_keypoints():
0.39063076, 0.96770745, 0.04935129,
0.21431068, 0.15826555, 0.42403573])
assert_almost_equal(exp_rows, keypoints[:, 0])
assert_almost_equal(exp_cols, keypoints[:, 1])
assert_almost_equal(exp_scales, scales)
assert_almost_equal(exp_response, responses)
assert_almost_equal(exp_orientations, np.rad2deg(orientations), 5)
assert_almost_equal(exp_rows, detector_extractor.keypoints_[:, 0])
assert_almost_equal(exp_cols, detector_extractor.keypoints_[:, 1])
assert_almost_equal(exp_scales, detector_extractor.scales_)
assert_almost_equal(exp_response, detector_extractor.responses_)
assert_almost_equal(exp_orientations,
np.rad2deg(detector_extractor.orientations_), 5)
keypoints, _ = detector_extractor.detect_and_extract(img)
assert_almost_equal(exp_rows, keypoints[:, 0])
assert_almost_equal(exp_cols, keypoints[:, 1])
detector_extractor.detect_and_extract(img)
assert_almost_equal(exp_rows, detector_extractor.keypoints_[:, 0])
assert_almost_equal(exp_cols, detector_extractor.keypoints_[:, 1])
def test_descriptor_orb():
@@ -96,14 +98,16 @@ def test_descriptor_orb():
[False, True, False, False, True, False, False, False, True, True],
[ True, False, True, False, False, False, True, True, False, False]], dtype=bool)
keypoints1, scales1, orientations1, responses1 \
= detector_extractor.detect(img)
descriptors1, mask1 \
= detector_extractor.extract(img, keypoints1, scales1, orientations1)
assert_array_equal(exp_descriptors, descriptors1[100:120, 10:20])
detector_extractor.detect(img)
detector_extractor.extract(img, detector_extractor.keypoints_,
detector_extractor.scales_,
detector_extractor.orientations_)
assert_array_equal(exp_descriptors,
detector_extractor.descriptors_[100:120, 10:20])
keypoints2, descriptors2 = detector_extractor.detect_and_extract(img)
assert_array_equal(exp_descriptors, descriptors2[100:120, 10:20])
detector_extractor.detect_and_extract(img)
assert_array_equal(exp_descriptors,
detector_extractor.descriptors_[100:120, 10:20])
if __name__ == '__main__':
+6
View File
@@ -5,6 +5,9 @@ from skimage.util import img_as_float
class FeatureDetector(object):
def __init__(self):
self.keypoints_ = np.array([])
def detect(self, image):
"""Detect keypoints in image.
@@ -19,6 +22,9 @@ class FeatureDetector(object):
class DescriptorExtractor(object):
def __init__(self):
self.descriptors_ = np.array([])
def extract(self, image, keypoints):
"""Extract feature descriptors in image for given keypoints.