Repository: scikit-image/scikit-image

Issue:
Gaussian filter does not have default value for sigma
Most functions of `scikit-image` have only one non-keyword argument, which is an input image, and all other arguments are optional keyword arguments. I would suggest that we make the `sigma` parameter of `filters.gaussian` optional (eg with the default value of 1), for more consistency between functions. In addition, having only one non-kw parameter, which is an image, makes introspection and testing easier.

What do you think?



Discussion:
And I would also add a default value for the `selem` parameter of `filters.median`.

That sounds good to me.
​

:+1: ! I thought we already had a default selem for median!


Resolving patch:
diff --git a/skimage/filters/_gaussian.py b/skimage/filters/_gaussian.py
--- a/skimage/filters/_gaussian.py
+++ b/skimage/filters/_gaussian.py
@@ -9,16 +9,16 @@
 __all__ = ['gaussian']
 
 
-def gaussian(image, sigma, output=None, mode='nearest', cval=0,
+def gaussian(image, sigma=1, output=None, mode='nearest', cval=0,
              multichannel=None):
     """Multi-dimensional Gaussian filter
 
     Parameters
     ----------
     image : array-like
-        input image (grayscale or color) to filter.
-    sigma : scalar or sequence of scalars
-        standard deviation for Gaussian kernel. The standard
+        Input image (grayscale or color) to filter.
+    sigma : scalar or sequence of scalars, optional
+        Standard deviation for Gaussian kernel. The standard
         deviations of the Gaussian filter are given for each axis as a
         sequence, or as a single number, in which case it is equal for
         all axes.
diff --git a/skimage/filters/rank/generic.py b/skimage/filters/rank/generic.py
--- a/skimage/filters/rank/generic.py
+++ b/skimage/filters/rank/generic.py
@@ -48,7 +48,9 @@
 
 """
 
+import functools
 import numpy as np
+from scipy import ndimage as ndi
 from ... import img_as_ubyte
 from ..._shared.utils import assert_nD, warn
 
@@ -127,6 +129,30 @@ def _apply_vector_per_pixel(func, image, selem, out, mask, shift_x, shift_y,
     return out
 
 
+def _default_selem(func):
+    """Decorator to add a default structuring element to morphology functions.
+
+    Parameters
+    ----------
+    func : function
+        A morphology function such as erosion, dilation, opening, closing,
+        white_tophat, or black_tophat.
+
+    Returns
+    -------
+    func_out : function
+        The function, using a default structuring element of same dimension
+        as the input image with connectivity 1.
+    """
+    @functools.wraps(func)
+    def func_out(image, selem=None, *args, **kwargs):
+        if selem is None:
+            selem = ndi.generate_binary_structure(image.ndim, image.ndim)
+        return func(image, selem=selem, *args, **kwargs)
+
+    return func_out
+
+
 def autolevel(image, selem, out=None, mask=None, shift_x=False, shift_y=False):
     """Auto-level image using local histogram.
 
@@ -374,7 +400,9 @@ def mean(image, selem, out=None, mask=None, shift_x=False, shift_y=False):
     return _apply_scalar_per_pixel(generic_cy._mean, image, selem, out=out,
                                    mask=mask, shift_x=shift_x, shift_y=shift_y)
 
-def geometric_mean(image, selem, out=None, mask=None, shift_x=False, shift_y=False):
+
+def geometric_mean(image, selem, out=None, mask=None,
+                   shift_x=False, shift_y=False):
     """Return local geometric mean of an image.
 
     Parameters
@@ -457,15 +485,18 @@ def subtract_mean(image, selem, out=None, mask=None, shift_x=False,
                                    shift_x=shift_x, shift_y=shift_y)
 
 
-def median(image, selem, out=None, mask=None, shift_x=False, shift_y=False):
+@_default_selem
+def median(image, selem=None, out=None, mask=None,
+           shift_x=False, shift_y=False):
     """Return local median of an image.
 
     Parameters
     ----------
     image : 2-D array (uint8, uint16)
         Input image.
-    selem : 2-D array
-        The neighborhood expressed as a 2-D array of 1's and 0's.
+    selem : 2-D array, optional
+        The neighborhood expressed as a 2-D array of 1's and 0's. If None, a
+        full square of size 3 is used.
     out : 2-D array (same dtype as input)
         If None, a new array is allocated.
     mask : ndarray


Test patch:
diff --git a/skimage/filters/rank/tests/test_rank.py b/skimage/filters/rank/tests/test_rank.py
--- a/skimage/filters/rank/tests/test_rank.py
+++ b/skimage/filters/rank/tests/test_rank.py
@@ -713,5 +713,14 @@ def test_windowed_histogram():
     assert larger_output.shape[2] == 5
 
 
+def test_median_default_value():
+    a = np.zeros((3, 3), dtype=np.uint8)
+    a[1] = 1
+    full_selem = np.ones((3, 3), dtype=np.uint8)
+    assert_equal(rank.median(a), rank.median(a, full_selem))
+    assert rank.median(a)[1, 1] == 0
+    assert rank.median(a, disk(1))[1, 1] == 1
+
+
 if __name__ == "__main__":
     run_module_suite()
diff --git a/skimage/filters/tests/test_gaussian.py b/skimage/filters/tests/test_gaussian.py
--- a/skimage/filters/tests/test_gaussian.py
+++ b/skimage/filters/tests/test_gaussian.py
@@ -19,6 +19,12 @@ def test_null_sigma():
     assert np.all(gaussian(a, 0) == a)
 
 
+def test_default_sigma():
+    a = np.zeros((3, 3))
+    a[1, 1] = 1.
+    assert np.all(gaussian(a) == gaussian(a, sigma=1))
+
+
 def test_energy_decrease():
     a = np.zeros((3, 3))
     a[1, 1] = 1.