158 lines
4.5 KiB
Cython
158 lines
4.5 KiB
Cython
# cython: cdivision=True
|
|
# cython: boundscheck=False
|
|
# cython: nonecheck=False
|
|
# cython: wraparound=False
|
|
import numpy as np
|
|
cimport numpy as cnp
|
|
|
|
cnp.import_array()
|
|
|
|
|
|
cdef inline Py_ssize_t _clip(Py_ssize_t x, Py_ssize_t low,
|
|
Py_ssize_t high) nogil:
|
|
"""Clips coordinate between high and low.
|
|
|
|
This method was created so that `hessian_det_appx` does not have to make
|
|
a Python call.
|
|
|
|
Parameters
|
|
----------
|
|
x : int
|
|
Coordinate to be clipped.
|
|
low : int
|
|
The lower bound.
|
|
high : int
|
|
The higher bound.
|
|
|
|
Returns
|
|
-------
|
|
x : int
|
|
`x` clipped between `high` and `low`.
|
|
"""
|
|
|
|
if(x > high):
|
|
return high
|
|
if(x < low):
|
|
return low
|
|
return x
|
|
|
|
|
|
cdef inline cnp.double_t _integ(
|
|
cnp.double_t[:, ::1] img, Py_ssize_t r, Py_ssize_t c,
|
|
Py_ssize_t rl, Py_ssize_t cl) nogil:
|
|
"""Integrate over the integral image in the given window
|
|
|
|
This method was created so that `hessian_det_appx` does not have to make
|
|
a Python call.
|
|
|
|
Parameters
|
|
----------
|
|
img : array
|
|
The integral image over which to integrate.
|
|
r : int
|
|
The row number of the top left corner.
|
|
c : int
|
|
The column number of the top left corner.
|
|
rl : int
|
|
The number of rows over which to integrate.
|
|
cl : int
|
|
The number of columns over which to integrate.
|
|
|
|
Returns
|
|
-------
|
|
ans : int
|
|
The integral over the given window.
|
|
"""
|
|
|
|
r = _clip(r, 0, img.shape[0] - 1)
|
|
c = _clip(c, 0, img.shape[1] - 1)
|
|
|
|
r2 = _clip(r + rl, 0, img.shape[0] - 1)
|
|
c2 = _clip(c + cl, 0, img.shape[1] - 1)
|
|
|
|
cdef cnp.double_t ans = img[r, c] + img[r2, c2] - img[r, c2] - img[r2, c]
|
|
|
|
if (ans < 0):
|
|
return 0
|
|
return ans
|
|
|
|
|
|
def _hessian_matrix_det(cnp.double_t[:, ::1] img, double sigma):
|
|
"""Computes the approximate Hessian Determinant over an image.
|
|
|
|
This method uses box filters over integral images to compute the
|
|
approximate Hessian Determinant as described in [1]_.
|
|
|
|
Parameters
|
|
----------
|
|
img : array
|
|
The integral image over which to compute Hessian Determinant.
|
|
sigma : float
|
|
Standard deviation used for the Gaussian kernel, used for the Hessian
|
|
matrix
|
|
|
|
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
|
|
-----
|
|
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 it's
|
|
determinant.
|
|
"""
|
|
|
|
cdef Py_ssize_t size = int(3 * sigma)
|
|
cdef Py_ssize_t height = img.shape[0]
|
|
cdef Py_ssize_t width = img.shape[1]
|
|
cdef Py_ssize_t r, c
|
|
cdef Py_ssize_t s2 = (size - 1) / 2
|
|
cdef Py_ssize_t s3 = size / 3
|
|
cdef Py_ssize_t l = size / 3
|
|
cdef Py_ssize_t w = size
|
|
cdef Py_ssize_t b = (size - 1) / 2
|
|
cdef cnp.double_t mid, side, tl, tr, bl, br
|
|
cdef cnp.double_t[:, ::1] out = np.zeros_like(img, dtype=np.double)
|
|
cdef cnp.double_t w_i = 1.0 / size / size
|
|
|
|
cdef float dxx, dyy, dxy
|
|
|
|
with nogil:
|
|
if size % 2 == 0:
|
|
size += 1
|
|
|
|
for r in range(height):
|
|
for c in range(width):
|
|
tl = _integ(img, r - s3, c - s3, s3, s3) # top left
|
|
br = _integ(img, r + 1, c + 1, s3, s3) # bottom right
|
|
bl = _integ(img, r - s3, c + 1, s3, s3) # bottom left
|
|
tr = _integ(img, r + 1, c - s3, s3, s3) # top right
|
|
|
|
dxy = bl + tr - tl - br
|
|
dxy = -dxy * w_i
|
|
|
|
mid = _integ(img, r - s3 + 1, c - s2, 2 * s3 - 1, w) # middle box
|
|
side = _integ(img, r - s3 + 1, c - s3 / 2, 2 * s3 - 1, s3) # sides
|
|
|
|
dxx = mid - 3 * side
|
|
dxx = -dxx * w_i
|
|
|
|
mid = _integ(img, r - s2, c - s3 + 1, w, 2 * s3 - 1)
|
|
side = _integ(img, r - s3 / 2, c - s3 + 1, s3, 2 * s3 - 1)
|
|
|
|
dyy = mid - 3 * side
|
|
dyy = -dyy * w_i
|
|
|
|
out[r, c] = (dxx * dyy - 0.81 * (dxy * dxy))
|
|
|
|
return out
|