diff --git a/skimage/feature/__init__.py b/skimage/feature/__init__.py index 89370fd7..0487f7b6 100644 --- a/skimage/feature/__init__.py +++ b/skimage/feature/__init__.py @@ -9,7 +9,7 @@ from .corner import (corner_kitchen_rosenfeld, corner_harris, hessian_matrix_eigvals) from .corner_cy import corner_moravec, corner_orientations from .template import match_template -from ._brief import brief +from ._brief import descriptor_brief from .match import match_binary_descriptors from .util import pairwise_hamming_distance from .censure import keypoints_censure @@ -29,7 +29,7 @@ __all__ = ['daisy', 'corner_peaks', 'corner_moravec', 'match_template', - 'brief', + 'descriptor_brief', 'pairwise_hamming_distance', 'match_binary_descriptors', 'keypoints_censure', diff --git a/skimage/feature/_brief.py b/skimage/feature/_brief.py index be7e4ac9..94c92f04 100644 --- a/skimage/feature/_brief.py +++ b/skimage/feature/_brief.py @@ -2,13 +2,13 @@ import numpy as np from scipy.ndimage.filters import gaussian_filter from .util import (_mask_border_keypoints, pairwise_hamming_distance, - _prepare_grayscale_input_2D) + _prepare_grayscale_input_2D) from ._brief_cy import _brief_loop -def brief(image, keypoints, descriptor_size=256, mode='normal', patch_size=49, - sample_seed=1, variance=2): +def descriptor_brief(image, keypoints, descriptor_size=256, mode='normal', + patch_size=49, sample_seed=1, variance=2): """**Experimental function**. Extract BRIEF Descriptor about given keypoints for a given image. @@ -57,8 +57,9 @@ def brief(image, keypoints, descriptor_size=256, mode='normal', patch_size=49, Examples -------- - >> from skimage.feature import corner_peaks, corner_harris, \\ - .. pairwise_hamming_distance, brief, match_keypoints_brief + >> import numpy as np + >> from skimage.feature.corner import corner_peaks, corner_harris + >> from skimage.feature import pairwise_hamming_distance, descriptor_brief, match_binary_descriptors >> square1 = np.zeros([8, 8], dtype=np.int32) >> square1[2:6, 2:6] = 1 >> square1 @@ -76,7 +77,7 @@ def brief(image, keypoints, descriptor_size=256, mode='normal', patch_size=49, [2, 5], [5, 2], [5, 5]]) - >> descriptors1, keypoints1 = brief(square1, keypoints1, patch_size=5) + >> descriptors1, keypoints1 = descriptor_brief(square1, keypoints1, patch_size=5) >> keypoints1 array([[2, 2], [2, 5], @@ -100,7 +101,7 @@ def brief(image, keypoints, descriptor_size=256, mode='normal', patch_size=49, [2, 6], [6, 2], [6, 6]]) - >> descriptors2, keypoints2 = brief(square2, keypoints2, patch_size=5) + >> descriptors2, keypoints2 = descriptor_brief(square2, keypoints2, patch_size=5) >> keypoints2 array([[2, 2], [2, 6], @@ -111,8 +112,11 @@ def brief(image, keypoints, descriptor_size=256, mode='normal', patch_size=49, [ 0.3203125, 0.03125 , 0.640625 , 0.375 ], [ 0.375 , 0.6328125, 0.0390625, 0.328125 ], [ 0.625 , 0.3671875, 0.34375 , 0.0234375]]) - >> match_keypoints_brief(keypoints1, descriptors1, - .. keypoints2, descriptors2) + >> matched_kpts, mask1, mask2 = match_binary_descriptors(keypoints1, + descriptors1, + keypoints2, + descriptors2) + >> matched_kpts array([[[ 2, 2], [ 2, 2]], @@ -165,6 +169,7 @@ def brief(image, keypoints, descriptor_size=256, mode='normal', patch_size=49, samples = np.random.randint(-(patch_size - 2) // 2, (patch_size // 2) + 1, (descriptor_size * 2, 2)) + samples = np.array(samples, dtype=np.int32) pos1, pos2 = np.split(samples, 2) pos1 = np.ascontiguousarray(pos1) diff --git a/skimage/feature/match.py b/skimage/feature/match.py index 4093c6b6..d9ae886a 100644 --- a/skimage/feature/match.py +++ b/skimage/feature/match.py @@ -73,7 +73,7 @@ def match_binary_descriptors(keypoints1, descriptors1, keypoints2, dtype=np.intp) matches[:, 0, :] = keypoints1[row_check] matches[:, 1, :] = matched_keypoints2[row_check] - mask1 = np.where(row_check == True) + mask1 = np.where(row_check == True)[0] mask2 = np.argmin(distance, axis=1)[row_check] return matches, mask1, mask2 diff --git a/skimage/feature/tests/_test_brief.py b/skimage/feature/tests/_test_brief.py index c7e3a134..7983b023 100644 --- a/skimage/feature/tests/_test_brief.py +++ b/skimage/feature/tests/_test_brief.py @@ -3,84 +3,56 @@ from numpy.testing import assert_array_equal, assert_raises from skimage import data from skimage import transform as tf from skimage.color import rgb2gray -from skimage.feature import (brief, match_binary_descriptors, corner_peaks, +from skimage.feature import (descriptor_brief, match_binary_descriptors, corner_peaks, corner_harris) -def test_brief_color_image_unsupported_error(): +def test_descriptor_brief_color_image_unsupported_error(): """Brief descriptors can be evaluated on gray-scale images only.""" img = np.zeros((20, 20, 3)) keypoints = [[7, 5], [11, 13]] - assert_raises(ValueError, brief, img, keypoints) + assert_raises(ValueError, descriptor_brief, img, keypoints) -def test_matching_brief_descriptors_lena_translation(): - """Test matched keypoints between lena image and its translated version.""" +def test_descriptor_brief_normal_mode(): + """Verify the computed BRIEF descriptors with expected for normal mode.""" img = data.lena() img = rgb2gray(img) - img.shape - tform = tf.SimilarityTransform(scale=1, rotation=0, translation=(15, 20)) - translated_img = tf.warp(img, tform) + keypoints = corner_peaks(corner_harris(img), min_distance=5) + descriptors, keypoints = descriptor_brief(img, keypoints[:8], + descriptor_size=8) - keypoints1 = corner_peaks(corner_harris(img), min_distance=5) - descriptors1, keypoints1 = brief(img, keypoints1, descriptor_size=512) + expected = np.array([[ True, False, True, False, True, True, False, False], + [False, False, False, False, True, False, False, False], + [ True, True, True, True, True, True, True, True], + [ True, False, True, True, False, True, False, True], + [False, True, True, True, True, True, True, True], + [ True, False, False, False, False, True, False, True], + [False, True, True, True, False, False, True, False], + [False, False, False, False, True, False, False, False]], dtype=bool) - keypoints2 = corner_peaks(corner_harris(translated_img), min_distance=5) - descriptors2, keypoints2 = brief(translated_img, keypoints2, - descriptor_size=512) - - matched_keypoints, m1, m2 = match_binary_descriptors(keypoints1, - descriptors1, - keypoints2, - descriptors2, - threshold=0.10) - - assert_array_equal(matched_keypoints[:, 0, :], matched_keypoints[:, 1, :] + - [20, 15]) + assert_array_equal(descriptors, expected) -def test_matching_brief_descriptors_lena_rotation(): - """Verify matched keypoints result between lena image and its rotated - version with the expected keypoint pairs.""" +def test_descriptor_brief_uniform_mode(): + """Verify the computed BRIEF descriptors with expected for uniform mode.""" img = data.lena() img = rgb2gray(img) - img.shape - tform = tf.SimilarityTransform(scale=1, rotation=0.10, translation=(0, 0)) - rotated_img = tf.warp(img, tform) + keypoints = corner_peaks(corner_harris(img), min_distance=5) + descriptors, keypoints = descriptor_brief(img, keypoints[:8], + descriptor_size=8, + mode='uniform') - keypoints1 = corner_peaks(corner_harris(img), min_distance=5) - descriptors1, keypoints1 = brief(img, keypoints1, descriptor_size=512) + expected = np.array([[ True, False, True, False, False, True, False, False], + [False, True, False, False, True, True, True, True], + [ True, False, False, False, False, False, False, False], + [False, True, True, False, False, False, True, False], + [False, False, False, False, False, False, True, False], + [False, True, False, False, True, False, False, False], + [False, False, True, True, False, False, True, True], + [ True, True, False, False, False, False, False, False]], dtype=bool) - keypoints2 = corner_peaks(corner_harris(rotated_img), min_distance=5) - descriptors2, keypoints2 = brief(rotated_img, keypoints2, - descriptor_size=512) - - matched_keypoints, m1, m2 = match_binary_descriptors(keypoints1, - descriptors1, - keypoints2, - descriptors2, - threshold=0.07) - - expected = np.array([[[263, 272], - [234, 298]], - - [[271, 120], - [258, 146]], - - [[323, 164], - [305, 195]], - - [[414, 70], - [405, 111]], - - [[435, 181], - [415, 223]], - - [[454, 176], - [435, 221]]]) - - print matched_keypoints - assert_array_equal(matched_keypoints, expected) + assert_array_equal(descriptors, expected) if __name__ == '__main__': diff --git a/skimage/feature/tests/test_match.py b/skimage/feature/tests/test_match.py new file mode 100644 index 00000000..1e1435e3 --- /dev/null +++ b/skimage/feature/tests/test_match.py @@ -0,0 +1,173 @@ +import numpy as np +from numpy.testing import assert_array_equal, assert_raises +from skimage import data +from skimage import transform as tf +from skimage.color import rgb2gray +from skimage.feature import (descriptor_brief, match_binary_descriptors, + corner_peaks, corner_harris) + + +def test_match_binary_descriptors_unequal_descriptor_keypoints_error(): + """Number of descriptors should be equal to the number of keypoints.""" + kp1 = np.array([[40, 50], + [60, 40], + [30, 70]]) + des1 = np.array([[True, True, False, True], + [False, True, False, True]]) + kp2 = np.array([[60, 50], + [50, 80]]) + des2 = np.array([[True, False, False, True], + [False, True, True, True]]) + assert_raises(ValueError, match_binary_descriptors, kp1, des1, kp2, des2) + + +def test_match_binary_descriptors_unequal_descriptor_sizes_error(): + """Sizes of descriptors of keypoints to be matched should be equal.""" + kp1 = np.array([[40, 50], + [60, 40]]) + des1 = np.array([[True, True, False, True], + [False, True, False, True]]) + kp2 = np.array([[60, 50], + [50, 80]]) + des2 = np.array([[True, False, False, True, False], + [False, True, True, True, False]]) + assert_raises(ValueError, match_binary_descriptors, kp1, des1, kp2, des2) + + +def test_match_binary_descriptors_lena_rotation_crosscheck_false(): + """Verify matched keypoints and their corresponding masks results between + lena image and its rotated version with the expected keypoint pairs with + cross_check disabled.""" + img = data.lena() + img = rgb2gray(img) + tform = tf.SimilarityTransform(scale=1, rotation=0.15, translation=(0, 0)) + rotated_img = tf.warp(img, tform) + + keypoints1 = corner_peaks(corner_harris(img), min_distance=5) + descriptors1, keypoints1 = descriptor_brief(img, keypoints1, descriptor_size=512) + + keypoints2 = corner_peaks(corner_harris(rotated_img), min_distance=5) + descriptors2, keypoints2 = descriptor_brief(rotated_img, keypoints2, + descriptor_size=512) + + matched_keypoints, m1, m2 = match_binary_descriptors(keypoints1, + descriptors1, + keypoints2, + descriptors2, + threshold=0.13, + cross_check=False) + + expected_mask1 = np.array([11, 12, 16, 20, 24, 26, 27, 29, 35, 39, 40, 42, 45]) + expected_mask2 = np.array([ 1, 3, 0, 4, 6, 7, 8, 9, 10, 10, 11, 12, 13]) + expected = np.array([[[245, 141], + [221, 176]], + + [[247, 130], + [225, 165]], + + [[263, 272], + [219, 309]], + + [[271, 120], + [250, 159]], + + [[311, 174], + [282, 218]], + + [[323, 164], + [294, 210]], + + [[327, 147], + [301, 195]], + + [[377, 157], + [349, 211]], + + [[414, 70], + [399, 131]], + + [[425, 67], + [399, 131]], + + [[435, 181], + [403, 244]], + + [[454, 176], + [423, 242]], + + [[467, 166], + [437, 234]]]) + + assert_array_equal(matched_keypoints, expected) + assert_array_equal(m1, expected_mask1) + assert_array_equal(m2, expected_mask2) + + +def test_match_binary_descriptors_lena_rotation_crosscheck_true(): + """Verify matched keypoints and their corresponding masks results between + lena image and its rotated version with the expected keypoint pairs with + cross_check enabled.""" + img = data.lena() + img = rgb2gray(img) + tform = tf.SimilarityTransform(scale=1, rotation=0.15, translation=(0, 0)) + rotated_img = tf.warp(img, tform) + + keypoints1 = corner_peaks(corner_harris(img), min_distance=5) + descriptors1, keypoints1 = descriptor_brief(img, keypoints1, descriptor_size=512) + + keypoints2 = corner_peaks(corner_harris(rotated_img), min_distance=5) + descriptors2, keypoints2 = descriptor_brief(rotated_img, keypoints2, + descriptor_size=512) + + matched_keypoints, m1, m2 = match_binary_descriptors(keypoints1, + descriptors1, + keypoints2, + descriptors2, + threshold=0.13) + + expected = np.array([[[245, 141], + [221, 176]], + + [[247, 130], + [225, 165]], + + [[263, 272], + [219, 309]], + + [[271, 120], + [250, 159]], + + [[311, 174], + [282, 218]], + + [[323, 164], + [294, 210]], + + [[327, 147], + [301, 195]], + + [[377, 157], + [349, 211]], + + [[414, 70], + [399, 131]], + + [[435, 181], + [403, 244]], + + [[454, 176], + [423, 242]], + + [[467, 166], + [437, 234]]]) + + expected_mask1 = np.array([11, 12, 16, 20, 24, 26, 27, 29, 35, 40, 42, 45]) + expected_mask2 = np.array([ 1, 3, 0, 4, 6, 7, 8, 9, 10, 11, 12, 13]) + assert_array_equal(matched_keypoints, expected) + assert_array_equal(m1, expected_mask1) + assert_array_equal(m2, expected_mask2) + + +if __name__ == '__main__': + from numpy import testing + testing.run_module_suite() diff --git a/skimage/feature/util.py b/skimage/feature/util.py index 9bbbaef5..29b36475 100644 --- a/skimage/feature/util.py +++ b/skimage/feature/util.py @@ -47,3 +47,9 @@ def pairwise_hamming_distance(array1, array2): """ distance = (array1[:, None] != array2[None]).mean(axis=2) return distance + + +def _create_keypoint_recarray(row, col, octave=None, orientation=None, + response=None): + kpt_recarray = np.recarray() +