Repository: scikit-image/scikit-image

Issue:
unwrap_phase SegFaults when passed a masked array with all elements masked
The **unwrap_phase** function in the skimage.restoration namespace Segmentation faults when passed a masked array with all elements masked.  For example:

``` Python
import numpy as np
from skimage.restoration import unwrap_phase
image = np.ma.arange(100, dtype='float32').reshape(10, 10)
image[:] = np.ma.masked
unwrap_phase(image)
```

The Segmentation fault occurs in the find_pivot function in unwrap_2d_ljmu.c which is called from quicker_sort.  The issue is that the routine is attempting to sort a length 0 array.  A check should be added in the unwrap2D (and related functions) to see if params.no_of_edges is not 0 (or 1?)



Discussion:
I'll submit a PR to fix this issue and issue #1346 later tonight when I am not at work.


Resolving patch:
diff --git a/skimage/restoration/unwrap.py b/skimage/restoration/unwrap.py
--- a/skimage/restoration/unwrap.py
+++ b/skimage/restoration/unwrap.py
@@ -88,13 +88,14 @@ def unwrap_phase(image, wrap_around=False, seed=None):
                       'algorithm')
 
     if np.ma.isMaskedArray(image):
-        mask = np.require(image.mask, np.uint8, ['C'])
-        image = image.data
+        mask = np.require(np.ma.getmaskarray(image), np.uint8, ['C'])
     else:
         mask = np.zeros_like(image, dtype=np.uint8, order='C')
 
-    image_not_masked = np.asarray(image, dtype=np.double, order='C')
-    image_unwrapped = np.empty_like(image, dtype=np.double, order='C')
+    image_not_masked = np.asarray(
+        np.ma.getdata(image), dtype=np.double, order='C')
+    image_unwrapped = np.empty_like(image, dtype=np.double, order='C',
+                                    subok=False)
 
     if image.ndim == 1:
         unwrap_1d(image_not_masked, image_unwrapped)


Test patch:
diff --git a/skimage/restoration/tests/test_unwrap.py b/skimage/restoration/tests/test_unwrap.py
--- a/skimage/restoration/tests/test_unwrap.py
+++ b/skimage/restoration/tests/test_unwrap.py
@@ -131,6 +131,7 @@ def test_mask():
         # The end of the unwrapped array should have value equal to the
         # endpoint of the unmasked ramp
         assert_array_almost_equal_nulp(image_unwrapped[:, -1], image[i, -1])
+        assert np.ma.isMaskedArray(image_unwrapped)
 
         # Same tests, but forcing use of the 3D unwrapper by reshaping
         with expected_warnings(['length 1 dimension']):
@@ -138,7 +139,7 @@ def test_mask():
             image_wrapped_3d = image_wrapped.reshape(shape)
             image_unwrapped_3d = unwrap_phase(image_wrapped_3d)
             # remove phase shift
-            image_unwrapped_3d -= image_unwrapped_3d[0, 0, 0]  
+            image_unwrapped_3d -= image_unwrapped_3d[0, 0, 0]
         assert_array_almost_equal_nulp(image_unwrapped_3d[:, :, -1], image[i, -1])
 
 
@@ -156,5 +157,51 @@ def test_unwrap_3d_middle_wrap_around():
     unwrap = unwrap_phase(image, wrap_around=[False, True, False])
     assert np.all(unwrap == 0)
 
+
+def test_unwrap_2d_compressed_mask():
+    # ValueError when image is masked array with a compressed mask (no masked
+    # elments).  GitHub issue #1346
+    image = np.ma.zeros((10, 10))
+    unwrap = unwrap_phase(image)
+    assert np.all(unwrap == 0)
+
+
+def test_unwrap_2d_all_masked():
+    # Segmentation fault when image is masked array with a all elements masked
+    # GitHub issue #1347
+    # all elements masked
+    image = np.ma.zeros((10, 10))
+    image[:] = np.ma.masked
+    unwrap = unwrap_phase(image)
+    assert np.ma.isMaskedArray(unwrap)
+    assert np.all(unwrap.mask)
+
+    # 1 unmasked element, still zero edges
+    image = np.ma.zeros((10, 10))
+    image[:] = np.ma.masked
+    image[0, 0] = 0
+    unwrap = unwrap_phase(image)
+    assert np.ma.isMaskedArray(unwrap)
+    assert np.sum(unwrap.mask) == 99    # all but one masked
+    assert unwrap[0, 0] == 0
+
+
+def test_unwrap_3d_all_masked():
+    # all elements masked
+    image = np.ma.zeros((10, 10, 10))
+    image[:] = np.ma.masked
+    unwrap = unwrap_phase(image)
+    assert np.ma.isMaskedArray(unwrap)
+    assert np.all(unwrap.mask)
+
+    # 1 unmasked element, still zero edges
+    image = np.ma.zeros((10, 10, 10))
+    image[:] = np.ma.masked
+    image[0, 0, 0] = 0
+    unwrap = unwrap_phase(image)
+    assert np.ma.isMaskedArray(unwrap)
+    assert np.sum(unwrap.mask) == 999   # all but one masked
+    assert unwrap[0, 0, 0] == 0
+
 if __name__ == "__main__":
     run_module_suite()