| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355 |
- import functools
- import math
- from itertools import combinations_with_replacement
- import numpy as np
- from scipy import ndimage as ndi
- from scipy import spatial, stats
- from .._shared.filters import gaussian
- from .._shared.utils import _supported_float_type, safe_as_int, warn
- from ..transform import integral_image
- from ..util import img_as_float
- from ._hessian_det_appx import _hessian_matrix_det
- from .corner_cy import _corner_fast, _corner_moravec, _corner_orientations
- from .peak import peak_local_max
- from .util import _prepare_grayscale_input_2D, _prepare_grayscale_input_nD
- def _compute_derivatives(image, mode='constant', cval=0):
- """Compute derivatives in axis directions using the Sobel operator.
- Parameters
- ----------
- image : ndarray
- Input image.
- mode : {'constant', 'reflect', 'wrap', 'nearest', 'mirror'}, optional
- How to handle values outside the image borders.
- cval : float, optional
- Used in conjunction with mode 'constant', the value outside
- the image boundaries.
- Returns
- -------
- derivatives : list of ndarray
- Derivatives in each axis direction.
- """
- derivatives = [
- ndi.sobel(image, axis=i, mode=mode, cval=cval) for i in range(image.ndim)
- ]
- return derivatives
- def structure_tensor(image, sigma=1, mode='constant', cval=0, order='rc'):
- """Compute structure tensor using sum of squared differences.
- The (2-dimensional) structure tensor A is defined as::
- A = [Arr Arc]
- [Arc Acc]
- which is approximated by the weighted sum of squared differences in a local
- window around each pixel in the image. This formula can be extended to a
- larger number of dimensions (see [1]_).
- Parameters
- ----------
- image : ndarray
- Input image.
- sigma : float or array-like of float, optional
- Standard deviation used for the Gaussian kernel, which is used as a
- weighting function for the local summation of squared differences.
- If sigma is an iterable, its length must be equal to `image.ndim` and
- each element is used for the Gaussian kernel applied along its
- respective axis.
- mode : {'constant', 'reflect', 'wrap', 'nearest', 'mirror'}, optional
- How to handle values outside the image borders.
- cval : float, optional
- Used in conjunction with mode 'constant', the value outside
- the image boundaries.
- order : {'rc', 'xy'}, optional
- NOTE: 'xy' is only an option for 2D images, higher dimensions must
- always use 'rc' order. This parameter allows for the use of reverse or
- forward order of the image axes in gradient computation. 'rc' indicates
- the use of the first axis initially (Arr, Arc, Acc), whilst 'xy'
- indicates the usage of the last axis initially (Axx, Axy, Ayy).
- Returns
- -------
- A_elems : list of ndarray
- Upper-diagonal elements of the structure tensor for each pixel in the
- input image.
- Examples
- --------
- >>> from skimage.feature import structure_tensor
- >>> square = np.zeros((5, 5))
- >>> square[2, 2] = 1
- >>> Arr, Arc, Acc = structure_tensor(square, sigma=0.1, order='rc')
- >>> Acc
- array([[0., 0., 0., 0., 0.],
- [0., 1., 0., 1., 0.],
- [0., 4., 0., 4., 0.],
- [0., 1., 0., 1., 0.],
- [0., 0., 0., 0., 0.]])
- See also
- --------
- structure_tensor_eigenvalues
- References
- ----------
- .. [1] https://en.wikipedia.org/wiki/Structure_tensor
- """
- if order == 'xy' and image.ndim > 2:
- raise ValueError('Only "rc" order is supported for dim > 2.')
- if order not in ['rc', 'xy']:
- raise ValueError(f'order {order} is invalid. Must be either "rc" or "xy"')
- if not np.isscalar(sigma):
- sigma = tuple(sigma)
- if len(sigma) != image.ndim:
- raise ValueError('sigma must have as many elements as image ' 'has axes')
- image = _prepare_grayscale_input_nD(image)
- derivatives = _compute_derivatives(image, mode=mode, cval=cval)
- if order == 'xy':
- derivatives = reversed(derivatives)
- # structure tensor
- A_elems = [
- gaussian(der0 * der1, sigma=sigma, mode=mode, cval=cval)
- for der0, der1 in combinations_with_replacement(derivatives, 2)
- ]
- return A_elems
- def _hessian_matrix_with_gaussian(image, sigma=1, mode='reflect', cval=0, order='rc'):
- """Compute the Hessian via convolutions with Gaussian derivatives.
- In 2D, the Hessian matrix is defined as:
- H = [Hrr Hrc]
- [Hrc Hcc]
- which is computed by convolving the image with the second derivatives
- of the Gaussian kernel in the respective r- and c-directions.
- The implementation here also supports n-dimensional data.
- Parameters
- ----------
- image : ndarray
- Input image.
- sigma : float or sequence of float, optional
- Standard deviation used for the Gaussian kernel, which sets the
- amount of smoothing in terms of pixel-distances. It is
- advised to not choose a sigma much less than 1.0, otherwise
- aliasing artifacts may occur.
- mode : {'constant', 'reflect', 'wrap', 'nearest', 'mirror'}, optional
- How to handle values outside the image borders.
- cval : float, optional
- Used in conjunction with mode 'constant', the value outside
- the image boundaries.
- order : {'rc', 'xy'}, optional
- This parameter allows for the use of reverse or forward order of
- the image axes in gradient computation. 'rc' indicates the use of
- the first axis initially (Hrr, Hrc, Hcc), whilst 'xy' indicates the
- usage of the last axis initially (Hxx, Hxy, Hyy)
- Returns
- -------
- H_elems : list of ndarray
- Upper-diagonal elements of the hessian matrix for each pixel in the
- input image. In 2D, this will be a three element list containing [Hrr,
- Hrc, Hcc]. In nD, the list will contain ``(n**2 + n) / 2`` arrays.
- """
- image = img_as_float(image)
- float_dtype = _supported_float_type(image.dtype)
- image = image.astype(float_dtype, copy=False)
- if image.ndim > 2 and order == "xy":
- raise ValueError("order='xy' is only supported for 2D images.")
- if order not in ["rc", "xy"]:
- raise ValueError(f"unrecognized order: {order}")
- if np.isscalar(sigma):
- sigma = (sigma,) * image.ndim
- # This function uses `scipy.ndimage.gaussian_filter` with the order
- # argument to compute convolutions. For example, specifying
- # ``order=[1, 0]`` would apply convolution with a first-order derivative of
- # the Gaussian along the first axis and simple Gaussian smoothing along the
- # second.
- # For small sigma, the SciPy Gaussian filter suffers from aliasing and edge
- # artifacts, given that the filter will approximate a sinc or sinc
- # derivative which only goes to 0 very slowly (order 1/n**2). Thus, we use
- # a much larger truncate value to reduce any edge artifacts.
- truncate = 8 if all(s > 1 for s in sigma) else 100
- sq1_2 = 1 / math.sqrt(2)
- sigma_scaled = tuple(sq1_2 * s for s in sigma)
- common_kwargs = dict(sigma=sigma_scaled, mode=mode, cval=cval, truncate=truncate)
- gaussian_ = functools.partial(ndi.gaussian_filter, **common_kwargs)
- # Apply two successive first order Gaussian derivative operations, as
- # detailed in:
- # https://dsp.stackexchange.com/questions/78280/are-scipy-second-order-gaussian-derivatives-correct
- # 1.) First order along one axis while smoothing (order=0) along the other
- ndim = image.ndim
- # orders in 2D = ([1, 0], [0, 1])
- # in 3D = ([1, 0, 0], [0, 1, 0], [0, 0, 1])
- # etc.
- orders = tuple([0] * d + [1] + [0] * (ndim - d - 1) for d in range(ndim))
- gradients = [gaussian_(image, order=orders[d]) for d in range(ndim)]
- # 2.) apply the derivative along another axis as well
- axes = range(ndim)
- if order == 'xy':
- axes = reversed(axes)
- H_elems = [
- gaussian_(gradients[ax0], order=orders[ax1])
- for ax0, ax1 in combinations_with_replacement(axes, 2)
- ]
- return H_elems
- def hessian_matrix(
- image, sigma=1, mode='constant', cval=0, order='rc', use_gaussian_derivatives=None
- ):
- r"""Compute the Hessian matrix.
- In 2D, the Hessian matrix is defined as::
- H = [Hrr Hrc]
- [Hrc Hcc]
- which is computed by convolving the image with the second derivatives
- of the Gaussian kernel in the respective r- and c-directions.
- The implementation here also supports n-dimensional data.
- Parameters
- ----------
- image : ndarray
- Input image.
- sigma : float
- Standard deviation used for the Gaussian kernel, which is used as
- weighting function for the auto-correlation matrix.
- mode : {'constant', 'reflect', 'wrap', 'nearest', 'mirror'}, optional
- How to handle values outside the image borders.
- cval : float, optional
- Used in conjunction with mode 'constant', the value outside
- the image boundaries.
- order : {'rc', 'xy'}, optional
- For 2D images, this parameter allows for the use of reverse or forward
- order of the image axes in gradient computation. 'rc' indicates the use
- of the first axis initially (Hrr, Hrc, Hcc), whilst 'xy' indicates the
- usage of the last axis initially (Hxx, Hxy, Hyy). Images with higher
- dimension must always use 'rc' order.
- use_gaussian_derivatives : bool, optional
- Indicates whether the Hessian is computed by convolving with Gaussian
- derivatives, or by a simple finite-difference operation.
- Returns
- -------
- H_elems : list of ndarray
- Upper-diagonal elements of the hessian matrix for each pixel in the
- input image. In 2D, this will be a three element list containing [Hrr,
- Hrc, Hcc]. In nD, the list will contain ``(n**2 + n) / 2`` arrays.
- Notes
- -----
- The distributive property of derivatives and convolutions allows us to
- restate the derivative of an image, I, smoothed with a Gaussian kernel, G,
- as the convolution of the image with the derivative of G.
- .. math::
- \frac{\partial }{\partial x_i}(I * G) =
- I * \left( \frac{\partial }{\partial x_i} G \right)
- When ``use_gaussian_derivatives`` is ``True``, this property is used to
- compute the second order derivatives that make up the Hessian matrix.
- When ``use_gaussian_derivatives`` is ``False``, simple finite differences
- on a Gaussian-smoothed image are used instead.
- Examples
- --------
- >>> from skimage.feature import hessian_matrix
- >>> square = np.zeros((5, 5))
- >>> square[2, 2] = 4
- >>> Hrr, Hrc, Hcc = hessian_matrix(square, sigma=0.1, order='rc',
- ... use_gaussian_derivatives=False)
- >>> Hrc
- array([[ 0., 0., 0., 0., 0.],
- [ 0., 1., 0., -1., 0.],
- [ 0., 0., 0., 0., 0.],
- [ 0., -1., 0., 1., 0.],
- [ 0., 0., 0., 0., 0.]])
- """
- image = img_as_float(image)
- float_dtype = _supported_float_type(image.dtype)
- image = image.astype(float_dtype, copy=False)
- if image.ndim > 2 and order == "xy":
- raise ValueError("order='xy' is only supported for 2D images.")
- if order not in ["rc", "xy"]:
- raise ValueError(f"unrecognized order: {order}")
- if use_gaussian_derivatives is None:
- use_gaussian_derivatives = False
- warn(
- "use_gaussian_derivatives currently defaults to False, but will "
- "change to True in a future version. Please specify this "
- "argument explicitly to maintain the current behavior",
- category=FutureWarning,
- stacklevel=2,
- )
- if use_gaussian_derivatives:
- return _hessian_matrix_with_gaussian(
- image, sigma=sigma, mode=mode, cval=cval, order=order
- )
- gaussian_filtered = gaussian(image, sigma=sigma, mode=mode, cval=cval)
- gradients = np.gradient(gaussian_filtered)
- axes = range(image.ndim)
- if order == 'xy':
- axes = reversed(axes)
- H_elems = [
- np.gradient(gradients[ax0], axis=ax1)
- for ax0, ax1 in combinations_with_replacement(axes, 2)
- ]
- return H_elems
- def hessian_matrix_det(image, sigma=1, approximate=True):
- """Compute the approximate Hessian Determinant over an image.
- The 2D approximate method uses box filters over integral images to
- compute the approximate Hessian Determinant.
- Parameters
- ----------
- image : ndarray
- The image over which to compute the Hessian Determinant.
- sigma : float, optional
- Standard deviation of the Gaussian kernel used for the Hessian
- matrix.
- approximate : bool, optional
- If ``True`` and the image is 2D, use a much faster approximate
- computation. This argument has no effect on 3D and higher images.
- Returns
- -------
- out : array
- The array of the Determinant of Hessians.
- References
- ----------
- .. [1] Herbert Bay, Andreas Ess, Tinne Tuytelaars, Luc Van Gool,
- "SURF: Speeded Up Robust Features"
- ftp://ftp.vision.ee.ethz.ch/publications/articles/eth_biwi_00517.pdf
- Notes
- -----
- For 2D images when ``approximate=True``, the running time of this method
- only depends on size of the image. It is independent of `sigma` as one
- would expect. The downside is that the result for `sigma` less than `3`
- is not accurate, i.e., not similar to the result obtained if someone
- computed the Hessian and took its determinant.
- """
- image = img_as_float(image)
- float_dtype = _supported_float_type(image.dtype)
- image = image.astype(float_dtype, copy=False)
- if image.ndim == 2 and approximate:
- integral = integral_image(image)
- return np.array(_hessian_matrix_det(integral, sigma))
- else: # slower brute-force implementation for nD images
- hessian_mat_array = _symmetric_image(
- hessian_matrix(image, sigma, use_gaussian_derivatives=False)
- )
- return np.linalg.det(hessian_mat_array)
- def _symmetric_compute_eigenvalues(S_elems):
- """Compute eigenvalues from the upper-diagonal entries of a symmetric
- matrix.
- Parameters
- ----------
- S_elems : list of ndarray
- The upper-diagonal elements of the matrix, as returned by
- `hessian_matrix` or `structure_tensor`.
- Returns
- -------
- eigs : ndarray
- The eigenvalues of the matrix, in decreasing order. The eigenvalues are
- the leading dimension. That is, ``eigs[i, j, k]`` contains the
- ith-largest eigenvalue at position (j, k).
- """
- if len(S_elems) == 3: # Fast explicit formulas for 2D.
- M00, M01, M11 = S_elems
- eigs = np.empty((2, *M00.shape), M00.dtype)
- eigs[:] = (M00 + M11) / 2
- hsqrtdet = np.sqrt(M01**2 + ((M00 - M11) / 2) ** 2)
- eigs[0] += hsqrtdet
- eigs[1] -= hsqrtdet
- return eigs
- else:
- matrices = _symmetric_image(S_elems)
- # eigvalsh returns eigenvalues in increasing order. We want decreasing
- eigs = np.linalg.eigvalsh(matrices)[..., ::-1]
- leading_axes = tuple(range(eigs.ndim - 1))
- return np.transpose(eigs, (eigs.ndim - 1,) + leading_axes)
- def _symmetric_image(S_elems):
- """Convert the upper-diagonal elements of a matrix to the full
- symmetric matrix.
- Parameters
- ----------
- S_elems : list of array
- The upper-diagonal elements of the matrix, as returned by
- `hessian_matrix` or `structure_tensor`.
- Returns
- -------
- image : array
- An array of shape ``(M, N[, ...], image.ndim, image.ndim)``,
- containing the matrix corresponding to each coordinate.
- """
- image = S_elems[0]
- symmetric_image = np.zeros(
- image.shape + (image.ndim, image.ndim), dtype=S_elems[0].dtype
- )
- for idx, (row, col) in enumerate(
- combinations_with_replacement(range(image.ndim), 2)
- ):
- symmetric_image[..., row, col] = S_elems[idx]
- symmetric_image[..., col, row] = S_elems[idx]
- return symmetric_image
- def structure_tensor_eigenvalues(A_elems):
- """Compute eigenvalues of structure tensor.
- Parameters
- ----------
- A_elems : list of ndarray
- The upper-diagonal elements of the structure tensor, as returned
- by `structure_tensor`.
- Returns
- -------
- ndarray
- The eigenvalues of the structure tensor, in decreasing order. The
- eigenvalues are the leading dimension. That is, the coordinate
- [i, j, k] corresponds to the ith-largest eigenvalue at position (j, k).
- Examples
- --------
- >>> from skimage.feature import structure_tensor
- >>> from skimage.feature import structure_tensor_eigenvalues
- >>> square = np.zeros((5, 5))
- >>> square[2, 2] = 1
- >>> A_elems = structure_tensor(square, sigma=0.1, order='rc')
- >>> structure_tensor_eigenvalues(A_elems)[0]
- array([[0., 0., 0., 0., 0.],
- [0., 2., 4., 2., 0.],
- [0., 4., 0., 4., 0.],
- [0., 2., 4., 2., 0.],
- [0., 0., 0., 0., 0.]])
- See also
- --------
- structure_tensor
- """
- return _symmetric_compute_eigenvalues(A_elems)
- def hessian_matrix_eigvals(H_elems):
- """Compute eigenvalues of Hessian matrix.
- Parameters
- ----------
- H_elems : list of ndarray
- The upper-diagonal elements of the Hessian matrix, as returned
- by `hessian_matrix`.
- Returns
- -------
- eigs : ndarray
- The eigenvalues of the Hessian matrix, in decreasing order. The
- eigenvalues are the leading dimension. That is, ``eigs[i, j, k]``
- contains the ith-largest eigenvalue at position (j, k).
- Examples
- --------
- >>> from skimage.feature import hessian_matrix, hessian_matrix_eigvals
- >>> square = np.zeros((5, 5))
- >>> square[2, 2] = 4
- >>> H_elems = hessian_matrix(square, sigma=0.1, order='rc',
- ... use_gaussian_derivatives=False)
- >>> hessian_matrix_eigvals(H_elems)[0]
- array([[ 0., 0., 2., 0., 0.],
- [ 0., 1., 0., 1., 0.],
- [ 2., 0., -2., 0., 2.],
- [ 0., 1., 0., 1., 0.],
- [ 0., 0., 2., 0., 0.]])
- """
- return _symmetric_compute_eigenvalues(H_elems)
- def shape_index(image, sigma=1, mode='constant', cval=0):
- """Compute the shape index.
- The shape index, as defined by Koenderink & van Doorn [1]_, is a
- single valued measure of local curvature, assuming the image as a 3D plane
- with intensities representing heights.
- It is derived from the eigenvalues of the Hessian, and its
- value ranges from -1 to 1 (and is undefined (=NaN) in *flat* regions),
- with following ranges representing following shapes:
- .. table:: Ranges of the shape index and corresponding shapes.
- =================== =============
- Interval (s in ...) Shape
- =================== =============
- [ -1, -7/8) Spherical cup
- [-7/8, -5/8) Through
- [-5/8, -3/8) Rut
- [-3/8, -1/8) Saddle rut
- [-1/8, +1/8) Saddle
- [+1/8, +3/8) Saddle ridge
- [+3/8, +5/8) Ridge
- [+5/8, +7/8) Dome
- [+7/8, +1] Spherical cap
- =================== =============
- Parameters
- ----------
- image : (M, N) ndarray
- Input image.
- sigma : float, optional
- Standard deviation used for the Gaussian kernel, which is used for
- smoothing the input data before Hessian eigen value calculation.
- mode : {'constant', 'reflect', 'wrap', 'nearest', 'mirror'}, optional
- How to handle values outside the image borders
- cval : float, optional
- Used in conjunction with mode 'constant', the value outside
- the image boundaries.
- Returns
- -------
- s : ndarray
- Shape index
- References
- ----------
- .. [1] Koenderink, J. J. & van Doorn, A. J.,
- "Surface shape and curvature scales",
- Image and Vision Computing, 1992, 10, 557-564.
- :DOI:`10.1016/0262-8856(92)90076-F`
- Examples
- --------
- >>> from skimage.feature import shape_index
- >>> square = np.zeros((5, 5))
- >>> square[2, 2] = 4
- >>> s = shape_index(square, sigma=0.1)
- >>> s
- array([[ nan, nan, -0.5, nan, nan],
- [ nan, -0. , nan, -0. , nan],
- [-0.5, nan, -1. , nan, -0.5],
- [ nan, -0. , nan, -0. , nan],
- [ nan, nan, -0.5, nan, nan]])
- """
- H = hessian_matrix(
- image,
- sigma=sigma,
- mode=mode,
- cval=cval,
- order='rc',
- use_gaussian_derivatives=False,
- )
- l1, l2 = hessian_matrix_eigvals(H)
- # don't warn on divide by 0 as occurs in the docstring example
- with np.errstate(divide='ignore', invalid='ignore'):
- return (2.0 / np.pi) * np.arctan((l2 + l1) / (l2 - l1))
- def corner_kitchen_rosenfeld(image, mode='constant', cval=0):
- """Compute Kitchen and Rosenfeld corner measure response image.
- The corner measure is calculated as follows::
- (imxx * imy**2 + imyy * imx**2 - 2 * imxy * imx * imy)
- / (imx**2 + imy**2)
- Where imx and imy are the first and imxx, imxy, imyy the second
- derivatives.
- Parameters
- ----------
- image : (M, N) ndarray
- Input image.
- mode : {'constant', 'reflect', 'wrap', 'nearest', 'mirror'}, optional
- How to handle values outside the image borders.
- cval : float, optional
- Used in conjunction with mode 'constant', the value outside
- the image boundaries.
- Returns
- -------
- response : ndarray
- Kitchen and Rosenfeld response image.
- References
- ----------
- .. [1] Kitchen, L., & Rosenfeld, A. (1982). Gray-level corner detection.
- Pattern recognition letters, 1(2), 95-102.
- :DOI:`10.1016/0167-8655(82)90020-4`
- """
- float_dtype = _supported_float_type(image.dtype)
- image = image.astype(float_dtype, copy=False)
- imy, imx = _compute_derivatives(image, mode=mode, cval=cval)
- imxy, imxx = _compute_derivatives(imx, mode=mode, cval=cval)
- imyy, imyx = _compute_derivatives(imy, mode=mode, cval=cval)
- numerator = imxx * imy**2 + imyy * imx**2 - 2 * imxy * imx * imy
- denominator = imx**2 + imy**2
- response = np.zeros_like(image, dtype=float_dtype)
- mask = denominator != 0
- response[mask] = numerator[mask] / denominator[mask]
- return response
- def corner_harris(image, method='k', k=0.05, eps=1e-6, sigma=1):
- """Compute Harris corner measure response image.
- This corner detector uses information from the auto-correlation matrix A::
- A = [(imx**2) (imx*imy)] = [Axx Axy]
- [(imx*imy) (imy**2)] [Axy Ayy]
- Where imx and imy are first derivatives, averaged with a gaussian filter.
- The corner measure is then defined as::
- det(A) - k * trace(A)**2
- or::
- 2 * det(A) / (trace(A) + eps)
- Parameters
- ----------
- image : (M, N) ndarray
- Input image.
- method : {'k', 'eps'}, optional
- Method to compute the response image from the auto-correlation matrix.
- k : float, optional
- Sensitivity factor to separate corners from edges, typically in range
- `[0, 0.2]`. Small values of k result in detection of sharp corners.
- eps : float, optional
- Normalisation factor (Noble's corner measure).
- sigma : float, optional
- Standard deviation used for the Gaussian kernel, which is used as
- weighting function for the auto-correlation matrix.
- Returns
- -------
- response : ndarray
- Harris response image.
- References
- ----------
- .. [1] https://en.wikipedia.org/wiki/Corner_detection
- Examples
- --------
- >>> from skimage.feature import corner_harris, corner_peaks
- >>> square = np.zeros([10, 10])
- >>> square[2:8, 2:8] = 1
- >>> square.astype(int)
- array([[0, 0, 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, 1, 0, 0],
- [0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
- [0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
- [0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
- [0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
- [0, 0, 1, 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, 0, 0]])
- >>> corner_peaks(corner_harris(square), min_distance=1)
- array([[2, 2],
- [2, 7],
- [7, 2],
- [7, 7]])
- """
- Arr, Arc, Acc = structure_tensor(image, sigma, order='rc')
- # determinant
- detA = Arr * Acc - Arc**2
- # trace
- traceA = Arr + Acc
- if method == 'k':
- response = detA - k * traceA**2
- else:
- response = 2 * detA / (traceA + eps)
- return response
- def corner_shi_tomasi(image, sigma=1):
- """Compute Shi-Tomasi (Kanade-Tomasi) corner measure response image.
- This corner detector uses information from the auto-correlation matrix A::
- A = [(imx**2) (imx*imy)] = [Axx Axy]
- [(imx*imy) (imy**2)] [Axy Ayy]
- Where imx and imy are first derivatives, averaged with a gaussian filter.
- The corner measure is then defined as the smaller eigenvalue of A::
- ((Axx + Ayy) - sqrt((Axx - Ayy)**2 + 4 * Axy**2)) / 2
- Parameters
- ----------
- image : (M, N) ndarray
- Input image.
- sigma : float, optional
- Standard deviation used for the Gaussian kernel, which is used as
- weighting function for the auto-correlation matrix.
- Returns
- -------
- response : ndarray
- Shi-Tomasi response image.
- References
- ----------
- .. [1] https://en.wikipedia.org/wiki/Corner_detection
- Examples
- --------
- >>> from skimage.feature import corner_shi_tomasi, corner_peaks
- >>> square = np.zeros([10, 10])
- >>> square[2:8, 2:8] = 1
- >>> square.astype(int)
- array([[0, 0, 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, 1, 0, 0],
- [0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
- [0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
- [0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
- [0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
- [0, 0, 1, 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, 0, 0]])
- >>> corner_peaks(corner_shi_tomasi(square), min_distance=1)
- array([[2, 2],
- [2, 7],
- [7, 2],
- [7, 7]])
- """
- Arr, Arc, Acc = structure_tensor(image, sigma, order='rc')
- # minimum eigenvalue of A
- response = ((Arr + Acc) - np.sqrt((Arr - Acc) ** 2 + 4 * Arc**2)) / 2
- return response
- def corner_foerstner(image, sigma=1):
- """Compute Foerstner corner measure response image.
- This corner detector uses information from the auto-correlation matrix A::
- A = [(imx**2) (imx*imy)] = [Axx Axy]
- [(imx*imy) (imy**2)] [Axy Ayy]
- Where imx and imy are first derivatives, averaged with a gaussian filter.
- The corner measure is then defined as::
- w = det(A) / trace(A) (size of error ellipse)
- q = 4 * det(A) / trace(A)**2 (roundness of error ellipse)
- Parameters
- ----------
- image : (M, N) ndarray
- Input image.
- sigma : float, optional
- Standard deviation used for the Gaussian kernel, which is used as
- weighting function for the auto-correlation matrix.
- Returns
- -------
- w : ndarray
- Error ellipse sizes.
- q : ndarray
- Roundness of error ellipse.
- References
- ----------
- .. [1] Förstner, W., & Gülch, E. (1987, June). A fast operator for
- detection and precise location of distinct points, corners and
- centres of circular features. In Proc. ISPRS intercommission
- conference on fast processing of photogrammetric data (pp. 281-305).
- https://cseweb.ucsd.edu/classes/sp02/cse252/foerstner/foerstner.pdf
- .. [2] https://en.wikipedia.org/wiki/Corner_detection
- Examples
- --------
- >>> from skimage.feature import corner_foerstner, corner_peaks
- >>> square = np.zeros([10, 10])
- >>> square[2:8, 2:8] = 1
- >>> square.astype(int)
- array([[0, 0, 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, 1, 0, 0],
- [0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
- [0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
- [0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
- [0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
- [0, 0, 1, 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, 0, 0]])
- >>> w, q = corner_foerstner(square)
- >>> accuracy_thresh = 0.5
- >>> roundness_thresh = 0.3
- >>> foerstner = (q > roundness_thresh) * (w > accuracy_thresh) * w
- >>> corner_peaks(foerstner, min_distance=1)
- array([[2, 2],
- [2, 7],
- [7, 2],
- [7, 7]])
- """
- Arr, Arc, Acc = structure_tensor(image, sigma, order='rc')
- # determinant
- detA = Arr * Acc - Arc**2
- # trace
- traceA = Arr + Acc
- w = np.zeros_like(image, dtype=detA.dtype)
- q = np.zeros_like(w)
- mask = traceA != 0
- w[mask] = detA[mask] / traceA[mask]
- q[mask] = 4 * detA[mask] / traceA[mask] ** 2
- return w, q
- def corner_fast(image, n=12, threshold=0.15):
- """Extract FAST corners for a given image.
- Parameters
- ----------
- image : (M, N) ndarray
- Input image.
- n : int, optional
- Minimum number of consecutive pixels out of 16 pixels on the circle
- that should all be either brighter or darker w.r.t testpixel.
- A point c on the circle is darker w.r.t test pixel p if
- `Ic < Ip - threshold` and brighter if `Ic > Ip + threshold`. Also
- stands for the n in `FAST-n` corner detector.
- threshold : float, optional
- 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
- -------
- response : ndarray
- FAST corner response image.
- References
- ----------
- .. [1] Rosten, E., & Drummond, T. (2006, May). Machine learning for
- high-speed corner detection. In European conference on computer
- vision (pp. 430-443). Springer, Berlin, Heidelberg.
- :DOI:`10.1007/11744023_34`
- http://www.edwardrosten.com/work/rosten_2006_machine.pdf
- .. [2] Wikipedia, "Features from accelerated segment test",
- https://en.wikipedia.org/wiki/Features_from_accelerated_segment_test
- Examples
- --------
- >>> from skimage.feature import corner_fast, corner_peaks
- >>> square = np.zeros((12, 12))
- >>> square[3:9, 3:9] = 1
- >>> square.astype(int)
- array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0],
- [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0],
- [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0],
- [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0],
- [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0],
- [0, 0, 0, 1, 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, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
- >>> corner_peaks(corner_fast(square, 9), min_distance=1)
- array([[3, 3],
- [3, 8],
- [8, 3],
- [8, 8]])
- """
- image = _prepare_grayscale_input_2D(image)
- image = np.ascontiguousarray(image)
- response = _corner_fast(image, n, threshold)
- return response
- def corner_subpix(image, corners, window_size=11, alpha=0.99):
- """Determine subpixel position of corners.
- A statistical test decides whether the corner is defined as the
- intersection of two edges or a single peak. Depending on the classification
- result, the subpixel corner location is determined based on the local
- covariance of the grey-values. If the significance level for either
- statistical test is not sufficient, the corner cannot be classified, and
- the output subpixel position is set to NaN.
- Parameters
- ----------
- image : (M, N) ndarray
- Input image.
- corners : (K, 2) ndarray
- Corner coordinates `(row, col)`.
- window_size : int, optional
- Search window size for subpixel estimation.
- alpha : float, optional
- Significance level for corner classification.
- Returns
- -------
- positions : (K, 2) ndarray
- Subpixel corner positions. NaN for "not classified" corners.
- References
- ----------
- .. [1] Förstner, W., & Gülch, E. (1987, June). A fast operator for
- detection and precise location of distinct points, corners and
- centres of circular features. In Proc. ISPRS intercommission
- conference on fast processing of photogrammetric data (pp. 281-305).
- https://cseweb.ucsd.edu/classes/sp02/cse252/foerstner/foerstner.pdf
- .. [2] https://en.wikipedia.org/wiki/Corner_detection
- Examples
- --------
- >>> from skimage.feature import corner_harris, corner_peaks, corner_subpix
- >>> img = np.zeros((10, 10))
- >>> img[:5, :5] = 1
- >>> img[5:, 5:] = 1
- >>> img.astype(int)
- array([[1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
- [1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
- [1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
- [1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
- [1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 1, 1, 1, 1, 1],
- [0, 0, 0, 0, 0, 1, 1, 1, 1, 1],
- [0, 0, 0, 0, 0, 1, 1, 1, 1, 1],
- [0, 0, 0, 0, 0, 1, 1, 1, 1, 1],
- [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]])
- >>> coords = corner_peaks(corner_harris(img), min_distance=2)
- >>> coords_subpix = corner_subpix(img, coords, window_size=7)
- >>> coords_subpix
- array([[4.5, 4.5]])
- """
- # window extent in one direction
- wext = (window_size - 1) // 2
- float_dtype = _supported_float_type(image.dtype)
- image = image.astype(float_dtype, copy=False)
- image = np.pad(image, pad_width=wext, mode='constant', constant_values=0)
- # add pad width, make sure to not modify the input values in-place
- corners = safe_as_int(corners + wext)
- # normal equation arrays
- N_dot = np.zeros((2, 2), dtype=float_dtype)
- N_edge = np.zeros((2, 2), dtype=float_dtype)
- b_dot = np.zeros((2,), dtype=float_dtype)
- b_edge = np.zeros((2,), dtype=float_dtype)
- # critical statistical test values
- redundancy = window_size**2 - 2
- t_crit_dot = stats.f.isf(1 - alpha, redundancy, redundancy)
- t_crit_edge = stats.f.isf(alpha, redundancy, redundancy)
- # coordinates of pixels within window
- y, x = np.mgrid[-wext : wext + 1, -wext : wext + 1]
- corners_subpix = np.zeros_like(corners, dtype=float_dtype)
- for i, (y0, x0) in enumerate(corners):
- # crop window around corner + border for sobel operator
- miny = y0 - wext - 1
- maxy = y0 + wext + 2
- minx = x0 - wext - 1
- maxx = x0 + wext + 2
- window = image[miny:maxy, minx:maxx]
- winy, winx = _compute_derivatives(window, mode='constant', cval=0)
- # compute gradient squares and remove border
- winx_winx = (winx * winx)[1:-1, 1:-1]
- winx_winy = (winx * winy)[1:-1, 1:-1]
- winy_winy = (winy * winy)[1:-1, 1:-1]
- # sum of squared differences (mean instead of gaussian filter)
- Axx = np.sum(winx_winx)
- Axy = np.sum(winx_winy)
- Ayy = np.sum(winy_winy)
- # sum of squared differences weighted with coordinates
- # (mean instead of gaussian filter)
- bxx_x = np.sum(winx_winx * x)
- bxx_y = np.sum(winx_winx * y)
- bxy_x = np.sum(winx_winy * x)
- bxy_y = np.sum(winx_winy * y)
- byy_x = np.sum(winy_winy * x)
- byy_y = np.sum(winy_winy * y)
- # normal equations for subpixel position
- N_dot[0, 0] = Axx
- N_dot[0, 1] = N_dot[1, 0] = -Axy
- N_dot[1, 1] = Ayy
- N_edge[0, 0] = Ayy
- N_edge[0, 1] = N_edge[1, 0] = Axy
- N_edge[1, 1] = Axx
- b_dot[:] = bxx_y - bxy_x, byy_x - bxy_y
- b_edge[:] = byy_y + bxy_x, bxx_x + bxy_y
- # estimated positions
- try:
- est_dot = np.linalg.solve(N_dot, b_dot)
- est_edge = np.linalg.solve(N_edge, b_edge)
- except np.linalg.LinAlgError:
- # if image is constant the system is singular
- corners_subpix[i, :] = np.nan, np.nan
- continue
- # residuals
- ry_dot = y - est_dot[0]
- rx_dot = x - est_dot[1]
- ry_edge = y - est_edge[0]
- rx_edge = x - est_edge[1]
- # squared residuals
- rxx_dot = rx_dot * rx_dot
- rxy_dot = rx_dot * ry_dot
- ryy_dot = ry_dot * ry_dot
- rxx_edge = rx_edge * rx_edge
- rxy_edge = rx_edge * ry_edge
- ryy_edge = ry_edge * ry_edge
- # determine corner class (dot or edge)
- # variance for different models
- var_dot = np.sum(
- winx_winx * ryy_dot - 2 * winx_winy * rxy_dot + winy_winy * rxx_dot
- )
- var_edge = np.sum(
- winy_winy * ryy_edge + 2 * winx_winy * rxy_edge + winx_winx * rxx_edge
- )
- # test value (F-distributed)
- if var_dot < np.spacing(1) and var_edge < np.spacing(1):
- t = np.nan
- elif var_dot == 0:
- t = np.inf
- else:
- t = var_edge / var_dot
- # 1 for edge, -1 for dot, 0 for "not classified"
- corner_class = int(t < t_crit_edge) - int(t > t_crit_dot)
- if corner_class == -1:
- corners_subpix[i, :] = y0 + est_dot[0], x0 + est_dot[1]
- elif corner_class == 0:
- corners_subpix[i, :] = np.nan, np.nan
- elif corner_class == 1:
- corners_subpix[i, :] = y0 + est_edge[0], x0 + est_edge[1]
- # subtract pad width
- corners_subpix -= wext
- return corners_subpix
- def corner_peaks(
- image,
- min_distance=1,
- threshold_abs=None,
- threshold_rel=None,
- exclude_border=True,
- indices=True,
- num_peaks=np.inf,
- footprint=None,
- labels=None,
- *,
- num_peaks_per_label=np.inf,
- p_norm=np.inf,
- ):
- """Find peaks in corner measure response image.
- This differs from `skimage.feature.peak_local_max` in that it suppresses
- multiple connected peaks with the same accumulator value.
- Parameters
- ----------
- image : (M, N) ndarray
- Input image.
- min_distance : int, optional
- The minimal allowed distance separating peaks.
- * : *
- See :py:meth:`skimage.feature.peak_local_max`.
- p_norm : float
- Which Minkowski p-norm to use. Should be in the range [1, inf].
- A finite large p may cause a ValueError if overflow can occur.
- ``inf`` corresponds to the Chebyshev distance and 2 to the
- Euclidean distance.
- Returns
- -------
- output : ndarray or ndarray of bools
- * If `indices = True` : (row, column, ...) coordinates of peaks.
- * If `indices = False` : Boolean array shaped like `image`, with peaks
- represented by True values.
- See also
- --------
- skimage.feature.peak_local_max
- Notes
- -----
- .. versionchanged:: 0.18
- The default value of `threshold_rel` has changed to None, which
- corresponds to letting `skimage.feature.peak_local_max` decide on the
- default. This is equivalent to `threshold_rel=0`.
- The `num_peaks` limit is applied before suppression of connected peaks.
- To limit the number of peaks after suppression, set `num_peaks=np.inf` and
- post-process the output of this function.
- Examples
- --------
- >>> from skimage.feature import peak_local_max
- >>> response = np.zeros((5, 5))
- >>> response[2:4, 2:4] = 1
- >>> response
- array([[0., 0., 0., 0., 0.],
- [0., 0., 0., 0., 0.],
- [0., 0., 1., 1., 0.],
- [0., 0., 1., 1., 0.],
- [0., 0., 0., 0., 0.]])
- >>> peak_local_max(response)
- array([[2, 2],
- [2, 3],
- [3, 2],
- [3, 3]])
- >>> corner_peaks(response)
- array([[2, 2]])
- """
- if np.isinf(num_peaks):
- num_peaks = None
- # Get the coordinates of the detected peaks
- coords = peak_local_max(
- image,
- min_distance=min_distance,
- threshold_abs=threshold_abs,
- threshold_rel=threshold_rel,
- exclude_border=exclude_border,
- num_peaks=np.inf,
- footprint=footprint,
- labels=labels,
- num_peaks_per_label=num_peaks_per_label,
- )
- if len(coords):
- # Use KDtree to find the peaks that are too close to each other
- tree = spatial.cKDTree(coords)
- rejected_peaks_indices = set()
- for idx, point in enumerate(coords):
- if idx not in rejected_peaks_indices:
- candidates = tree.query_ball_point(point, r=min_distance, p=p_norm)
- candidates.remove(idx)
- rejected_peaks_indices.update(candidates)
- # Remove the peaks that are too close to each other
- coords = np.delete(coords, tuple(rejected_peaks_indices), axis=0)[:num_peaks]
- if indices:
- return coords
- peaks = np.zeros_like(image, dtype=bool)
- peaks[tuple(coords.T)] = True
- return peaks
- def corner_moravec(image, window_size=1):
- """Compute Moravec corner measure response image.
- This is one of the simplest corner detectors and is comparatively fast but
- has several limitations (e.g. not rotation invariant).
- Parameters
- ----------
- image : (M, N) ndarray
- Input image.
- window_size : int, optional
- Window size.
- Returns
- -------
- response : ndarray
- Moravec response image.
- References
- ----------
- .. [1] https://en.wikipedia.org/wiki/Corner_detection
- Examples
- --------
- >>> from skimage.feature import corner_moravec
- >>> square = np.zeros([7, 7])
- >>> square[3, 3] = 1
- >>> square.astype(int)
- array([[0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 1, 0, 0, 0],
- [0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 0, 0]])
- >>> corner_moravec(square).astype(int)
- array([[0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 0, 0],
- [0, 0, 1, 1, 1, 0, 0],
- [0, 0, 1, 2, 1, 0, 0],
- [0, 0, 1, 1, 1, 0, 0],
- [0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 0, 0]])
- """
- image = img_as_float(image)
- float_dtype = _supported_float_type(image.dtype)
- image = image.astype(float_dtype, copy=False)
- return _corner_moravec(np.ascontiguousarray(image), window_size)
- def corner_orientations(image, corners, mask):
- """Compute the orientation of corners.
- The orientation of corners is computed using the first order central moment
- i.e. the center of mass approach. The corner orientation is the angle of
- the vector from the corner coordinate to the intensity centroid in the
- local neighborhood around the corner calculated using first order central
- moment.
- Parameters
- ----------
- image : (M, N) array
- Input grayscale image.
- corners : (K, 2) array
- Corner coordinates as ``(row, col)``.
- mask : 2D array
- Mask defining the local neighborhood of the corner used for the
- calculation of the central moment.
- Returns
- -------
- orientations : (K, 1) array
- Orientations of corners in the range [-pi, pi].
- References
- ----------
- .. [1] Ethan Rublee, Vincent Rabaud, Kurt Konolige and Gary Bradski
- "ORB : An efficient alternative to SIFT and SURF"
- http://www.vision.cs.chubu.ac.jp/CV-R/pdf/Rublee_iccv2011.pdf
- .. [2] Paul L. Rosin, "Measuring Corner Properties"
- http://users.cs.cf.ac.uk/Paul.Rosin/corner2.pdf
- Examples
- --------
- >>> from skimage.morphology import octagon
- >>> from skimage.feature import (corner_fast, corner_peaks,
- ... corner_orientations)
- >>> square = np.zeros((12, 12))
- >>> square[3:9, 3:9] = 1
- >>> square.astype(int)
- array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0],
- [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0],
- [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0],
- [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0],
- [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0],
- [0, 0, 0, 1, 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, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
- >>> corners = corner_peaks(corner_fast(square, 9), min_distance=1)
- >>> corners
- array([[3, 3],
- [3, 8],
- [8, 3],
- [8, 8]])
- >>> orientations = corner_orientations(square, corners, octagon(3, 2))
- >>> np.rad2deg(orientations)
- array([ 45., 135., -45., -135.])
- """
- image = _prepare_grayscale_input_2D(image)
- return _corner_orientations(image, corners, mask)
|