added generic method to adaptive thresholding

This commit is contained in:
Johannes Schönberger
2012-04-25 23:44:06 +02:00
parent bacde61e39
commit 8b8b6d0d60
4 changed files with 54 additions and 25 deletions
+6 -3
View File
@@ -52,17 +52,20 @@ plt.axis('off')
#: Adaptive thresholding
plt.subplot(2, 3, 4)
plt.imshow(threshold_adaptive(image, 11, 5, 'gaussian'), cmap=plt.cm.gray)
plt.imshow(threshold_adaptive(image, 11, method='gaussian', offset=5),
cmap=plt.cm.gray)
plt.title('Adaptive edge thresholding')
plt.axis('off')
plt.subplot(2, 3, 5)
plt.imshow(threshold_adaptive(image, 125, 7.5, 'gaussian'), cmap=plt.cm.gray)
plt.imshow(threshold_adaptive(image, 125, method='gaussian', offset=7.5),
cmap=plt.cm.gray)
plt.title('Adaptive Gaussian')
plt.axis('off')
plt.subplot(2, 3, 6)
plt.imshow(threshold_adaptive(image, 125, 7.5, 'mean'), cmap=plt.cm.gray)
plt.imshow(threshold_adaptive(image, 125, method='mean', offset=7.5),
cmap=plt.cm.gray)
plt.title('Adaptive Mean')
plt.axis('off')
+16 -11
View File
@@ -6,22 +6,27 @@ cimport cython
@cython.boundscheck(False)
@cython.wraparound(False)
def _threshold_adaptive(np.ndarray[np.double_t, ndim=2] image,
int block_size, double offset, method):
def _threshold_adaptive(np.ndarray[np.double_t, ndim=2] image, int block_size,
method, double offset, mode, param):
cdef int r, c
cdef np.ndarray[np.float64_t, ndim=2] mean_image
if method == 'gaussian':
# covers > 99% of distribution
sigma = (block_size - 1) / 6.0
mean_image = scipy.ndimage.gaussian_filter(image, sigma)
cdef np.ndarray[np.float64_t, ndim=2] thres_image
if method == 'generic':
thres_image = scipy.ndimage.generic_filter(image, param, block_size,
mode=mode)
elif method == 'gaussian':
if param is None:
# automatically determine sigme which covers > 99% of distribution
sigma = (block_size - 1) / 6.0
thres_image = scipy.ndimage.gaussian_filter(image, sigma, mode=mode)
elif method == 'mean':
mask = 1. / block_size**2 * np.ones((block_size, block_size))
mean_image = scipy.ndimage.convolve(image, mask)
thres_image = scipy.ndimage.convolve(image, mask, mode=mode)
elif method == 'median':
mean_image = scipy.ndimage.median_filter(image, block_size)
thres_image = scipy.ndimage.median_filter(image, block_size, mode=mode)
for r in range(image.shape[0]):
for c in range(image.shape[1]):
mean_image[r,c] = image[r,c] > (mean_image[r,c] - offset)
thres_image[r,c] = image[r,c] > (thres_image[r,c] - offset)
return mean_image.astype('bool')
return thres_image.astype('bool')
+16 -3
View File
@@ -25,6 +25,19 @@ class TestSimpleImage():
image = np.float64(self.image)
assert 2 <= threshold_otsu(image) < 3
def test_threshold_adaptive_generic(self):
def func(arr):
return arr.sum() / arr.shape[0]
ref = np.array(
[[False, False, False, False, True],
[False, False, True, False, True],
[False, False, True, True, False],
[False, True, True, False, False],
[ True, True, False, False, False]]
)
out = threshold_adaptive(self.image, 3, method='generic', param=func)
assert_array_equal(ref, out)
def test_threshold_adaptive_gaussian(self):
ref = np.array(
[[False, False, False, False, True],
@@ -33,7 +46,7 @@ class TestSimpleImage():
[False, True, True, False, False],
[ True, True, False, False, False]]
)
out = threshold_adaptive(self.image, 3, 0, 'gaussian')
out = threshold_adaptive(self.image, 3, method='gaussian')
assert_array_equal(ref, out)
def test_threshold_adaptive_mean(self):
@@ -44,7 +57,7 @@ class TestSimpleImage():
[False, True, True, False, False],
[ True, True, False, False, False]]
)
out = threshold_adaptive(self.image, 3, 0, 'mean')
out = threshold_adaptive(self.image, 3, method='mean')
assert_array_equal(ref, out)
def test_threshold_adaptive_median(self):
@@ -55,7 +68,7 @@ class TestSimpleImage():
[False, False, True, True, False],
[False, True, False, False, False]]
)
out = threshold_adaptive(self.image, 3, 0, 'median')
out = threshold_adaptive(self.image, 3, method='median')
assert_array_equal(ref, out)
+16 -8
View File
@@ -7,12 +7,14 @@ from ._thresholding import _threshold_adaptive
__all__ = ['threshold_otsu', 'threshold_adaptive']
def threshold_adaptive(image, block_size, offset, method='gaussian'):
def threshold_adaptive(image, block_size, method='gaussian', offset=0,
mode='reflect', param=None):
"""Applies an adaptive threshold to an array.
Also known as local or dynamic thresholding where the threshold value is the
weighted mean for the local neighborhood of a pixel subtracted by a
constant.
constant. Alternatively the threshold can be determined dynamically by a
a given function using the 'generic' method.
Parameters
----------
@@ -21,12 +23,18 @@ def threshold_adaptive(image, block_size, offset, method='gaussian'):
block_size : int
uneven size of pixel neighborhood which is used to calculate the
threshold value (e.g. 3, 5, 7, ..., 21, ...)
offset : float
method : {'generic', 'gaussian', 'mean', 'median'}, optional
method used to determine adaptive threshold. By default the 'gaussian'
method is used.
offset : float, optional
constant subtracted from weighted mean of neighborhood to calculate
the local threshold value
method : string, optional
thresholding type which must be one of 'gaussian', 'mean' or 'median'.
By default the 'gaussian' method is used.
the local threshold value. Default offset is 0.
mode : {reflect,constant,nearest,mirror, wrap}, optional
The mode parameter determines how the array borders are handled, where
cval is the value when mode is equal to constant. Default is reflect
param : {int, function}, optional
either specify sigma for 'gaussian' method or function object for
'generic' method.
Returns
-------
@@ -40,7 +48,7 @@ def threshold_adaptive(image, block_size, offset, method='gaussian'):
"""
# not using img_as_float because offset parameter wouldn't work
image = image.astype('double')
return _threshold_adaptive(image, block_size, offset, method)
return _threshold_adaptive(image, block_size, method, offset, mode, param)
def threshold_otsu(image, nbins=256):
"""Return threshold value based on Otsu's method.