diff --git a/bento.info b/bento.info index 30367057..ae547267 100644 --- a/bento.info +++ b/bento.info @@ -102,6 +102,9 @@ Library: Extension: skimage.feature.corner_cy Sources: skimage/feature/corner_cy.pyx + Extension: skimage.feature.fast_cy + Sources: + skimage/feature/fast_cy.pyx Extension: skimage.feature._texture Sources: skimage/feature/_texture.pyx diff --git a/skimage/feature/__init__.py b/skimage/feature/__init__.py index 4a6518d6..4a98fe9a 100644 --- a/skimage/feature/__init__.py +++ b/skimage/feature/__init__.py @@ -7,7 +7,10 @@ from .corner import (corner_kitchen_rosenfeld, corner_harris, corner_peaks) from .corner_cy import corner_moravec from .template import match_template - +from ._brief import brief, match_keypoints_brief +from .util import pairwise_hamming_distance +from .censure import keypoints_censure +from .fast import corner_fast __all__ = ['daisy', 'hog', @@ -22,4 +25,9 @@ __all__ = ['daisy', 'corner_subpix', 'corner_peaks', 'corner_moravec', - 'match_template'] + 'match_template', + 'brief', + 'pairwise_hamming_distance', + 'match_keypoints_brief', + 'keypoints_censure', + 'corner_fast'] diff --git a/skimage/feature/fast.py b/skimage/feature/fast.py index 6b6ae264..8194093e 100644 --- a/skimage/feature/fast.py +++ b/skimage/feature/fast.py @@ -1,35 +1,22 @@ import numpy as np -from ..util import img_as_float +from scipy.ndimage.filters import maximum_filter + +from fast_cy import _corner_fast -def corner_fast(image, n=9, threshold=0.15): +def corner_fast(image, n=12, threshold=0.15): image = np.squeeze(image) if image.ndim != 2: raise ValueError("Only 2-D gray-scale images supported.") - image = img_as_float(image) - corner_mask = np.zeros(image.shape, dtype=bool) + image = np.ascontiguousarray(image, dtype=np.double) + corner = _corner_fast(image, n, threshold) - test_pixels = np.asarray([[-3, 0], [-3, 1], [-2, 2], [-1, 3], [0, 3], - [1, 3], [2, 2], [3, 1], [3, 0], [3, -1], - [2, -2], [1, -3], [0, -3], [-1, -3], - [-2, -2], [-1, -3]]) + corner_zero_mask = corner != 0 + c, d = np.nonzero(corner) - # TODO : Outsource to Cython - for i in range(3, image.shape[0] - 3): - for j in range(3, image.shape[1] - 3): - test_x = i + test_pixels[:, 0] - test_y = j + test_pixels[:, 1] - intensities = image[test_x, test_y] - low = intensities < image[i, j] - threshold - high = intensities > image[i, j] + threshold - low = np.concatenate(low, low) - high = np.concatenate(high, high) - # How to check if a sequence n * [True] exists in low/high ? - # if n * [True] in low or n * True in high: - # corner_mask[i, j] = True - - corner_x, corner_y = np.where(corner_mask == True) - corners = np.dstack(corner_x, corner_y) - return corners + maximas = (maximum_filter(corner, (3, 3)) == corner) & corner_zero_mask + x, y = np.where(maximas == True) + + return np.squeeze(np.dstack((x, y))) diff --git a/skimage/feature/fast_cy.pyx b/skimage/feature/fast_cy.pyx new file mode 100644 index 00000000..a7af63c2 --- /dev/null +++ b/skimage/feature/fast_cy.pyx @@ -0,0 +1,82 @@ +#cython: cdivision=True +#cython: boundscheck=False +#cython: nonecheck=False +#cython: wraparound=False + +import numpy as np + +from ..util import img_as_float + + +def _corner_fast(double[:, ::1] image, int n, double threshold): + + cdef int[:] rp = (np.round(3 * np.sin(2 * np.pi * np.arange(16, dtype=np.double) / 16))).astype(np.int32) + cdef int[:] cp = (np.round(3 * np.cos(2 * np.pi * np.arange(16, dtype=np.double) / 16))).astype(np.int32) + + cdef Py_ssize_t rows = image.shape[0] + cdef Py_ssize_t cols = image.shape[1] + + cdef Py_ssize_t i, j, k, l, m + + cdef char[:] bins + cdef int consecutive_count = 0 + cdef double sum_b + cdef double sum_d + cdef double[:, ::1] corner = np.zeros((rows, cols), dtype=np.double) + + cdef double circle_intensity + + for i in range(3, rows - 3): + for j in range(3, cols - 3): + + bins = np.zeros(16, dtype='S1') + sum_b = 0 + sum_d = 0 + + for k in range(16): + circle_intensity = image[i + rp[k], j + cp[k]] + if circle_intensity > image[i, j] + threshold: + bins[k] = 'b' + elif circle_intensity < image[i, j] - threshold: + bins[k] = 'd' + else: + bins[k] = 's' + + consecutive_count = 0 + for l in range(15 + n): + if bins[l % 16] == 'b': + consecutive_count += 1 + if consecutive_count == n: + for m in range(16): + if bins[m] == 'b': + sum_b += image[i + rp[m], j + cp[m]] - image[i, j] - threshold + elif bins[m] == 'd': + sum_d += image[i, j] - image[i + rp[m], j + cp[m]] - threshold + if sum_d > sum_b: + corner[i, j] = sum_d + else: + corner[i, j] = sum_b + break + else: + consecutive_count = 0 + + if corner[i, j] == 0: + consecutive_count = 0 + for l in range(15 + n): + if bins[l % 16] == 'd': + consecutive_count += 1 + if consecutive_count == n: + for m in range(16): + if bins[m] == 'b': + sum_b += image[i + rp[m], j + cp[m]] - image[i, j] - threshold + elif bins[m] == 'd': + sum_d += image[i, j] - image[i + rp[m], j + cp[m]] - threshold + if sum_d > sum_b: + corner[i, j] = sum_d + else: + corner[i, j] = sum_b + break + else: + consecutive_count = 0 + + return np.asarray(corner) diff --git a/skimage/feature/setup.py b/skimage/feature/setup.py index 7df64c32..039d234f 100644 --- a/skimage/feature/setup.py +++ b/skimage/feature/setup.py @@ -14,6 +14,7 @@ def configuration(parent_package='', top_path=None): cython(['corner_cy.pyx'], working_path=base_path) cython(['censure_cy.pyx'], working_path=base_path) + cython(['fast_cy.pyx'], working_path=base_path) cython(['_brief_cy.pyx'], working_path=base_path) cython(['_texture.pyx'], working_path=base_path) cython(['_template.pyx'], working_path=base_path) @@ -22,6 +23,8 @@ def configuration(parent_package='', top_path=None): include_dirs=[get_numpy_include_dirs()]) config.add_extension('censure_cy', sources=['censure_cy.c'], include_dirs=[get_numpy_include_dirs()]) + config.add_extension('fast_cy', sources=['fast_cy.c'], + include_dirs=[get_numpy_include_dirs()]) config.add_extension('_brief_cy', sources=['_brief_cy.c'], include_dirs=[get_numpy_include_dirs()]) config.add_extension('_texture', sources=['_texture.c'],