Repository: scikit-image/scikit-image

Issue:
peak_local_max fails to enforce min_distance
## Description
I am trying to extract local maxima from a distance transform (made using openCV).  When I run: coordinates = peak_local_max(distance_xform, min_distance=100, num_peaks=12) 
The following values are in coordinates: [[2775 2647] [944 1540] [919 2420] [1486 1192] [899 2099] [900 2054] [900 2055] [900 2056] [900 2057] [900 2058] [900 2059] [900 2060]]

If you look at the distance transform image there is a line one pixel wide on the horizontal axis extending from 1922 to 2228 at 900. All pixels on this line have the same  value. It seems this causes peak_local_max to ignore min_distance and pick peaks 1 pixel adjacent to each other along this line.

I am using:
Ubuntu 14.04.2 LTS
skimage 0.9.3
Python 2.7.6

[If reporting a bug, attach the entire traceback from Python.] (No traceback: functional error)
[If proposing an enhancement/new feature, provide links to related articles, reference examples, etc.]


## Way to reproduce
[If reporting a bug, please include the following important information:]
- [x] Code example
- [x] Relevant images (if any)
- [x] Operating system and version
- [x] Python version
- [x] scikit-image version (run `skimage.__version__`)

![outfile](https://cloud.githubusercontent.com/assets/1760559/24625044/9f107d42-1862-11e7-902a-629c4c78033e.jpg)


feature.peak_local_max changes labels
## Description

When **non sequential** `labels` are provided as input to `peak_local_max`, **the values get altered**.

If the function needs to update the values, it ought to **copy** the matrix.

I suppose this bug will be addresses by #4760 (there is a description "labels renumbering is avoided" in that PR).  But I didn't find any explicit description of this bug, which seems serious due to its silent nature, so I thought I should document it.

## Way to reproduce
```python
import numpy as np
from skimage import feature
```


```python
im = np.zeros([10, 10])
labels = np.zeros([10, 10]).astype(int)

label = 2
for x in range(0, 10, 2):
    for y in range(0, 10, 2):
        im[x,y] = 1
        labels[x:x+1, y:y+1] = label
        label += 2
```


```python
labels
```




    array([[ 2,  0,  4,  0,  6,  0,  8,  0, 10,  0],
           [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0],
           [12,  0, 14,  0, 16,  0, 18,  0, 20,  0],
           [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0],
           [22,  0, 24,  0, 26,  0, 28,  0, 30,  0],
           [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0],
           [32,  0, 34,  0, 36,  0, 38,  0, 40,  0],
           [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0],
           [42,  0, 44,  0, 46,  0, 48,  0, 50,  0],
           [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0]])




```python
res = feature.peak_local_max(im, labels=labels, num_peaks_per_label=1)
```


```python
labels
```




    array([[ 1,  0,  2,  0,  3,  0,  4,  0,  5,  0],
           [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0],
           [ 6,  0,  7,  0,  8,  0,  9,  0, 10,  0],
           [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0],
           [11,  0, 12,  0, 13,  0, 14,  0, 15,  0],
           [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0],
           [16,  0, 17,  0, 18,  0, 19,  0, 20,  0],
           [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0],
           [21,  0, 22,  0, 23,  0, 24,  0, 25,  0],
           [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0]])




```python
from __future__ import print_function
import sys; print(sys.version)
import platform; print(platform.platform())
import skimage; print("scikit-image version: {}".format(skimage.__version__))
import numpy; print("numpy version: {}".format(numpy.__version__))

```

    3.8.5 (default, Jul 21 2020, 10:48:26) 
    [Clang 11.0.3 (clang-1103.0.32.62)]
    macOS-10.15.7-x86_64-i386-64bit
    scikit-image version: 0.17.2
    numpy version: 1.19.2



```python

```



## Version information
```python
# Paste the output of the following python commands
from __future__ import print_function
import sys; print(sys.version)
import platform; print(platform.platform())
import skimage; print("scikit-image version: {}".format(skimage.__version__))
import numpy; print("numpy version: {}".format(numpy.__version__))
```

```python
# your output here
3.8.5 (default, Jul 21 2020, 10:48:26) 
[Clang 11.0.3 (clang-1103.0.32.62)]
macOS-10.15.7-x86_64-i386-64bit
scikit-image version: 0.17.2
numpy version: 1.19.2
```

Trivial image check for peak_local_max
## Description

`peak_local_max`, current tests for trivial images. That is, if an image is constant, and exactly constant, then it will return that no local peaks are found. If even one pixel is off, then it will return that many local peaks were found.

I think this test is too much of an edge case, and should simply be removed possibly without deprecation.

The relevant code is:

```python
    # no peak for a trivial image
    if np.all(image == image.flat[0]):
        if indices is True:
            return np.empty((0, 2), np.int)
        else:
            return out
```
(not linking to the original code since it will be obsolete soon).

I think this is *such* an edge case that it isn't worth the cost imposed on checking equality on every other user of this function.

1. Real cameras cannot actually take images with a constant value. Either dark noise, or shot noise from incomming photons will cause natural fluctuations in an image.
2. Images that are computer generated have better ways of testing if they are of a constant value.

The users that may have trivial images in their analysis pipelines should find better ways of detecting them.

We've tried to think of better ways for testing for the trivial case, but due to the amount of options that the peak_local_max function provides, it seems like it is more complicated than it seems at first glance:

https://github.com/scikit-image/scikit-image/pull/3984#issuecomment-508541246


cc: @clementkng and @brownmk
xref: https://github.com/scikit-image/scikit-image/pull/3984



Discussion:
@jfflanagan do you have the option to update skimage? That is an old version (3.5 years old; we just released 0.13), and I'm pretty sure that issue was fixed in the interim.
I switched to skimage 0.14dev and problem persists. Box positions are a little different: [[2775 2647] [944 1540] [919 2420] [1486 1192] [900 2125] [900 2050] [900 2052] [900 2053] [900 2055] [900 2056] [900 2057] [900 2058]]
Isn't `corner_peaks` what we are looking for here, or am I missing something? Can't this issue be closed then?

If we choose to remove this trivial check, I think it would be worth adding it back to corner_peaks, as I think this function can easily face trivial images if corner detectors fail.

Resolving patch:
diff --git a/skimage/_shared/coord.py b/skimage/_shared/coord.py
new file mode 100644
--- /dev/null
+++ b/skimage/_shared/coord.py
@@ -0,0 +1,51 @@
+import numpy as np
+from scipy.spatial import cKDTree, distance
+
+
+def ensure_spacing(coord, spacing=1, p_norm=np.inf):
+    """Returns a subset of coord where a minimum spacing is guaranteed.
+
+    Parameters
+    ----------
+    coord : ndarray
+        The coordinates of the considered points.
+    spacing : float
+        the maximum allowed spacing between the points.
+    p_norm : float
+        Which Minkowski p-norm to use. Should be in the range [1, inf].
+        A finite large p may cause a ValueError if overflow can occur.
+        ``inf`` corresponds to the Chebyshev distance and 2 to the
+        Euclidean distance.
+
+    Returns
+    -------
+    output : ndarray
+        A subset of coord where a minimum spacing is guaranteed.
+
+    """
+
+    output = coord
+    if len(coord):
+        # Use KDtree to find the peaks that are too close to each other
+        tree = cKDTree(coord)
+
+        indices = tree.query_ball_point(coord, r=spacing, p=p_norm)
+        rejected_peaks_indices = set()
+        for idx, candidates in enumerate(indices):
+            if idx not in rejected_peaks_indices:
+                # keep current point and the points at exactly spacing from it
+                candidates.remove(idx)
+                dist = distance.cdist([coord[idx]],
+                                      coord[candidates],
+                                      distance.minkowski,
+                                      p=p_norm).reshape(-1)
+                candidates = [c for c, d in zip(candidates, dist)
+                              if d < spacing]
+
+                # candidates.remove(keep)
+                rejected_peaks_indices.update(candidates)
+
+        # Remove the peaks that are too close to each other
+        output = np.delete(coord, tuple(rejected_peaks_indices), axis=0)
+
+    return output
diff --git a/skimage/feature/peak.py b/skimage/feature/peak.py
--- a/skimage/feature/peak.py
+++ b/skimage/feature/peak.py
@@ -2,11 +2,11 @@
 import numpy as np
 import scipy.ndimage as ndi
 from .. import measure
-from ..filters import rank_order
 from .._shared.utils import remove_arg
+from .._shared.coord import ensure_spacing
 
 
-def _get_high_intensity_peaks(image, mask, num_peaks):
+def _get_high_intensity_peaks(image, mask, num_peaks, min_distance, p_norm):
     """
     Return the highest intensity peak coordinates.
     """
@@ -15,72 +15,127 @@ def _get_high_intensity_peaks(image, mask, num_peaks):
     intensities = image[coord]
     # Highest peak first
     idx_maxsort = np.argsort(-intensities)
-    coord = np.stack(coord, axis=-1)[idx_maxsort]
-    # select num_peaks peaks
+    coord = np.transpose(coord)[idx_maxsort]
+
+    coord = ensure_spacing(coord, spacing=min_distance, p_norm=p_norm)
+
     if len(coord) > num_peaks:
         coord = coord[:num_peaks]
+
     return coord
 
 
-def _get_peak_mask(image, min_distance, footprint, threshold_abs,
-                   threshold_rel):
+def _get_peak_mask(image, footprint, threshold, mask=None):
     """
     Return the mask containing all peak candidates above thresholds.
     """
-    if footprint is not None:
-        image_max = ndi.maximum_filter(image, footprint=footprint,
-                                       mode='constant')
-    else:
-        size = 2 * min_distance + 1
-        image_max = ndi.maximum_filter(image, size=size, mode='constant')
-    mask = image == image_max
-    if threshold_rel is not None:
-        threshold = max(threshold_abs, threshold_rel * image.max())
-    else:
-        threshold = threshold_abs
-    mask &= image > threshold
-    return mask
+    if footprint.size == 1 or image.size == 1:
+        return image > threshold
 
+    image_max = ndi.maximum_filter(image, footprint=footprint,
+                                   mode='constant')
+
+    out = image == image_max
+
+    # no peak for a trivial image
+    image_is_trivial = np.all(out) if mask is None else np.all(out[mask])
+    if image_is_trivial:
+        out[:] = False
+        if mask is not None:
+            # isolated pixels in masked area are returned as peaks
+            isolated_px = np.logical_xor(mask, ndi.binary_opening(mask))
+            out[isolated_px] = True
+
+    out &= image > threshold
+    return out
+
+
+def _exclude_border(label, border_width):
+    """Set label border values to 0.
 
-def _exclude_border(mask, exclude_border):
-    """
-    Remove peaks near the borders
     """
-    # zero out the image borders
-    for i, excluded in enumerate(exclude_border):
-        if excluded == 0:
+    # zero out label borders
+    for i, width in enumerate(border_width):
+        if width == 0:
             continue
-        mask[(slice(None),) * i + (slice(None, excluded),)] = False
-        mask[(slice(None),) * i + (slice(-excluded, None),)] = False
-    return mask
+        label[(slice(None),) * i + (slice(None, width),)] = 0
+        label[(slice(None),) * i + (slice(-width, None),)] = 0
+    return label
+
+
+def _get_threshold(image, threshold_abs, threshold_rel):
+    """Return the threshold value according to an absolute and a relative
+    value.
+
+    """
+    threshold = threshold_abs if threshold_abs is not None else image.min()
+
+    if threshold_rel is not None:
+        threshold = max(threshold, threshold_rel * image.max())
+
+    return threshold
+
+
+def _get_excluded_border_width(image, min_distance, exclude_border):
+    """Return border_width values relative to a min_distance if requested.
+
+    """
+
+    if isinstance(exclude_border, bool):
+        border_width = (min_distance if exclude_border else 0,) * image.ndim
+    elif isinstance(exclude_border, int):
+        if exclude_border < 0:
+            raise ValueError("`exclude_border` cannot be a negative value")
+        border_width = (exclude_border,) * image.ndim
+    elif isinstance(exclude_border, tuple):
+        if len(exclude_border) != image.ndim:
+            raise ValueError(
+                "`exclude_border` should have the same length as the "
+                "dimensionality of the image.")
+        for exclude in exclude_border:
+            if not isinstance(exclude, int):
+                raise ValueError(
+                    "`exclude_border`, when expressed as a tuple, must only "
+                    "contain ints."
+                )
+            if exclude < 0:
+                raise ValueError(
+                    "`exclude_border` can not be a negative value")
+        border_width = exclude_border
+    else:
+        raise TypeError(
+            "`exclude_border` must be bool, int, or tuple with the same "
+            "length as the dimensionality of the image.")
+
+    return border_width
 
 
 @remove_arg("indices", changed_version="0.20")
 def peak_local_max(image, min_distance=1, threshold_abs=None,
                    threshold_rel=None, exclude_border=True, indices=True,
                    num_peaks=np.inf, footprint=None, labels=None,
-                   num_peaks_per_label=np.inf):
+                   num_peaks_per_label=np.inf, p_norm=np.inf):
     """Find peaks in an image as coordinate list or boolean mask.
 
     Peaks are the local maxima in a region of `2 * min_distance + 1`
     (i.e. peaks are separated by at least `min_distance`).
 
-    If there are multiple local maxima with identical pixel intensities
-    inside the region defined with `min_distance`,
-    the coordinates of all such pixels are returned.
-
     If both `threshold_abs` and `threshold_rel` are provided, the maximum
     of the two is chosen as the minimum intensity threshold of peaks.
 
+    .. versionchanged:: 0.18
+        Prior to version 0.18, peaks of the same height within a radius of
+        `min_distance` were all returned, but this could cause unexpected
+        behaviour. From 0.18 onwards, an arbitrary peak within the region is
+        returned. See issue gh-2592.
+
     Parameters
     ----------
     image : ndarray
         Input image.
     min_distance : int, optional
-        Minimum number of pixels separating peaks in a region of `2 *
-        min_distance + 1` (i.e. peaks are separated by at least
-        `min_distance`).
-        To find the maximum number of peaks, use `min_distance=1`.
+        The minimal allowed distance separating peaks. To find the
+        maximum number of peaks, use `min_distance=1`.
     threshold_abs : float, optional
         Minimum intensity of peaks. By default, the absolute threshold is
         the minimum intensity of the image.
@@ -110,13 +165,17 @@ def peak_local_max(image, min_distance=1, threshold_abs=None,
         return `num_peaks` peaks based on highest peak intensity.
     footprint : ndarray of bools, optional
         If provided, `footprint == 1` represents the local region within which
-        to search for peaks at every point in `image`.  Overrides
-        `min_distance`.
+        to search for peaks at every point in `image`.
     labels : ndarray of ints, optional
         If provided, each unique region `labels == value` represents a unique
         region to search for peaks. Zero is reserved for background.
     num_peaks_per_label : int, optional
         Maximum number of peaks for each label.
+    p_norm : float
+        Which Minkowski p-norm to use. Should be in the range [1, inf].
+        A finite large p may cause a ValueError if overflow can occur.
+        ``inf`` corresponds to the Chebyshev distance and 2 to the
+        Euclidean distance.
 
     Returns
     -------
@@ -169,110 +228,89 @@ def peak_local_max(image, min_distance=1, threshold_abs=None,
     >>> peak_mask[peak_idx] = True
 
     """
-    out = np.zeros_like(image, dtype=np.bool)
-
-    threshold_abs = threshold_abs if threshold_abs is not None else image.min()
-
-    if min_distance < 1:
+    if (footprint is None or footprint.size == 1) and min_distance < 1:
         warn("When min_distance < 1, peak_local_max acts as finding "
-             "image > max(threshold_abs, threshold_rel).",
+             "image > max(threshold_abs, threshold_rel * max(image)).",
              RuntimeWarning, stacklevel=2)
 
-    if footprint is not None:
+    border_width = _get_excluded_border_width(image, min_distance,
+                                              exclude_border)
+
+    threshold = _get_threshold(image, threshold_abs, threshold_rel)
+
+    if footprint is None:
+        size = 2 * min_distance + 1
+        footprint = np.ones((size, ) * image.ndim, dtype=bool)
+    else:
         footprint = np.asarray(footprint)
-        if footprint.size == 1:
-            warn("When footprint.size < 2, peak_local_max acts as finding "
-                 "image > max(threshold_abs, threshold_rel).",
-                 RuntimeWarning, stacklevel=2)
 
-    if isinstance(exclude_border, bool):
-        exclude_border = (min_distance if exclude_border else 0,) * image.ndim
-    elif isinstance(exclude_border, int):
-        if exclude_border < 0:
-            raise ValueError("`exclude_border` cannot be a negative value")
-        exclude_border = (exclude_border,) * image.ndim
-    elif isinstance(exclude_border, tuple):
-        if len(exclude_border) != image.ndim:
-            raise ValueError(
-                "`exclude_border` should have the same length as the "
-                "dimensionality of the image.")
-        for exclude in exclude_border:
-            if not isinstance(exclude, int):
-                raise ValueError(
-                    "`exclude_border`, when expressed as a tuple, must only "
-                    "contain ints."
-                )
-            if exclude < 0:
-                raise ValueError(
-                    "`exclude_border` cannot contain a negative value")
+    if labels is None:
+        # Non maximum filter
+        mask = _get_peak_mask(image, footprint, threshold)
+
+        mask = _exclude_border(mask, border_width)
+
+        # Select highest intensities (num_peaks)
+        coordinates = _get_high_intensity_peaks(image, mask,
+                                                num_peaks,
+                                                min_distance, p_norm)
+
     else:
-        raise TypeError(
-            "`exclude_border` must be bool, int, or tuple with the same "
-            "length as the dimensionality of the image.")
+        _labels = _exclude_border(labels.astype(int, casting="safe"),
+                                  border_width)
 
-    # no peak for a trivial image
-    if np.all(image == image.flat[0]):
-        if indices is True:
-            return np.empty((0, image.ndim), np.int)
+        if np.issubdtype(image.dtype, np.floating):
+            bg_val = np.finfo(image.dtype).min
         else:
-            return out
+            bg_val = np.iinfo(image.dtype).min
 
-    # In the case of labels, call ndi on each label
-    if labels is not None:
-        label_values = np.unique(labels)
-        # Reorder label values to have consecutive integers (no gaps)
-        if np.any(np.diff(label_values) != 1):
-            mask = labels >= 1
-            labels[mask] = 1 + rank_order(labels[mask])[0].astype(labels.dtype)
-        labels = labels.astype(np.int32)
+        # For each label, extract a smaller image enclosing the object of
+        # interest, identify num_peaks_per_label peaks
+        labels_peak_coord = []
 
-        # create a mask for the non-exclude region
-        inner_mask = _exclude_border(np.ones_like(labels, dtype=bool),
-                                     exclude_border)
+        for label_idx, roi in enumerate(ndi.find_objects(_labels)):
+
+            if roi is None:
+                continue
+
+            # Get roi mask
+            label_mask = labels[roi] == label_idx + 1
+            # Extract image roi
+            img_object = image[roi]
+            # Ensure masked values don't affect roi's local peaks
+            img_object[np.logical_not(label_mask)] = bg_val
+
+            mask = _get_peak_mask(img_object, footprint, threshold, label_mask)
 
-        # For each label, extract a smaller image enclosing the object of
-        # interest, identify num_peaks_per_label peaks and mark them in
-        # variable out.
-        for label_idx, obj in enumerate(ndi.find_objects(labels)):
-            img_object = image[obj] * (labels[obj] == label_idx + 1)
-            mask = _get_peak_mask(img_object, min_distance, footprint,
-                                  threshold_abs, threshold_rel)
-            if exclude_border:
-                # remove peaks fall in the exclude region
-                mask &= inner_mask[obj]
             coordinates = _get_high_intensity_peaks(img_object, mask,
-                                                    num_peaks_per_label)
-            nd_indices = tuple(coordinates.T)
-            mask.fill(False)
-            mask[nd_indices] = True
-            out[obj] += mask
-
-        if not indices and np.isinf(num_peaks):
-            return out
-
-        coordinates = _get_high_intensity_peaks(image, out, num_peaks)
-        if indices:
-            return coordinates
-        else:
-            out.fill(False)
-            nd_indices = tuple(coordinates.T)
-            out[nd_indices] = True
-            return out
+                                                    num_peaks_per_label,
+                                                    min_distance,
+                                                    p_norm)
 
-    # Non maximum filter
-    mask = _get_peak_mask(image, min_distance, footprint, threshold_abs,
-                          threshold_rel)
+            # transform coordinates in global image indices space
+            for idx, s in enumerate(roi):
+                coordinates[:, idx] += s.start
 
-    mask = _exclude_border(mask, exclude_border)
+            labels_peak_coord.append(coordinates)
+
+        if labels_peak_coord:
+            coordinates = np.vstack(labels_peak_coord)
+        else:
+            coordinates = np.empty((0, 2), dtype=int)
 
-    # Select highest intensities (num_peaks)
-    coordinates = _get_high_intensity_peaks(image, mask, num_peaks)
+        if len(coordinates) > num_peaks:
+            out = np.zeros_like(image, dtype=np.bool)
+            out[tuple(coordinates.T)] = True
+            coordinates = _get_high_intensity_peaks(image, out,
+                                                    num_peaks,
+                                                    min_distance,
+                                                    p_norm)
 
-    if indices is True:
+    if indices:
         return coordinates
     else:
-        nd_indices = tuple(coordinates.T)
-        out[nd_indices] = True
+        out = np.zeros_like(image, dtype=np.bool)
+        out[tuple(coordinates.T)] = True
         return out
 
 


Test patch:
diff --git a/skimage/_shared/tests/test_coord.py b/skimage/_shared/tests/test_coord.py
new file mode 100644
--- /dev/null
+++ b/skimage/_shared/tests/test_coord.py
@@ -0,0 +1,43 @@
+import pytest
+import numpy as np
+from scipy.spatial.distance import pdist, minkowski
+from skimage._shared.coord import ensure_spacing
+
+
+@pytest.mark.parametrize("p", [1, 2, np.inf])
+def test_ensure_spacing_trivial(p):
+    # --- Empty input
+    assert ensure_spacing([], p_norm=p) == []
+
+    # --- Verified spacing
+    coord = np.random.randn(100, 2)
+
+    # --- 0 spacing
+    assert np.array_equal(coord, ensure_spacing(coord, spacing=0, p_norm=p))
+
+    # Spacing is chosen to be half the minimum distance
+    spacing = pdist(coord, minkowski, p=p).min() * 0.5
+
+    out = ensure_spacing(coord, spacing=spacing, p_norm=p)
+
+    assert np.array_equal(coord, out)
+
+
+@pytest.mark.parametrize("ndim", [1, 2, 3, 4, 5])
+def test_ensure_spacing_nD(ndim):
+    coord = np.ones((5, ndim))
+
+    expected = np.ones((1, ndim))
+
+    assert np.array_equal(ensure_spacing(coord), expected)
+
+
+@pytest.mark.parametrize("p", [1, 2, np.inf])
+def test_ensure_spacing_p_norm(p):
+    coord = np.random.randn(100, 2)
+
+    # --- Consider the average distance btween the point as spacing
+    spacing = np.median(pdist(coord, minkowski, p))
+    out = ensure_spacing(coord, spacing=spacing, p_norm=p)
+
+    assert pdist(out, minkowski, p).min() > spacing
diff --git a/skimage/feature/tests/test_corner.py b/skimage/feature/tests/test_corner.py
--- a/skimage/feature/tests/test_corner.py
+++ b/skimage/feature/tests/test_corner.py
@@ -251,10 +251,9 @@ def test_square_image():
     im[:25, :25] = 1.
 
     # Moravec
-    results = peak_local_max(corner_moravec(im),
-                             min_distance=10, threshold_rel=0)
+    results = corner_moravec(im) > 0
     # interest points along edge
-    assert len(results) == 57
+    assert np.count_nonzero(results) == 92
 
     # Harris
     results = peak_local_max(corner_harris(im, method='k'),
@@ -327,28 +326,22 @@ def test_rotated_img():
     im_rotated = im.T
 
     # Moravec
-    results = peak_local_max(corner_moravec(im),
-                             min_distance=10, threshold_rel=0)
-    results_rotated = peak_local_max(corner_moravec(im_rotated),
-                                     min_distance=10, threshold_rel=0)
-    assert (np.sort(results[:, 0]) == np.sort(results_rotated[:, 1])).all()
-    assert (np.sort(results[:, 1]) == np.sort(results_rotated[:, 0])).all()
+    results = np.nonzero(corner_moravec(im))
+    results_rotated = np.nonzero(corner_moravec(im_rotated))
+    assert (np.sort(results[0]) == np.sort(results_rotated[1])).all()
+    assert (np.sort(results[1]) == np.sort(results_rotated[0])).all()
 
     # Harris
-    results = peak_local_max(corner_harris(im),
-                             min_distance=10, threshold_rel=0)
-    results_rotated = peak_local_max(corner_harris(im_rotated),
-                                     min_distance=10, threshold_rel=0)
-    assert (np.sort(results[:, 0]) == np.sort(results_rotated[:, 1])).all()
-    assert (np.sort(results[:, 1]) == np.sort(results_rotated[:, 0])).all()
+    results = np.nonzero(corner_harris(im))
+    results_rotated = np.nonzero(corner_harris(im_rotated))
+    assert (np.sort(results[0]) == np.sort(results_rotated[1])).all()
+    assert (np.sort(results[1]) == np.sort(results_rotated[0])).all()
 
     # Shi-Tomasi
-    results = peak_local_max(corner_shi_tomasi(im),
-                             min_distance=10, threshold_rel=0)
-    results_rotated = peak_local_max(corner_shi_tomasi(im_rotated),
-                                     min_distance=10, threshold_rel=0)
-    assert (np.sort(results[:, 0]) == np.sort(results_rotated[:, 1])).all()
-    assert (np.sort(results[:, 1]) == np.sort(results_rotated[:, 0])).all()
+    results = np.nonzero(corner_shi_tomasi(im))
+    results_rotated = np.nonzero(corner_shi_tomasi(im_rotated))
+    assert (np.sort(results[0]) == np.sort(results_rotated[1])).all()
+    assert (np.sort(results[1]) == np.sort(results_rotated[0])).all()
 
 
 def test_subpix_edge():
diff --git a/skimage/feature/tests/test_peak.py b/skimage/feature/tests/test_peak.py
--- a/skimage/feature/tests/test_peak.py
+++ b/skimage/feature/tests/test_peak.py
@@ -195,7 +195,10 @@ def test_ndarray_exclude_border(self):
         nd_image[2, 2, 2] = 1
         expected = np.zeros_like(nd_image, dtype=np.bool)
         expected[2, 2, 2] = True
-        expectedNoBorder = nd_image > 0
+        expectedNoBorder = np.zeros_like(nd_image, dtype=bool)
+        expectedNoBorder[2, 2, 2] = True
+        expectedNoBorder[0, 0, 1] = True
+        expectedNoBorder[3, 0, 0] = True
         with expected_warnings(["indices argument is deprecated"]):
             result = peak.peak_local_max(nd_image, min_distance=2,
                                          exclude_border=2, indices=False)
@@ -222,7 +225,7 @@ def test_ndarray_exclude_border(self):
             assert_equal(
                 peak.peak_local_max(nd_image,
                                     exclude_border=False, indices=False),
-                expectedNoBorder,
+                nd_image.astype(bool)
             )
 
     def test_empty(self):
@@ -238,7 +241,7 @@ def test_empty(self):
     def test_empty_non2d_indices(self):
         image = np.zeros((10, 10, 10))
         result = peak.peak_local_max(image,
-                                     footprint=np.ones((3, 3), bool),
+                                     footprint=np.ones((3, 3, 3), bool),
                                      min_distance=1, threshold_rel=0,
                                      exclude_border=False)
         assert result.shape == (0, image.ndim)
@@ -357,16 +360,14 @@ def test_disk(self):
         '''
         image = np.random.uniform(size=(10, 20))
         footprint = np.array([[1]])
-        with expected_warnings(["indices argument is deprecated",
-                                "When footprint.size < 2"]):
-            result = peak.peak_local_max(image, labels=np.ones((10, 20)),
+        with expected_warnings(["indices argument is deprecated"]):
+            result = peak.peak_local_max(image, labels=np.ones((10, 20), int),
                                          footprint=footprint,
                                          min_distance=1, threshold_rel=0,
                                          threshold_abs=-1, indices=False,
                                          exclude_border=False)
         assert np.all(result)
-        with expected_warnings(["indices argument is deprecated",
-                                "When footprint.size < 2"]):
+        with expected_warnings(["indices argument is deprecated"]):
             result = peak.peak_local_max(image, footprint=footprint,
                                          threshold_abs=-1,
                                          indices=False,
@@ -531,7 +532,7 @@ def test_peaks_in_contact(self):
         image[y1, x1] = i1
         image[y2, x2] = i2
         out = peak._prominent_peaks(image, min_xdistance=3,
-                                   min_ydistance=3,)
+                                    min_ydistance=3,)
         assert_equal(out[0], np.array((i1,)))
         assert_equal(out[1], np.array((x1,)))
         assert_equal(out[2], np.array((y1,)))
@@ -540,13 +541,13 @@ def test_input_labels_unmodified(self):
         image = np.zeros((10, 20))
         labels = np.zeros((10, 20), int)
         image[5, 5] = 1
-        labels[5, 5] = 1
+        labels[5, 5] = 3
         labelsin = labels.copy()
         with expected_warnings(["indices argument is deprecated"]):
-            result = peak.peak_local_max(image, labels=labels,
-                                         footprint=np.ones((3, 3), bool),
-                                         min_distance=1, threshold_rel=0,
-                                         indices=False, exclude_border=False)
+            peak.peak_local_max(image, labels=labels,
+                                footprint=np.ones((3, 3), bool),
+                                min_distance=1, threshold_rel=0,
+                                indices=False, exclude_border=False)
         assert np.all(labels == labelsin)
 
     def test_many_objects(self):