scikit-image
295 строк · 10.1 Кб
1#cython: cdivision=True
2#cython: boundscheck=False
3#cython: nonecheck=False
4#cython: wraparound=False
5import numpy as np
6cimport numpy as cnp
7from libc.float cimport DBL_MAX
8from libc.math cimport atan2, fabs
9
10from .._shared.fused_numerics cimport np_floats
11
12cnp.import_array()
13
14
15def _corner_moravec(np_floats[:, ::1] cimage, Py_ssize_t window_size=1):
16"""Compute Moravec corner measure response image.
17
18This is one of the simplest corner detectors and is comparatively fast but
19has several limitations (e.g. not rotation invariant).
20
21Parameters
22----------
23image : ndarray
24Input image.
25window_size : int, optional (default 1)
26Window size.
27
28Returns
29-------
30response : ndarray
31Moravec response image.
32
33References
34----------
35.. [1] http://kiwi.cs.dal.ca/~dparks/CornerDetection/moravec.htm
36.. [2] https://en.wikipedia.org/wiki/Corner_detection
37
38Examples
39--------
40>>> from skimage.feature import corner_moravec
41>>> square = np.zeros([7, 7])
42>>> square[3, 3] = 1
43>>> square.astype(int)
44array([[0, 0, 0, 0, 0, 0, 0],
45[0, 0, 0, 0, 0, 0, 0],
46[0, 0, 0, 0, 0, 0, 0],
47[0, 0, 0, 1, 0, 0, 0],
48[0, 0, 0, 0, 0, 0, 0],
49[0, 0, 0, 0, 0, 0, 0],
50[0, 0, 0, 0, 0, 0, 0]])
51>>> corner_moravec(square).astype(int)
52array([[0, 0, 0, 0, 0, 0, 0],
53[0, 0, 0, 0, 0, 0, 0],
54[0, 0, 1, 1, 1, 0, 0],
55[0, 0, 1, 2, 1, 0, 0],
56[0, 0, 1, 1, 1, 0, 0],
57[0, 0, 0, 0, 0, 0, 0],
58[0, 0, 0, 0, 0, 0, 0]])
59"""
60
61cdef Py_ssize_t rows = cimage.shape[0]
62cdef Py_ssize_t cols = cimage.shape[1]
63
64if np_floats is cnp.float32_t:
65dtype = np.float32
66else:
67dtype = np.float64
68
69cdef np_floats[:, ::1] out = np.zeros((rows, cols), dtype=dtype)
70
71cdef np_floats msum, min_msum, t
72cdef Py_ssize_t r, c, br, bc, mr, mc
73
74with nogil:
75for r in range(2 * window_size, rows - 2 * window_size):
76for c in range(2 * window_size, cols - 2 * window_size):
77min_msum = DBL_MAX
78for br in range(r - window_size, r + window_size + 1):
79for bc in range(c - window_size, c + window_size + 1):
80if br != r and bc != c:
81msum = 0
82for mr in range(- window_size, window_size + 1):
83for mc in range(- window_size, window_size + 1):
84t = cimage[r + mr, c + mc] - cimage[br + mr, bc + mc]
85msum += t * t
86min_msum = min(msum, min_msum)
87
88out[r, c] = min_msum
89
90return np.asarray(out)
91
92
93cdef inline np_floats _corner_fast_response(np_floats curr_pixel,
94np_floats* circle_intensities,
95signed char* bins, signed char
96state, char n) noexcept nogil:
97cdef char consecutive_count = 0
98cdef np_floats curr_response
99cdef Py_ssize_t l, m
100for l in range(15 + n):
101if bins[l % 16] == state:
102consecutive_count += 1
103if consecutive_count == n:
104curr_response = 0
105for m in range(16):
106curr_response += fabs(circle_intensities[m] - curr_pixel)
107return curr_response
108else:
109consecutive_count = 0
110return 0
111
112
113def _corner_fast(np_floats[:, ::1] image, signed char n, np_floats threshold):
114
115if np_floats is cnp.float32_t:
116dtype = np.float32
117else:
118dtype = np.float64
119
120cdef Py_ssize_t rows = image.shape[0]
121cdef Py_ssize_t cols = image.shape[1]
122
123cdef Py_ssize_t i, j, k
124
125cdef signed char speed_sum_b, speed_sum_d
126cdef np_floats curr_pixel, ring_pixel
127cdef np_floats lower_threshold, upper_threshold
128cdef np_floats[:, ::1] corner_response = np.zeros((rows, cols),
129dtype=dtype)
130
131cdef signed char *rp = [0, 1, 2, 3, 3, 3, 2, 1, 0, -1, -2, -3, -3,
132-3, -2, -1]
133cdef signed char *cp = [3, 3, 2, 1, 0, -1, -2, -3, -3, -3, -2, -1,
1340, 1, 2, 3]
135cdef signed char bins[16]
136cdef np_floats circle_intensities[16]
137
138cdef cnp.float64_t curr_response
139
140with nogil:
141for i in range(3, rows - 3):
142for j in range(3, cols - 3):
143
144curr_pixel = image[i, j]
145lower_threshold = curr_pixel - threshold
146upper_threshold = curr_pixel + threshold
147
148# High speed test for n >= 12
149if n >= 12:
150speed_sum_b = 0
151speed_sum_d = 0
152for k in range(0, 16, 4):
153ring_pixel = image[i + rp[k], j + cp[k]]
154if ring_pixel > upper_threshold:
155speed_sum_b += 1
156elif ring_pixel < lower_threshold:
157speed_sum_d += 1
158if speed_sum_d < 3 and speed_sum_b < 3:
159continue
160
161for k in range(16):
162circle_intensities[k] = image[i + rp[k], j + cp[k]]
163if circle_intensities[k] > upper_threshold:
164# Brighter pixel
165bins[k] = b'b'
166elif circle_intensities[k] < lower_threshold:
167# Darker pixel
168bins[k] = b'd'
169else:
170# Similar pixel
171bins[k] = b's'
172
173# Test for bright pixels
174curr_response = _corner_fast_response[np_floats](curr_pixel,
175circle_intensities, bins,
176b'b', n)
177
178# Test for dark pixels
179if curr_response == 0:
180curr_response = _corner_fast_response[np_floats](curr_pixel,
181circle_intensities,
182bins, b'd', n)
183
184corner_response[i, j] = curr_response
185
186return np.asarray(corner_response)
187
188
189def _corner_orientations(np_floats[:, ::1] image, Py_ssize_t[:, :] corners,
190mask):
191"""Compute the orientation of corners.
192
193The orientation of corners is computed using the first order central moment
194i.e. the center of mass approach. The corner orientation is the angle of
195the vector from the corner coordinate to the intensity centroid in the
196local neighborhood around the corner calculated using first order central
197moment.
198
199Parameters
200----------
201image : 2D array
202Input grayscale image.
203corners : (N, 2) array
204Corner coordinates as ``(row, col)``.
205mask : 2D array
206Mask defining the local neighborhood of the corner used for the
207calculation of the central moment.
208
209Returns
210-------
211orientations : (N, 1) array
212Orientations of corners in the range [-pi, pi].
213
214References
215----------
216.. [1] Ethan Rublee, Vincent Rabaud, Kurt Konolige and Gary Bradski
217"ORB : An efficient alternative to SIFT and SURF"
218http://www.vision.cs.chubu.ac.jp/CV-R/pdf/Rublee_iccv2011.pdf
219.. [2] Paul L. Rosin, "Measuring Corner Properties"
220http://users.cs.cf.ac.uk/Paul.Rosin/corner2.pdf
221
222Examples
223--------
224>>> from skimage.morphology import octagon
225>>> from skimage.feature import (corner_fast, corner_peaks,
226... corner_orientations)
227>>> square = np.zeros((12, 12))
228>>> square[3:9, 3:9] = 1
229>>> square.astype(int)
230array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
231[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
232[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
233[0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0],
234[0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0],
235[0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0],
236[0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0],
237[0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0],
238[0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0],
239[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
240[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
241[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
242>>> corners = corner_peaks(corner_fast(square, 9), min_distance=1)
243>>> corners
244array([[3, 3],
245[3, 8],
246[8, 3],
247[8, 8]])
248>>> orientations = corner_orientations(square, corners, octagon(3, 2))
249>>> np.rad2deg(orientations)
250array([ 45., 135., -45., -135.])
251
252"""
253
254if np_floats is cnp.float32_t:
255dtype = np.float32
256else:
257dtype = np.float64
258
259if mask.shape[0] % 2 != 1 or mask.shape[1] % 2 != 1:
260raise ValueError("Size of mask must be uneven.")
261
262cdef unsigned char[:, ::1] cmask = np.ascontiguousarray(mask != 0,
263dtype=np.uint8)
264
265cdef Py_ssize_t i, r, c, r0, c0
266cdef Py_ssize_t mrows = mask.shape[0]
267cdef Py_ssize_t mcols = mask.shape[1]
268cdef Py_ssize_t mrows2 = (mrows - 1) / 2
269cdef Py_ssize_t mcols2 = (mcols - 1) / 2
270cdef np_floats[:, :] cimage = np.pad(image, (mrows2, mcols2),
271mode='constant',
272constant_values=0)
273cdef np_floats[:] orientations = np.zeros(corners.shape[0], dtype=dtype)
274cdef np_floats curr_pixel, m01, m10, m01_tmp
275
276with nogil:
277for i in range(corners.shape[0]):
278r0 = corners[i, 0]
279c0 = corners[i, 1]
280
281m01 = 0
282m10 = 0
283
284for r in range(mrows):
285m01_tmp = 0
286for c in range(mcols):
287if cmask[r, c]:
288curr_pixel = cimage[r0 + r, c0 + c]
289m10 += curr_pixel * (c - mcols2)
290m01_tmp += curr_pixel
291m01 += m01_tmp * (r - mrows2)
292
293orientations[i] = atan2(m01, m10)
294
295return np.asarray(orientations)
296