diff --git a/doc/examples/plot_censure_keypoints.py b/doc/examples/plot_censure_keypoints.py index 7f08d409..95fa0e58 100644 --- a/doc/examples/plot_censure_keypoints.py +++ b/doc/examples/plot_censure_keypoints.py @@ -1,12 +1,14 @@ """ -=========================== -Censure Keypoints Detection -=========================== +========================= +CenSurE Feature Detection +========================= -In this example, we detect and plot the Censure Keypoints at various scales -using Difference of Boxes, Octagon and Star shaped bi-level filters. +In this example we detect and plot the CenSurE (Center Surround Extrema) +features at various scales using Difference of Boxes, Octagon and Star shaped +bi-level filters. """ + from skimage.feature import keypoints_censure from skimage.data import lena from skimage.color import rgb2gray @@ -15,32 +17,39 @@ import matplotlib.pyplot as plt # Initializing the parameters for Censure keypoints img = lena() gray_img = rgb2gray(img) -min_scale = 1 -max_scale = 7 -nms_threshold = 0.15 -rpc_threshold = 10 +min_scale = 2 +max_scale = 6 +non_max_threshold = 0.15 +line_threshold = 10 + + +f, ax = plt.subplots(nrows=(max_scale - min_scale - 1), ncols=3, + figsize=(6, 6)) +plt.subplots_adjust(wspace=0.02, hspace=0.02, top=0.94, + bottom=0.02, left=0.06, right=0.98) # Detecting Censure keypoints for the following filters -for mode in ['dob', 'octagon', 'star']: +for col, mode in enumerate(['dob', 'octagon', 'star']): - kp_censure, scale = keypoints_censure(gray_img, min_scale, max_scale, - mode, nms_threshold, rpc_threshold) - f, axarr = plt.subplots((max_scale - min_scale + 1) // 3, 3) + ax[0, col].set_title(mode.upper(), fontsize=12) + + keypoints, scales = keypoints_censure(gray_img, min_scale, max_scale, + mode, non_max_threshold, + line_threshold) # Plotting Censure features at all the scales - for i in range(max_scale - min_scale - 1): - keypoints = kp_censure[scale == i + min_scale + 1] - num = len(keypoints) - x = keypoints[:, 1] - y = keypoints[:, 0] - s = 5 * 2**(i + min_scale + 1) - axarr[i // 3, i - (i // 3) * 3].imshow(img) - axarr[i // 3, i - (i // 3) * 3].scatter(x, y, s, facecolors='none', - edgecolors='g') - axarr[i // 3, i - (i // 3) * 3].set_title(' %s %s-Censure features at ' - 'scale %d' % (num, mode, i + - min_scale + 1)) + for row, scale in enumerate(range(min_scale + 1, max_scale)): + keypoints_i = keypoints[scales == scale] + num = len(keypoints_i) + x = keypoints_i[:, 1] + y = keypoints_i[:, 0] + s = 0.5 * 2 ** (scale + min_scale + 1) + ax[row, col].imshow(img) + ax[row, col].scatter(x, y, s, facecolors='none', edgecolors='b') + ax[row, col].set_xticks([]) + ax[row, col].set_yticks([]) + ax[row, col].axis((0, img.shape[1], img.shape[0], 0)) + if col == 0: + ax[row, col].set_ylabel('Scale %d' % scale, fontsize=12) - plt.suptitle('NMS threshold = %f, RPC threshold = %d' - % (nms_threshold, rpc_threshold)) plt.show() diff --git a/skimage/feature/censure.py b/skimage/feature/censure.py index d90b36cf..2dda9306 100644 --- a/skimage/feature/censure.py +++ b/skimage/feature/censure.py @@ -19,18 +19,17 @@ STAR_FILTER_SHAPE = [(1, 0), (3, 1), (4, 2), (5, 3), (7, 4), (8, 5), (9, 6), (11, 8), (13, 10), (14, 11), (15, 12), (16, 14)] -def _get_filtered_image(image, min_scale, max_scale, mode): +def _filter_image(image, min_scale, max_scale, mode): - scales = np.zeros((image.shape[0], image.shape[1], - max_scale - min_scale + 1), dtype=np.double) + response = np.zeros((image.shape[0], image.shape[1], + max_scale - min_scale + 1), dtype=np.double) if mode == 'dob': - # make scales[:, :, i] contiguous memory block - item_size = scales.itemsize - scales.strides = (item_size * scales.shape[0], - item_size, - item_size * scales.shape[0] * scales.shape[1]) + # make response[:, :, i] contiguous memory block + item_size = response.itemsize + response.strides = (item_size * response.shape[0], item_size, + item_size * response.shape[0] * response.shape[1]) integral_img = integral_image(image) @@ -38,12 +37,12 @@ def _get_filtered_image(image, min_scale, max_scale, mode): n = min_scale + i # Constant multipliers for the outer region and the inner region - # of the bilevel filters with the constraint of keeping the + # of the bi-level filters with the constraint of keeping the # DC bias 0. inner_weight = (1.0 / (2 * n + 1)**2) outer_weight = (1.0 / (12 * n**2 + 4 * n)) - _censure_dob_loop(n, integral_img, scales[:, :, i], + _censure_dob_loop(n, integral_img, response[:, :, i], inner_weight, outer_weight) # NOTE : For the Octagon shaped filter, we implemented and evaluated the @@ -57,17 +56,17 @@ def _get_filtered_image(image, min_scale, max_scale, mode): for i in range(max_scale - min_scale + 1): mo, no = OCTAGON_OUTER_SHAPE[min_scale + i - 1] mi, ni = OCTAGON_INNER_SHAPE[min_scale + i - 1] - scales[:, :, i] = convolve(image, - _octagon_filter_kernel(mo, no, mi, ni)) + response[:, :, i] = convolve(image, + _octagon_filter_kernel(mo, no, mi, ni)) + elif mode == 'star': for i in range(max_scale - min_scale + 1): m = STAR_SHAPE[STAR_FILTER_SHAPE[min_scale + i - 1][0]] n = STAR_SHAPE[STAR_FILTER_SHAPE[min_scale + i - 1][1]] - scales[:, :, i] = convolve(image, - _star_filter_kernel(m, n)) + response[:, :, i] = convolve(image, _star_filter_kernel(m, n)) - return scales + return response # TODO : Import from selem after getting #669 merged. @@ -138,24 +137,24 @@ def _suppress_lines(feature_mask, image, sigma, line_threshold): def keypoints_censure(image, min_scale=1, max_scale=7, mode='DoB', non_max_threshold=0.15, line_threshold=10): """ - Extracts Censure keypoints along with the corresponding scale using - either Difference of Boxes, Octagon or STAR bilevel filter. + Extracts CenSurE keypoints along with the corresponding scale using + either Difference of Boxes, Octagon or STAR bi-level filter. Parameters ---------- image : 2D ndarray Input image. - min_scale : positive integer + min_scale : int Minimum scale to extract keypoints from. - max_scale : positive integer + max_scale : int Maximum scale to extract keypoints from. The keypoints will be extracted from all the scales except the first and the last i.e. from the scales in the range [min_scale + 1, max_scale - 1]. - mode : ('DoB', 'Octagon', 'STAR') - Type of bilevel filter used to get the scales of the input image. + mode : {'DoB', 'Octagon', 'STAR'} + Type of bi-level filter used to get the scales of the input image. Possible values are 'DoB', 'Octagon' and 'STAR'. The three modes - represent the shape of the bilevel filters i.e. box(square), octagon - and star respectively. For instance, a bilevel octagon filter consists + represent the shape of the bi-level filters i.e. box(square), octagon + and star respectively. For instance, a bi-level octagon filter consists of a smaller inner octagon and a larger outer octagon with the filter weights being uniformly negative in both the inner octagon while uniformly positive in the difference region. Use STAR and Octagon for @@ -170,7 +169,7 @@ def keypoints_censure(image, min_scale=1, max_scale=7, mode='DoB', Returns ------- keypoints : (N, 2) array - Location of the extracted keypoints in the (row, col) format. + Location of the extracted keypoints in the ``(row, col)`` format. scales : (N, 1) array The corresponding scale of the N extracted keypoints. @@ -187,8 +186,9 @@ def keypoints_censure(image, min_scale=1, max_scale=7, mode='DoB', http://www.jamris.org/01_2013/saveas.php?QUEST=JAMRIS_No01_2013_P_11-20.pdf """ + # (1) First we generate the required scales on the input grayscale image - # using a bilevel filter and stack them up in `filter_response`. + # using a bi-level filter and stack them up in `filter_response`. # (2) We then perform Non-Maximal suppression in 3 x 3 x 3 window on the # filter_response to suppress points that are neither minima or maxima in # 3 x 3 x 3 neighbourhood. We obtain a boolean ndarray `feature_mask` @@ -207,15 +207,15 @@ def keypoints_censure(image, min_scale=1, max_scale=7, mode='DoB', if mode not in ('dob', 'octagon', 'star'): raise ValueError('Mode must be one of "DoB", "Octagon", "STAR".') - if max_scale - min_scale < 2: - raise ValueError('The number of scales should be greater than or' - 'equal to 3.') + if min_scale < 1 or max_scale < 1 or max_scale - min_scale < 2: + raise ValueError('The scales must be >= 1 and the number of scales ' + 'should be >= 3.') image = img_as_float(image) image = np.ascontiguousarray(image) # Generating all the scales - filter_response = _get_filtered_image(image, min_scale, max_scale, mode) + filter_response = _filter_image(image, min_scale, max_scale, mode) # Suppressing points that are neither minima or maxima in their 3 x 3 x 3 # neighbourhood to zero