From b8958ccee08403abf48da76f8cca9831eaccc6da Mon Sep 17 00:00:00 2001 From: Ankit Agrawal Date: Wed, 21 Aug 2013 01:44:04 +0530 Subject: [PATCH] Transferring all the FAST code to corner.py and corner_cy.pyx --- bento.info | 3 -- skimage/feature/__init__.py | 3 +- skimage/feature/corner.py | 49 +++++++++++++++++++ skimage/feature/corner_cy.pyx | 90 +++++++++++++++++++++++++++++++++++ skimage/feature/setup.py | 3 -- 5 files changed, 140 insertions(+), 8 deletions(-) diff --git a/bento.info b/bento.info index ae547267..30367057 100644 --- a/bento.info +++ b/bento.info @@ -102,9 +102,6 @@ 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 4a98fe9a..c16edb87 100644 --- a/skimage/feature/__init__.py +++ b/skimage/feature/__init__.py @@ -4,13 +4,12 @@ from .texture import greycomatrix, greycoprops, local_binary_pattern from .peak import peak_local_max from .corner import (corner_kitchen_rosenfeld, corner_harris, corner_shi_tomasi, corner_foerstner, corner_subpix, - corner_peaks) + corner_peaks, corner_fast) 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', diff --git a/skimage/feature/corner.py b/skimage/feature/corner.py index a025a7e4..bbb7fde5 100644 --- a/skimage/feature/corner.py +++ b/skimage/feature/corner.py @@ -1,10 +1,13 @@ import numpy as np from scipy import ndimage from scipy import stats +from scipy.ndimage.filters import maximum_filter from skimage.color import rgb2grey from skimage.util import img_as_float, pad from skimage.feature import peak_local_max +from corner_cy import _corner_fast + def _compute_derivatives(image): """Compute derivatives in x and y direction using the Sobel operator. @@ -542,3 +545,49 @@ def corner_peaks(image, min_distance=10, threshold_abs=0, threshold_rel=0.1, return np.transpose(peaks.nonzero()) else: return peaks + + +def corner_fast(image, n=12, threshold=0.15): + + """Extract FAST corners for a given image. + + Parameters + ---------- + image : 2D ndarray + Input image. + n : int + Number of consecutive pixels out of 16 pixels on the circle that + should be brighter or darker with respect to test pixel above the + `threshold` so as to classify the test pixel as a FAST corner. Also + stands for the n in `FAST-n` corner detector. + threshold : float + Threshold used in deciding whether the pixels on the circle are + brighter, darker or similar w.r.t. the test pixel. Decrease the + threshold when more corners are desired and vice-versa. + + Returns + ------- + corners : (N, 2) ndarray + Location i.e. (row, col) of extracted FAST corners. + + References + ---------- + .. [1] Edward Rosten and Tom Drummond + "Machine Learning for high-speed corner detection", + http://www.edwardrosten.com/work/rosten_2006_machine.pdf + + """ + image = np.squeeze(image) + if image.ndim != 2: + raise ValueError("Only 2-D gray-scale images supported.") + + image = np.ascontiguousarray(image, dtype=np.double) + corner_response = _corner_fast(image, n, threshold) + + # Non-maximal Suppression + corner_zero_mask = corner_response != 0 + maximas = (maximum_filter(corner_response, (3, 3)) == corner_response) & corner_zero_mask + x, y = np.where(maximas == True) + + corners = np.squeeze(np.dstack((x, y))) + return corners diff --git a/skimage/feature/corner_cy.pyx b/skimage/feature/corner_cy.pyx index c459ee92..56112211 100644 --- a/skimage/feature/corner_cy.pyx +++ b/skimage/feature/corner_cy.pyx @@ -80,3 +80,93 @@ def corner_moravec(image, Py_ssize_t window_size=1): out[r, c] = min_msum return np.asarray(out) + + +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 = np.zeros(16, dtype=np.uint8) + cdef int consecutive_count, speed_sum_b, speed_sum_d + cdef int sp + cdef double sum_b, sum_d, current_pixel + cdef double[:, ::1] corner_response = 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): + + current_pixel = image[i, j] + speed_sum_b = 0 + speed_sum_d = 0 + sum_b = 0 + sum_d = 0 + + for k in range(16): + circle_intensity = image[i + rp[k], j + cp[k]] + if circle_intensity > current_pixel + threshold: + # Brighter pixel + bins[k] = 'b' + elif circle_intensity < current_pixel - threshold: + # Darker pixel + bins[k] = 'd' + else: + # Similar pixel + bins[k] = 's' + + # High speed test for n>=12 + if n >= 12: + for k in range(0, 16, 4): + if bins[k] == 'b': + speed_sum_b += 1 + elif bins[k] == 'd': + speed_sum_d += 1 + if speed_sum_d < 3 and speed_sum_b < 3: + continue + + 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]] - current_pixel - threshold + elif bins[m] == 'd': + sum_d += current_pixel - image[i + rp[m], j + cp[m]] - threshold + # Finding the response of the corner + if sum_d > sum_b: + corner_response[i, j] = sum_d + else: + corner_response[i, j] = sum_b + break + else: + consecutive_count = 0 + + if corner_response[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]] - current_pixel - threshold + elif bins[m] == 'd': + sum_d += current_pixel - image[i + rp[m], j + cp[m]] - threshold + # Finding the response of the corner + if sum_d > sum_b: + corner_response[i, j] = sum_d + else: + corner_response[i, j] = sum_b + break + else: + consecutive_count = 0 + + return np.asarray(corner_response) diff --git a/skimage/feature/setup.py b/skimage/feature/setup.py index 039d234f..7df64c32 100644 --- a/skimage/feature/setup.py +++ b/skimage/feature/setup.py @@ -14,7 +14,6 @@ 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) @@ -23,8 +22,6 @@ 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'],