Repository: scikit-image/scikit-image

Issue:
Missing cval parameter in threshold_local
## Description
Maybe I am missing something, but I cannot set the `cval` parameter when using mode `constant` with `threshold_local`.

`TypeError: threshold_local() got an unexpected keyword argument 'cval'`

## Way to reproduce
Tried with both v0.13.1 and v0.14.0. It is missing also from the API documentation of 0.15dev so I am not sure if it got fixed yet (if a bug at all) and it does not appear in the source code either.

Thanks! :)




Discussion:
You are absolutely right, we don't currently allow passing cval through to the underlying padding function!

Incidentally, you can infer what arguments are allowed from the documentation:
http://scikit-image.org/docs/dev/api/skimage.filters.html#threshold-local

If you look at the function signature, there is no mention of cval.

Would you like to submit a pull request adding the argument? Otherwise we will get to this eventually, but I don't know when.
Good to know. If I have time to work on this over the weekend I will set up a pull request. Thanks for the prompt reply! :)

Resolving patch:
diff --git a/skimage/filters/thresholding.py b/skimage/filters/thresholding.py
--- a/skimage/filters/thresholding.py
+++ b/skimage/filters/thresholding.py
@@ -135,7 +135,7 @@ def wrapper(im):
 
 
 def threshold_local(image, block_size, method='gaussian', offset=0,
-                    mode='reflect', param=None):
+                    mode='reflect', param=None, cval=0):
     """Compute a threshold mask image based on local pixel neighborhood.
 
     Also known as adaptive or dynamic thresholding. The threshold value is
@@ -173,6 +173,8 @@ def threshold_local(image, block_size, method='gaussian', offset=0,
         'generic' method. This functions takes the flat array of local
         neighbourhood as a single argument and returns the calculated
         threshold for the centre pixel.
+    cval : float, optional
+        Value to fill past edges of input if mode is 'constant'.
 
     Returns
     -------
@@ -200,22 +202,28 @@ def threshold_local(image, block_size, method='gaussian', offset=0,
     thresh_image = np.zeros(image.shape, 'double')
     if method == 'generic':
         ndi.generic_filter(image, param, block_size,
-                           output=thresh_image, mode=mode)
+                           output=thresh_image, mode=mode, cval=cval)
     elif method == 'gaussian':
         if param is None:
             # automatically determine sigma which covers > 99% of distribution
             sigma = (block_size - 1) / 6.0
         else:
             sigma = param
-        ndi.gaussian_filter(image, sigma, output=thresh_image, mode=mode)
+        ndi.gaussian_filter(image, sigma, output=thresh_image, mode=mode,
+                            cval=cval)
     elif method == 'mean':
         mask = 1. / block_size * np.ones((block_size,))
         # separation of filters to speedup convolution
-        ndi.convolve1d(image, mask, axis=0, output=thresh_image, mode=mode)
-        ndi.convolve1d(thresh_image, mask, axis=1,
-                       output=thresh_image, mode=mode)
+        ndi.convolve1d(image, mask, axis=0, output=thresh_image, mode=mode,
+                       cval=cval)
+        ndi.convolve1d(thresh_image, mask, axis=1, output=thresh_image,
+                       mode=mode, cval=cval)
     elif method == 'median':
-        ndi.median_filter(image, block_size, output=thresh_image, mode=mode)
+        ndi.median_filter(image, block_size, output=thresh_image, mode=mode,
+                          cval=cval)
+    else:
+        raise ValueError("Invalid method specified. Please use `generic`, "
+                         "`gaussian`, `mean`, or `median`.")
 
     return thresh_image - offset
 


Test patch:
diff --git a/skimage/filters/tests/test_thresholding.py b/skimage/filters/tests/test_thresholding.py
--- a/skimage/filters/tests/test_thresholding.py
+++ b/skimage/filters/tests/test_thresholding.py
@@ -153,6 +153,16 @@ def test_threshold_local_median(self):
         out = threshold_local(self.image, 3, method='median')
         assert_equal(ref, self.image > out)
 
+    def test_threshold_local_median_constant_mode(self):
+        out = threshold_local(self.image, 3, method='median',
+                              mode='constant', cval=20)
+        expected = np.array([[20.,  1.,  3.,  4., 20.],
+                            [ 1.,  1.,  3.,  4.,  4.],
+                            [ 2.,  2.,  4.,  4.,  4.],
+                            [ 4.,  4.,  4.,  1.,  2.],
+                            [20.,  5.,  5.,  2., 20.]])
+        assert_equal(expected, out)
+
     def test_threshold_niblack(self):
         ref = np.array(
             [[False, False, False, True, True],