Repository: scikit-image/scikit-image

Issue:
Document negative `sigma` values in `filters.gaussian_filter` are clipped to zero.
Negative sigma values have no effect; they are clipped to zero. This should be documented.



Discussion:
Proof:

``` python
import numpy as np
import matplotlib.pyplot as plt
from skimage.filters import gaussian_filter
from skimage.data import camera

# Original cameraman
cam0 = camera().astype(float)

# Apply gaussian filter with negative `sigma`
cam1 = gaussian_filter(cam0, sigma=-0.7)
cam2 = gaussian_filter(cam0, sigma=-5)

# Plot the original and difference images
fig, (ax0, ax1, ax2) = plt.subplots(ncols=3, figsize=(10, 6))

ax0.imshow(cam0, cmap='gray', interpolation='nearest')
ax1.imshow(cam0 - cam1, cmap='gray', interpolation='nearest')
ax2.imshow(cam0 - cam2, cmap='gray', interpolation='nearest')
plt.show()

# Quantitative tests
np.testing.assert_allclose(np.zeros_like(cam0), cam0 - cam1)
np.testing.assert_allclose(np.zeros_like(cam0), cam0 - cam2)
```

![image](https://cloud.githubusercontent.com/assets/2184487/8632132/bd401338-2753-11e5-94a0-229431a72340.png)

We don't clip this ourselves - just pass this parameter to a wrapped `scipy.ndimage.gaussian_filter` - so raising an issue over at scipy/scipy is likely warranted. They do not document this behavior either.

Or raise an error...?

On Fri, Jul 10, 2015 at 8:34 PM, Josh Warner notifications@github.com
wrote:

> Proof:
> 
> from skimage.filters import gaussian_filterfrom skimage.data import camera
> 
> # Original cameraman
> 
> cam0 = camera().astype(float)
> 
> # Apply gaussian filter with negative `sigma`
> 
> cam1 = gaussian_filter(cam0, sigma=-0.7)
> cam2 = gaussian_filter(cam0, sigma=-5)
> 
> # Plot the original and difference images
> 
> fig, (ax0, ax1, ax2) = plt.subplots(ncols=3, figsize=(10, 6))
> 
> ax0.imshow(cam0, cmap='gray', interpolation='nearest')
> ax1.imshow(cam0 - cam1, cmap='gray', interpolation='nearest')
> ax2.imshow(cam0 - cam2, cmap='gray', interpolation='nearest')
> plt.show()
> 
> # Quantitative tests
> 
> np.testing.assert_allclose(np.zeros_like(cam0), cam0 - cam1)
> np.testing.assert_allclose(np.zeros_like(cam0), cam0 - cam2)
> 
> [image: image]
> https://cloud.githubusercontent.com/assets/2184487/8632132/bd401338-2753-11e5-94a0-229431a72340.png
> 
> —
> Reply to this email directly or view it on GitHub
> https://github.com/scikit-image/scikit-image/issues/1586#issuecomment-120566690
> .

+1 for raising an error

I'll look into this.


Resolving patch:
diff --git a/skimage/filters/_gaussian.py b/skimage/filters/_gaussian.py
--- a/skimage/filters/_gaussian.py
+++ b/skimage/filters/_gaussian.py
@@ -93,6 +93,8 @@ def gaussian_filter(image, sigma, output=None, mode='nearest', cval=0,
                "3D image with last dimension of length 3.")
         warnings.warn(RuntimeWarning(msg))
         multichannel = True
+    if np.any(np.asarray(sigma) < 0.0):
+        raise ValueError("Sigma values less than zero are not valid")
     if multichannel:
         # do not filter across channels
         if not isinstance(sigma, coll.Iterable):


Test patch:
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
@@ -1,8 +1,18 @@
 import numpy as np
+from numpy.testing import assert_raises
 from skimage.filters._gaussian import gaussian_filter
 from skimage._shared._warnings import expected_warnings
 
 
+def test_negative_sigma():
+    a = np.zeros((3, 3))
+    a[1, 1] = 1.
+    assert_raises(ValueError, gaussian_filter, a, sigma=-1.0)
+    assert_raises(ValueError, gaussian_filter, a, sigma=[-1.0, 1.0])
+    assert_raises(ValueError, gaussian_filter, a,
+                  sigma=np.asarray([-1.0, 1.0]))
+
+
 def test_null_sigma():
     a = np.zeros((3, 3))
     a[1, 1] = 1.