mirror of
https://github.com/wassname/scikit-image.git
synced 2026-09-09 11:33:41 +08:00
Fix bug in array padding, template matching
This commit is contained in:
+99
-41
@@ -1,19 +1,33 @@
|
||||
"""template.py - Template matching
|
||||
"""
|
||||
import numpy as np
|
||||
from . import _template
|
||||
from scipy.signal import fftconvolve
|
||||
|
||||
from skimage.util import pad
|
||||
|
||||
|
||||
def match_template(image, template, pad_input=False):
|
||||
def _window_sum(image, window_shape):
|
||||
|
||||
window_sum = np.cumsum(image, axis=0)
|
||||
window_sum = (window_sum[window_shape[0]:-1]
|
||||
- window_sum[:-window_shape[0]-1])
|
||||
|
||||
window_sum = np.cumsum(window_sum, axis=1)
|
||||
window_sum = (window_sum[:, window_shape[1]:-1]
|
||||
- window_sum[:, :-window_shape[1]-1])
|
||||
|
||||
return window_sum
|
||||
|
||||
|
||||
def match_template(image, template, pad_input=False, mode='constant',
|
||||
constant_values=0):
|
||||
"""Match a template to a 2-D image using normalized correlation.
|
||||
|
||||
The output is an array with values between -1.0 and 1.0, which correspond
|
||||
to the probability that the template is found at that position.
|
||||
to the correlation coefficient that the template is found at the position.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : array_like
|
||||
2-D Image to process.
|
||||
2-D Image to process.
|
||||
template : array_like
|
||||
Template to locate.
|
||||
pad_input : bool
|
||||
@@ -22,6 +36,10 @@ def match_template(image, template, pad_input=False):
|
||||
Otherwise, the output is an array with shape `(M - m + 1, N - n + 1)`
|
||||
for an `(M, N)` image and an `(m, n)` template, and matches correspond
|
||||
to origin (top-left corner) of the template.
|
||||
mode : see `numpy.pad`, optional
|
||||
Padding mode.
|
||||
constant_values : see `numpy.pad`, optional
|
||||
Constant values used in conjunction with ``mode='constant'``.
|
||||
|
||||
Returns
|
||||
-------
|
||||
@@ -30,52 +48,92 @@ def match_template(image, template, pad_input=False):
|
||||
`(m, n)` template, the `output` is `(M - m + 1, N - n + 1)` when
|
||||
`pad_input = False` and `(M, N)` when `pad_input = True`.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Briechle and Hanebeck, "Template Matching using Fast Normalized
|
||||
Cross Correlation", Proceedings of the SPIE (2001).
|
||||
.. [2] J. P. Lewis, "Fast Normalized Cross-Correlation", Industrial Light
|
||||
and Magic.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> template = np.zeros((3, 3))
|
||||
>>> template[1, 1] = 1
|
||||
>>> print(template)
|
||||
[[ 0. 0. 0.]
|
||||
[ 0. 1. 0.]
|
||||
[ 0. 0. 0.]]
|
||||
>>> template
|
||||
array([[ 0. 0. 0.]
|
||||
[ 0. 1. 0.]
|
||||
[ 0. 0. 0.]])
|
||||
>>> image = np.zeros((6, 6))
|
||||
>>> image[1, 1] = 1
|
||||
>>> image[4, 4] = -1
|
||||
>>> print(image)
|
||||
[[ 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. -1. 0.]
|
||||
[ 0. 0. 0. 0. 0. 0.]]
|
||||
>>> image
|
||||
array([[ 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. -1. 0.]
|
||||
[ 0. 0. 0. 0. 0. 0.]])
|
||||
>>> result = match_template(image, template)
|
||||
>>> print(np.round(result, 3))
|
||||
[[ 1. -0.125 0. 0. ]
|
||||
[-0.125 -0.125 0. 0. ]
|
||||
[ 0. 0. 0.125 0.125]
|
||||
[ 0. 0. 0.125 -1. ]]
|
||||
>>> np.round(result, 3)
|
||||
array([[ 1. -0.125 0. 0. ]
|
||||
[-0.125 -0.125 0. 0. ]
|
||||
[ 0. 0. 0.125 0.125]
|
||||
[ 0. 0. 0.125 -1. ]])
|
||||
>>> result = match_template(image, template, pad_input=True)
|
||||
>>> print(np.round(result, 3))
|
||||
[[-0.125 -0.125 -0.125 0. 0. 0. ]
|
||||
[-0.125 1. -0.125 0. 0. 0. ]
|
||||
[-0.125 -0.125 -0.125 0. 0. 0. ]
|
||||
[ 0. 0. 0. 0.125 0.125 0.125]
|
||||
[ 0. 0. 0. 0.125 -1. 0.125]
|
||||
[ 0. 0. 0. 0.125 0.125 0.125]]
|
||||
>>> np.round(result, 3)
|
||||
array([[-0.125 -0.125 -0.125 0. 0. 0. ]
|
||||
[-0.125 1. -0.125 0. 0. 0. ]
|
||||
[-0.125 -0.125 -0.125 0. 0. 0. ]
|
||||
[ 0. 0. 0. 0.125 0.125 0.125]
|
||||
[ 0. 0. 0. 0.125 -1. 0.125]
|
||||
[ 0. 0. 0. 0.125 0.125 0.125]])
|
||||
"""
|
||||
|
||||
if np.any(np.less(image.shape, template.shape)):
|
||||
raise ValueError("Image must be larger than template.")
|
||||
image = np.ascontiguousarray(image, dtype=np.float32)
|
||||
template = np.ascontiguousarray(template, dtype=np.float32)
|
||||
|
||||
orig_shape = image.shape
|
||||
|
||||
image = np.array(image, dtype=np.float32, copy=False)
|
||||
|
||||
if mode == 'constant':
|
||||
image = pad(image, pad_width=template.shape, mode=mode,
|
||||
constant_values=constant_values)
|
||||
else:
|
||||
image = pad(image, pad_width=template.shape, mode=mode)
|
||||
|
||||
image_window_sum = _window_sum(image, template.shape)
|
||||
image_window_sum2 = _window_sum(image**2, template.shape)
|
||||
|
||||
template_area = np.prod(template.shape)
|
||||
template_ssd = np.sum((template - template.mean())**2)
|
||||
|
||||
xcorr = fftconvolve(image, template[::-1, ::-1], mode="valid")[1:-1, 1:-1]
|
||||
nom = xcorr - image_window_sum * (template.sum() / template_area)
|
||||
|
||||
denom = image_window_sum2 - image_window_sum**2 / template_area
|
||||
denom *= template_ssd
|
||||
np.maximum(denom, 0, out=denom) # sqrt of negative number not allowed
|
||||
np.sqrt(denom, out=denom)
|
||||
|
||||
response = np.zeros_like(xcorr, dtype=np.float32)
|
||||
|
||||
# avoid zero-division
|
||||
mask = denom > np.finfo(np.float32).eps
|
||||
|
||||
response[mask] = nom[mask] / denom[mask]
|
||||
|
||||
if pad_input:
|
||||
pad_size = tuple(np.array(image.shape) + np.array(template.shape) - 1)
|
||||
pad_image = np.mean(image) * np.ones(pad_size, dtype=np.float32)
|
||||
h, w = image.shape
|
||||
i0, j0 = template.shape
|
||||
i0 /= 2
|
||||
j0 /= 2
|
||||
pad_image[i0:i0 + h, j0:j0 + w] = image
|
||||
image = pad_image
|
||||
result = _template.match_template(image, template)
|
||||
return result
|
||||
r0 = (template.shape[0] - 1) // 2
|
||||
r1 = r0 + orig_shape[0]
|
||||
c0 = (template.shape[1] - 1) // 2
|
||||
c1 = c0 + orig_shape[1]
|
||||
else:
|
||||
r0 = template.shape[0] - 1
|
||||
r1 = r0 + orig_shape[0] - template.shape[0] + 1
|
||||
c0 = template.shape[1] - 1
|
||||
c1 = c0 + orig_shape[1] - template.shape[1] + 1
|
||||
|
||||
response = response[r0:r1, c0:c1]
|
||||
|
||||
return response
|
||||
|
||||
Reference in New Issue
Block a user