mirror of
https://github.com/wassname/scikit-image.git
synced 2026-07-08 05:54:47 +08:00
Add subpixel corner detection
This commit is contained in:
@@ -2,6 +2,6 @@ from ._hog import hog
|
||||
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_foerstner, corner_subpix)
|
||||
from .corner_cy import corner_moravec
|
||||
from .template import match_template
|
||||
|
||||
+125
-3
@@ -1,6 +1,8 @@
|
||||
import numpy as np
|
||||
from scipy import ndimage
|
||||
from scipy import stats
|
||||
from skimage.color import rgb2grey
|
||||
from skimage.util import img_as_float
|
||||
from . import peak
|
||||
|
||||
|
||||
@@ -19,8 +21,8 @@ def _compute_derivatives(image):
|
||||
|
||||
"""
|
||||
|
||||
imx = ndimage.sobel(image, axis=0, mode='constant', cval=0)
|
||||
imy = ndimage.sobel(image, axis=1, mode='constant', cval=0)
|
||||
imy = ndimage.sobel(image, axis=0, mode='constant', cval=0)
|
||||
imx = ndimage.sobel(image, axis=1, mode='constant', cval=0)
|
||||
|
||||
return imx, imy
|
||||
|
||||
@@ -44,7 +46,7 @@ def _compute_auto_correlation(image, sigma):
|
||||
"""
|
||||
|
||||
if image.ndim == 3:
|
||||
image = rgb2grey(image)
|
||||
image = img_as_float(rgb2grey(image))
|
||||
|
||||
imx, imy = _compute_derivatives(image)
|
||||
|
||||
@@ -279,3 +281,123 @@ def corner_foerstner(image, sigma=1):
|
||||
q = 4 * detA / traceA**2
|
||||
|
||||
return w, q
|
||||
|
||||
|
||||
def corner_subpix(image, corners, window_size=11, alpha=0.99):
|
||||
"""Determine subpixel position of corners.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : ndarray
|
||||
Input image.
|
||||
corners : (N, 2) ndarray
|
||||
Corner coordinates `(row, cols)`.
|
||||
window_size : int, optional
|
||||
Search window size for subpixel estimation.
|
||||
alpha : float, optional
|
||||
Significance level for point classification.
|
||||
|
||||
Returns
|
||||
-------
|
||||
positions : (N, 2) ndarray
|
||||
Subpixel corner positions. NaN for "not classified" corners.
|
||||
|
||||
"""
|
||||
|
||||
# window extent in one direction
|
||||
wext = (window_size - 1) / 2
|
||||
|
||||
# normal equation arrays
|
||||
N_dot = np.zeros((2, 2), dtype=np.double)
|
||||
N_edge = np.zeros((2, 2), dtype=np.double)
|
||||
b_dot = np.zeros((2, ), dtype=np.double)
|
||||
b_edge = np.zeros((2, ), dtype=np.double)
|
||||
|
||||
# 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]
|
||||
|
||||
# output arrays
|
||||
corners_subpix = np.zeros_like(corners, dtype=np.double)
|
||||
corners_class = np.zeros(corners.shape[0], dtype=np.int8)
|
||||
|
||||
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]
|
||||
|
||||
winx, winy = _compute_derivatives(window)
|
||||
|
||||
# compute gradient suares 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
|
||||
est_dot = np.linalg.solve(N_dot, b_dot)
|
||||
est_edge = np.linalg.solve(N_edge, b_edge)
|
||||
|
||||
# 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)
|
||||
w_dot = np.sum(winx_winx * ryy_dot - 2 * winx_winy * rxy_dot \
|
||||
+ winy_winy * rxx_dot)
|
||||
w_edge = np.sum(winy_winy * ryy_edge + 2 * winx_winy * rxy_edge \
|
||||
+ winx_winx * rxx_edge)
|
||||
t = w_edge / w_dot
|
||||
# 1 for edge, -1 for dot, 0 for "not classified"
|
||||
corner_class = (t < t_crit_edge) - (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]
|
||||
|
||||
return corners_subpix
|
||||
|
||||
Reference in New Issue
Block a user