Repository: scikit-image/scikit-image

Issue:
skimage.transform.resize doesn't account for NaN in the image when clipping
### Description:

`skimage.transform.resize(img, clip=True, ...)` doesn't account for NaN in the image when computing the image bounds

https://github.com/scikit-image/scikit-image/blob/c1af2bf80d2c8845bbbf06222f87e8b58a8e5c1e/skimage/transform/_warps.py#L165

Interestingly, I think it would work if the input image was simply passed to to [_clip_warp_output](https://github.com/scikit-image/scikit-image/blob/c1af2bf80d2c8845bbbf06222f87e8b58a8e5c1e/skimage/transform/_warps.py#L235) instead of the computed bounds

https://github.com/scikit-image/scikit-image/blob/c1af2bf80d2c8845bbbf06222f87e8b58a8e5c1e/skimage/transform/_warps.py#L720-L729

This is actually what skimage.transform.warp does (Note, when resize calls [warp](https://github.com/scikit-image/scikit-image/blob/c1af2bf80d2c8845bbbf06222f87e8b58a8e5c1e/skimage/transform/_warps.py#L219), it sets clip to False [Edit: actually, warp was only being called if numpy < 1.6.0; in v0.20.0, warp is no longer called by resize, in favor of ndi.zoom]).

https://github.com/scikit-image/scikit-image/blob/c1af2bf80d2c8845bbbf06222f87e8b58a8e5c1e/skimage/transform/_warps.py#L997

### Way to reproduce:

_No response_

### Version information:

```Shell
3.8.10 (default, Mar 15 2022, 12:22:08) 
[GCC 9.4.0]
Linux-6.1.9-200.fc37.x86_64-x86_64-with-glibc2.29
scikit-image version: 0.19.3
numpy version: 1.22.4
```



Discussion:
Nicely spotted @scott-vsi! If you submit a fix with a test I suspect it would be merged in short order. 😊 

Resolving patch:
diff --git a/skimage/transform/_warps.py b/skimage/transform/_warps.py
--- a/skimage/transform/_warps.py
+++ b/skimage/transform/_warps.py
@@ -162,9 +162,6 @@ def resize(image, output_shape, order=None, mode='reflect', cval=0, clip=True,
     if order > 0:
         image = convert_to_float(image, preserve_range)
 
-    # Save input value range for clip
-    img_bounds = np.array([image.min(), image.max()]) if clip else None
-
     # Translate modes used by np.pad to those used by scipy.ndimage
     ndi_mode = _to_ndimage_mode(mode)
     if anti_aliasing:
@@ -179,14 +176,16 @@ def resize(image, output_shape, order=None, mode='reflect', cval=0, clip=True,
             elif np.any((anti_aliasing_sigma > 0) & (factors <= 1)):
                 warn("Anti-aliasing standard deviation greater than zero but "
                      "not down-sampling along all axes")
-        image = ndi.gaussian_filter(image, anti_aliasing_sigma,
-                                    cval=cval, mode=ndi_mode)
+        filtered = ndi.gaussian_filter(image, anti_aliasing_sigma,
+                                       cval=cval, mode=ndi_mode)
+    else:
+        filtered = image
 
     zoom_factors = [1 / f for f in factors]
-    out = ndi.zoom(image, zoom_factors, order=order, mode=ndi_mode,
+    out = ndi.zoom(filtered, zoom_factors, order=order, mode=ndi_mode,
                    cval=cval, grid_mode=True)
 
-    _clip_warp_output(img_bounds, out, mode, cval, clip)
+    _clip_warp_output(image, out, mode, cval, clip)
 
     return out
 


Test patch:
diff --git a/skimage/transform/tests/test_warps.py b/skimage/transform/tests/test_warps.py
--- a/skimage/transform/tests/test_warps.py
+++ b/skimage/transform/tests/test_warps.py
@@ -446,10 +446,12 @@ def test_resize_clip(order, preserve_range, anti_aliasing, dtype):
     x = np.ones((5, 5), dtype=dtype)
     if dtype == np.uint8:
         x *= 255
+    else:
+        x[0, 0] = np.NaN
     resized = resize(x, (3, 3), order=order, preserve_range=preserve_range,
                      anti_aliasing=anti_aliasing)
 
-    assert resized.max() == expected_max
+    assert np.nanmax(resized) == expected_max
 
 
 @pytest.mark.parametrize('dtype', [np.float16, np.float32, np.float64])