Repository: scikit-image/scikit-image

Issue:
feature.peak_local_max modifies input image
## Description
Hi
I am processing a float image (linked below) to detect peaks.
When i call `feature.peak_local_max` without defining labels everything is fine. If I define labels the call to `feature.peak_local_max` causes a change in the intensity values (adding/changing the value of some pixels to `1.7976931348623157e+308`) of the image processed. I can replicate the issue with `skimage 0.18.0 and 0.18.1` however everything works fine with `skimage 0.17.2`. Please let me know if I missed anything because I cannot wrap my head around the issue. Thanks!

<!--
(Note: for guidance on how to use `scikit-image`, please post instead on https://forum.image.sc/tag/scikit-image)
-->

## File to reproduce the issue
img_path_dropbox = [get image from here](https://www.dropbox.com/s/j5jkl9h2zfu0oi6/img_and_metadata.pkl?dl=0)

## Way to reproduce
```python

from skimage import feature, measure
from scipy import ndimage as nd

def test(img):
    print(f'min value original: {img.min()}')
    thr = 2.208
    img_mask = img>thr
        
    labels = nd.label(img_mask)[0]
            
    properties = measure.regionprops(labels)
                
    for ob in properties:
        if ob.area< 10 or ob.area>600:
            img_mask[ob.coords[:,0],ob.coords[:,1]]=0

    labels = nd.label(img_mask)[0]
    properties = measure.regionprops(labels,intensity_image=img)

    selected_peaks = feature.peak_local_max(img, min_distance=5, 
                            threshold_abs=thr, exclude_border=True, 
                            footprint=None,num_peaks_per_label=1)

    print(f'min value after peak calling no labels: {img.min()}')

    selected_peaks = feature.peak_local_max(img, min_distance=5, 
                            threshold_abs=thr, exclude_border=True, 
                            footprint=None, labels=labels,num_peaks_per_label=1)
    
    print(f'min value after peak calling using labels: {img.min()}')
    return selected_peaks


img, meta = pickle.load(open(path-to-img ,'rb'))
pk = test(img, thr)

# RESULT 
# min value original: 0.2976309153266585
# min value after peak calling no labels: 0.2976309153266585
# min value after peak calling using labels: -1.7976931348623157e+308

```


## Version information
```python
3.8.2 (default, May  7 2020, 20:00:49)
[GCC 7.3.0]
Linux-3.10.0-957.1.3.el7.x86_64-x86_64-with-glibc2.10
scikit-image version: 0.18.0
# same error in scikit-image version: 0.18.1
# everything works fine in version 0.17.2
numpy version: 1.19.4

```



Discussion:
Thank you @simone-codeluppi for the report. I confirm the bug, I can reproduce :confounded: 

Resolving patch:
diff --git a/skimage/feature/peak.py b/skimage/feature/peak.py
--- a/skimage/feature/peak.py
+++ b/skimage/feature/peak.py
@@ -281,7 +281,7 @@ def peak_local_max(image, min_distance=1, threshold_abs=None,
             # Get roi mask
             label_mask = labels[roi] == label_idx + 1
             # Extract image roi
-            img_object = image[roi]
+            img_object = image[roi].copy()
             # Ensure masked values don't affect roi's local peaks
             img_object[np.logical_not(label_mask)] = bg_val
 


Test patch:
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
@@ -11,6 +11,7 @@
 
 np.random.seed(21)
 
+
 class TestPeakLocalMax():
     def test_trivial_case(self):
         trivial = np.zeros((25, 25))
@@ -82,7 +83,8 @@ def test_num_peaks(self):
         image[1, 5] = 12
         image[3, 5] = 8
         image[5, 3] = 7
-        assert len(peak.peak_local_max(image, min_distance=1, threshold_abs=0)) == 5
+        assert len(peak.peak_local_max(image, min_distance=1,
+                                       threshold_abs=0)) == 5
         peaks_limited = peak.peak_local_max(
             image, min_distance=1, threshold_abs=0, num_peaks=2)
         assert len(peaks_limited) == 2
@@ -111,7 +113,6 @@ def test_num_peaks_and_labels(self):
             image, min_distance=1, threshold_abs=0, labels=labels, num_peaks=2)
         assert len(peaks_limited) == 2
 
-
     def test_num_peaks_tot_vs_labels_4quadrants(self):
         np.random.seed(21)
         image = np.random.uniform(size=(20, 30))
@@ -133,12 +134,11 @@ def test_num_peaks_tot_vs_labels_4quadrants(self):
                                      num_peaks_per_label=2)
         assert len(result) == 2
 
-
     def test_num_peaks3D(self):
         # Issue 1354: the old code only hold for 2D arrays
         # and this code would die with IndexError
         image = np.zeros((10, 10, 100))
-        image[5,5,::5] = np.arange(20)
+        image[5, 5, ::5] = np.arange(20)
         peaks_limited = peak.peak_local_max(image, min_distance=1, num_peaks=2)
         assert len(peaks_limited) == 2
 
@@ -153,7 +153,7 @@ def test_reorder_labels(self):
         for imin, imax in ((0, 20), (20, 40)):
             for jmin, jmax in ((0, 30), (30, 60)):
                 expected[imin:imax, jmin:jmax] = ndi.maximum_filter(
-                                image[imin:imax, jmin:jmax], footprint=footprint)
+                    image[imin:imax, jmin:jmax], footprint=footprint)
         expected = (expected == image)
         with expected_warnings(["indices argument is deprecated"]):
             result = peak.peak_local_max(image, labels=labels, min_distance=1,
@@ -378,12 +378,15 @@ def test_3D(self):
         image = np.zeros((30, 30, 30))
         image[15, 15, 15] = 1
         image[5, 5, 5] = 1
-        assert_equal(peak.peak_local_max(image, min_distance=10, threshold_rel=0),
-                     [[15, 15, 15]])
-        assert_equal(peak.peak_local_max(image, min_distance=6, threshold_rel=0),
-                     [[15, 15, 15]])
-        assert sorted(peak.peak_local_max(image, min_distance=10, threshold_rel=0,
-                                          exclude_border=False).tolist()) == \
+        assert_equal(
+            peak.peak_local_max(image, min_distance=10, threshold_rel=0),
+            [[15, 15, 15]])
+        assert_equal(
+            peak.peak_local_max(image, min_distance=6, threshold_rel=0),
+            [[15, 15, 15]])
+        assert sorted(
+            peak.peak_local_max(image, min_distance=10, threshold_rel=0,
+                                exclude_border=False).tolist()) == \
             [[5, 5, 5], [15, 15, 15]]
         assert sorted(peak.peak_local_max(image, min_distance=5,
                                           threshold_rel=0).tolist()) == \
@@ -393,13 +396,16 @@ def test_4D(self):
         image = np.zeros((30, 30, 30, 30))
         image[15, 15, 15, 15] = 1
         image[5, 5, 5, 5] = 1
-        assert_equal(peak.peak_local_max(image, min_distance=10, threshold_rel=0),
-                     [[15, 15, 15, 15]])
-        assert_equal(peak.peak_local_max(image, min_distance=6, threshold_rel=0),
-                     [[15, 15, 15, 15]])
-        assert sorted(peak.peak_local_max(image, min_distance=10, threshold_rel=0,
-                                          exclude_border=False).tolist()) == \
-            [[5, 5, 5, 5], [15, 15, 15, 15]]
+        assert_equal(
+            peak.peak_local_max(image, min_distance=10, threshold_rel=0),
+            [[15, 15, 15, 15]])
+        assert_equal(
+            peak.peak_local_max(image, min_distance=6, threshold_rel=0),
+            [[15, 15, 15, 15]])
+        assert sorted(
+            peak.peak_local_max(image, min_distance=10, threshold_rel=0,
+                                exclude_border=False).tolist()
+        ) == [[5, 5, 5, 5], [15, 15, 15, 15]]
         assert sorted(peak.peak_local_max(image, min_distance=5,
                                           threshold_rel=0).tolist()) == \
             [[5, 5, 5, 5], [15, 15, 15, 15]]
@@ -487,6 +493,22 @@ def test_exclude_border_errors():
         assert peak.peak_local_max(image, exclude_border=-1)
 
 
+def test_input_values_with_labels():
+    # Issue #5235: input values may be modified when labels are used
+
+    img = np.random.rand(128, 128)
+    labels = np.zeros((128, 128), int)
+
+    labels[10:20, 10:20] = 1
+    labels[12:16, 12:16] = 0
+
+    img_before = img.copy()
+
+    _ = peak.peak_local_max(img, labels=labels)
+
+    assert_equal(img, img_before)
+
+
 class TestProminentPeaks(unittest.TestCase):
     def test_isolated_peaks(self):
         image = np.zeros((15, 15))
@@ -498,7 +520,7 @@ def test_isolated_peaks(self):
         image[y2, x2] = i2
         out = peak._prominent_peaks(image)
         assert len(out[0]) == 3
-        for i, x, y in zip (out[0], out[1], out[2]):
+        for i, x, y in zip(out[0], out[1], out[2]):
             self.assertTrue(i in (i0, i1, i2))
             self.assertTrue(x in (x0, x1, x2))
             self.assertTrue(y in (y0, y1, y2))
@@ -513,12 +535,12 @@ def test_threshold(self):
         image[y2, x2] = i2
         out = peak._prominent_peaks(image, threshold=None)
         assert len(out[0]) == 3
-        for i, x, y in zip (out[0], out[1], out[2]):
+        for i, x, y in zip(out[0], out[1], out[2]):
             self.assertTrue(i in (i0, i1, i2))
             self.assertTrue(x in (x0, x1, x2))
         out = peak._prominent_peaks(image, threshold=9)
         assert len(out[0]) == 2
-        for i, x, y in zip (out[0], out[1], out[2]):
+        for i, x, y in zip(out[0], out[1], out[2]):
             self.assertTrue(i in (i0, i2))
             self.assertTrue(x in (x0, x2))
             self.assertTrue(y in (y0, y2))
@@ -526,7 +548,7 @@ def test_threshold(self):
     def test_peaks_in_contact(self):
         image = np.zeros((15, 15))
         x0, y0, i0 = (8, 8, 1)
-        x1, y1, i1 = (7, 7, 1) # prominent peak
+        x1, y1, i1 = (7, 7, 1)  # prominent peak
         x2, y2, i2 = (6, 6, 1)
         image[y0, x0] = i0
         image[y1, x1] = i1