Repository: scipy/scipy

Issue:
Many SciPy ndimage filters accept both size and footprint
Appears that many of the filters in `scipy.ndimage.filters` will accept both a `size` and a ` footprint` specified at the same time. Even if they are contradictory (e.g. smaller `size` than dims of `footprint`), this doesn't seem to raise an error. Checked and verified this problem with the ordering filters (`minimum_filter`, `median_filter`, `maximum_filter`, `rank_filter`, and `percentile_filter`), but this may extend to other filters that take `size` and ` footprint` (e.g. `generic_filter`). In these cases, it seems more appropriate to error and explain to the user that they must either set `size` or `footprint`, but not both. Example of this problem is shown below. Tested this with SciPy 0.19.0.

```python
In [1]: import numpy

In [2]: import numpy as np

In [3]: import scipy

In [4]: import scipy.ndimage

In [5]: import scipy.ndimage.filters

In [6]: import scipy.ndimage as spnd

In [7]: spnd.filters.median_filter(a, size=1, footprint=np.ones((2,2), dtype=bool))
Out[7]: 
array([[  0.,   1.,   2.,   3.,   4.,   5.],
       [  6.,   6.,   7.,   8.,   9.,  10.],
       [ 12.,  12.,  13.,  14.,  15.,  16.],
       [ 18.,  18.,  19.,  20.,  21.,  22.],
       [ 24.,  24.,  25.,  26.,  27.,  28.]])
```


Discussion:
For the record, the typical behaviour is to ignore the size if the footprint is provided.
What would the resolution of this look like? Since the typical behaviour is to ignore the size, I think a warning should be enough.
Yes, `UserWarning` seems right to me. Documentation looks correct already.

Resolving patch:
diff --git a/scipy/ndimage/_ni_docstrings.py b/scipy/ndimage/_ni_docstrings.py
--- a/scipy/ndimage/_ni_docstrings.py
+++ b/scipy/ndimage/_ni_docstrings.py
@@ -19,7 +19,7 @@
     will be created.""")
 _size_foot_doc = (
 """size : scalar or tuple, optional
-    See footprint, below
+    See footprint, below. Ignored if footprint is given.
 footprint : array, optional
     Either `size` or `footprint` must be defined.  `size` gives
     the shape that is taken from the input array, at every element
@@ -30,7 +30,7 @@
     to ``footprint=np.ones((n,m))``.  We adjust `size` to the number
     of dimensions of the input array, so that, if the input array is
     shape (10,10,10), and `size` is 2, then the actual size used is
-    (2,2,2).""")
+    (2,2,2). When `footprint` is given, `size` is ignored.""")
 _mode_doc = (
 """mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional
     The `mode` parameter determines how the input array is extended
diff --git a/scipy/ndimage/filters.py b/scipy/ndimage/filters.py
--- a/scipy/ndimage/filters.py
+++ b/scipy/ndimage/filters.py
@@ -29,6 +29,7 @@
 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 from __future__ import division, print_function, absolute_import
+import warnings
 
 import math
 import numpy
@@ -951,6 +952,8 @@ def maximum_filter1d(input, size, axis=-1, output=None,
 
 def _min_or_max_filter(input, size, footprint, structure, output, mode,
                        cval, origin, minimum):
+    if (size is not None) and (footprint is not None):
+        warnings.warn("ignoring size because footprint is set", UserWarning, stacklevel=3)
     if structure is None:
         if footprint is None:
             if size is None:
@@ -1091,6 +1094,8 @@ def maximum_filter(input, size=None, footprint=None, output=None,
 @_ni_docstrings.docfiller
 def _rank_filter(input, rank, size=None, footprint=None, output=None,
                  mode="reflect", cval=0.0, origin=0, operation='rank'):
+    if (size is not None) and (footprint is not None):
+        warnings.warn("ignoring size because footprint is set", UserWarning, stacklevel=3)
     input = numpy.asarray(input)
     if numpy.iscomplexobj(input):
         raise TypeError('Complex type not supported')
@@ -1396,6 +1401,8 @@ def generic_filter(input, function, size=None, footprint=None,
     not be used in new code.
 
     """
+    if (size is not None) and (footprint is not None):
+        warnings.warn("ignoring size because footprint is set", UserWarning, stacklevel=2)
     if extra_keywords is None:
         extra_keywords = {}
     input = numpy.asarray(input)
diff --git a/scipy/ndimage/morphology.py b/scipy/ndimage/morphology.py
--- a/scipy/ndimage/morphology.py
+++ b/scipy/ndimage/morphology.py
@@ -29,6 +29,7 @@
 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 from __future__ import division, print_function, absolute_import
+import warnings
 
 import numpy
 from . import _ni_support
@@ -1398,6 +1399,8 @@ def grey_opening(input, size=None, footprint=None, structure=None,
     >>> # Note that the local maximum a[3,3] has disappeared
 
     """
+    if (size is not None) and (footprint is not None):
+        warnings.warn("ignoring size because footprint is set", UserWarning, stacklevel=2)
     tmp = grey_erosion(input, size, footprint, structure, None, mode,
                        cval, origin)
     return grey_dilation(tmp, size, footprint, structure, output, mode,
@@ -1479,6 +1482,8 @@ def grey_closing(input, size=None, footprint=None, structure=None,
     >>> # Note that the local minimum a[3,3] has disappeared
 
     """
+    if (size is not None) and (footprint is not None):
+        warnings.warn("ignoring size because footprint is set", UserWarning, stacklevel=2)
     tmp = grey_dilation(input, size, footprint, structure, None, mode,
                         cval, origin)
     return grey_erosion(tmp, size, footprint, structure, output, mode,
@@ -1690,6 +1695,8 @@ def white_tophat(input, size=None, footprint=None, structure=None,
     black_tophat
 
     """
+    if (size is not None) and (footprint is not None):
+        warnings.warn("ignoring size because footprint is set", UserWarning, stacklevel=2)
     tmp = grey_erosion(input, size, footprint, structure, None, mode,
                        cval, origin)
     tmp = grey_dilation(tmp, size, footprint, structure, output, mode,
@@ -1746,6 +1753,8 @@ def black_tophat(input, size=None, footprint=None,
     white_tophat, grey_opening, grey_closing
 
     """
+    if (size is not None) and (footprint is not None):
+        warnings.warn("ignoring size because footprint is set", UserWarning, stacklevel=2)
     tmp = grey_dilation(input, size, footprint, structure, None, mode,
                         cval, origin)
     tmp = grey_erosion(tmp, size, footprint, structure, output, mode,


Test patch: