Add subpixel corner detection

This commit is contained in:
Johannes Schönberger
2012-09-16 10:48:18 +02:00
parent bf3d34ccf2
commit cea889c997
2 changed files with 126 additions and 4 deletions
+1 -1
View File
@@ -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
View File
@@ -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