Files
scikit-image/skimage/feature/_brief.py
T
2013-11-04 08:05:42 +01:00

230 lines
8.3 KiB
Python

import numpy as np
from scipy.ndimage.filters import gaussian_filter
from ..util import img_as_float
from .util import _mask_border_keypoints, pairwise_hamming_distance
from ._brief_cy import _brief_loop
def 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.
Parameters
----------
image : 2D ndarray
Input image.
keypoints : (P, 2) ndarray
Array of keypoint locations in the format (row, col).
descriptor_size : int
Size of BRIEF descriptor about each keypoint. Sizes 128, 256 and 512
preferred by the authors. Default is 256.
mode : string
Probability distribution for sampling location of decision pixel-pairs
around keypoints. Default is 'normal' otherwise uniform.
patch_size : int
Length of the two dimensional square patch sampling region around
the keypoints. Default is 49.
sample_seed : int
Seed for sampling the decision pixel-pairs. From a square window with
length patch_size, pixel pairs are sampled using the `mode` parameter
to build the descriptors using intensity comparison. The value of
`sample_seed` should be the same for the images to be matched while
building the descriptors. Default is 1.
variance : float
Variance of the Gaussian Low Pass filter applied on the image to
alleviate noise sensitivity. Default is 2.
Returns
-------
descriptors : (Q, `descriptor_size`) ndarray of dtype bool
2D ndarray of binary descriptors of size `descriptor_size` about Q
keypoints after filtering out border keypoints with value at an index
(i, j) either being True or False representing the outcome
of Intensity comparison about ith keypoint on jth decision pixel-pair.
keypoints : (Q, 2) ndarray
Location i.e. (row, col) of keypoints after removing out those that
are near border.
References
----------
.. [1] Michael Calonder, Vincent Lepetit, Christoph Strecha and Pascal Fua
"BRIEF : Binary robust independent elementary features",
http://cvlabwww.epfl.ch/~lepetit/papers/calonder_eccv10.pdf
Examples
--------
>> from skimage.feature import corner_peaks, corner_harris, \\
.. pairwise_hamming_distance, brief, match_keypoints_brief
>> square1 = np.zeros([8, 8], dtype=np.int32)
>> square1[2:6, 2:6] = 1
>> square1
array([[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 1, 1, 0, 0],
[0, 0, 1, 1, 1, 1, 0, 0],
[0, 0, 1, 1, 1, 1, 0, 0],
[0, 0, 1, 1, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0]], dtype=int32)
>> keypoints1 = corner_peaks(corner_harris(square1), min_distance=1)
>> keypoints1
array([[2, 2],
[2, 5],
[5, 2],
[5, 5]])
>> descriptors1, keypoints1 = brief(square1, keypoints1, patch_size=5)
>> keypoints1
array([[2, 2],
[2, 5],
[5, 2],
[5, 5]])
>> square2 = np.zeros([9, 9], dtype=np.int32)
>> square2[2:7, 2:7] = 1
>> square2
array([[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 1, 1, 1, 0, 0],
[0, 0, 1, 1, 1, 1, 1, 0, 0],
[0, 0, 1, 1, 1, 1, 1, 0, 0],
[0, 0, 1, 1, 1, 1, 1, 0, 0],
[0, 0, 1, 1, 1, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=int32)
>> keypoints2 = corner_peaks(corner_harris(square2), min_distance=1)
>> keypoints2
array([[2, 2],
[2, 6],
[6, 2],
[6, 6]])
>> descriptors2, keypoints2 = brief(square2, keypoints2, patch_size=5)
>> keypoints2
array([[2, 2],
[2, 6],
[6, 2],
[6, 6]])
>> pairwise_hamming_distance(descriptors1, descriptors2)
array([[ 0.03125 , 0.3203125, 0.3671875, 0.6171875],
[ 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)
array([[[ 2, 2],
[ 2, 2]],
[[ 2, 5],
[ 2, 6]],
[[ 5, 2],
[ 6, 2]],
[[ 5, 5],
[ 6, 6]]])
"""
np.random.seed(sample_seed)
image = np.squeeze(image)
if image.ndim != 2:
raise ValueError("Only 2-D gray-scale images supported.")
image = img_as_float(image)
# Gaussian Low pass filtering to alleviate noise
# sensitivity
image = gaussian_filter(image, variance)
image = np.ascontiguousarray(image)
keypoints = np.array(keypoints + 0.5, dtype=np.intp, order='C')
# Removing keypoints that are within (patch_size / 2) distance from the
# image border
keypoints = keypoints[_mask_border_keypoints(image, keypoints, patch_size // 2)]
keypoints = np.ascontiguousarray(keypoints)
descriptors = np.zeros((keypoints.shape[0], descriptor_size), dtype=bool,
order='C')
# Sampling pairs of decision pixels in patch_size x patch_size window
if mode == 'normal':
samples = (patch_size / 5.0) * np.random.randn(descriptor_size * 8)
samples = np.array(samples, dtype=np.int32)
samples = samples[(samples < (patch_size // 2))
& (samples > - (patch_size - 2) // 2)]
pos1 = samples[:descriptor_size * 2]
pos1 = pos1.reshape(descriptor_size, 2)
pos2 = samples[descriptor_size * 2:descriptor_size * 4]
pos2 = pos2.reshape(descriptor_size, 2)
else:
samples = np.random.randint(-(patch_size - 2) // 2,
(patch_size // 2) + 1,
(descriptor_size * 2, 2))
pos1, pos2 = np.split(samples, 2)
pos1 = np.ascontiguousarray(pos1)
pos2 = np.ascontiguousarray(pos2)
_brief_loop(image, descriptors.view(np.uint8), keypoints, pos1, pos2)
return descriptors, keypoints
def match_keypoints_brief(keypoints1, descriptors1, keypoints2,
descriptors2, threshold=0.15):
"""**Experimental function**.
Match keypoints described using BRIEF descriptors in one image to
those in second image.
Parameters
----------
keypoints1 : (M, 2) ndarray
M Keypoints from the first image described using skimage.feature.brief
descriptors1 : (M, P) ndarray
BRIEF descriptors of size P about M keypoints in the first image.
keypoints2 : (N, 2) ndarray
N Keypoints from the second image described using skimage.feature.brief
descriptors2 : (N, P) ndarray
BRIEF descriptors of size P about N keypoints in the second image.
threshold : float in range [0, 1]
Maximum allowable hamming distance between descriptors of two keypoints
in separate images to be regarded as a match. Default is 0.15.
Returns
-------
match_keypoints_brief : (Q, 2, 2) ndarray
Location of Q matched keypoint pairs from two images.
"""
if (keypoints1.shape[0] != descriptors1.shape[0]
or keypoints2.shape[0] != descriptors2.shape[0]):
raise ValueError("The number of keypoints and number of described "
"keypoints do not match. Make the optional parameter "
"return_keypoints True to get described keypoints.")
if descriptors1.shape[1] != descriptors2.shape[1]:
raise ValueError("Descriptor sizes for matching keypoints in both "
"the images should be equal.")
# Get hamming distances between keeypoints1 and keypoints2
distance = pairwise_hamming_distance(descriptors1, descriptors2)
temp = distance > threshold
row_check = np.any(~temp, axis=1)
matched_keypoints2 = keypoints2[np.argmin(distance, axis=1)]
matched_keypoint_pairs = np.zeros((np.sum(row_check), 2, 2), dtype=np.intp)
matched_keypoint_pairs[:, 0, :] = keypoints1[row_check]
matched_keypoint_pairs[:, 1, :] = matched_keypoints2[row_check]
return matched_keypoint_pairs