We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
SSIM should fail unless data_range specified (especially for float)
### Description:

See https://github.com/scikit-image/scikit-image/pull/6595

### Way to reproduce:

_No response_

### Traceback or output:

_No response_

### Version information:

_No response_

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `skimage/metrics/_structural_similarity.py`
Content:
```
1 import functools
2 
3 import numpy as np
4 from scipy.ndimage import uniform_filter
5 
6 from .._shared import utils
7 from .._shared.filters import gaussian
8 from .._shared.utils import _supported_float_type, check_shape_equality, warn
9 from ..util.arraycrop import crop
10 from ..util.dtype import dtype_range
11 
12 __all__ = ['structural_similarity']
13 
14 
15 @utils.deprecate_multichannel_kwarg()
16 def structural_similarity(im1, im2,
17                           *,
18                           win_size=None, gradient=False, data_range=None,
19                           channel_axis=None, multichannel=False,
20                           gaussian_weights=False, full=False, **kwargs):
21     """
22     Compute the mean structural similarity index between two images.
23     Please pay attention to the `data_range` parameter with floating-point images.
24 
25     Parameters
26     ----------
27     im1, im2 : ndarray
28         Images. Any dimensionality with same shape.
29     win_size : int or None, optional
30         The side-length of the sliding window used in comparison. Must be an
31         odd value. If `gaussian_weights` is True, this is ignored and the
32         window size will depend on `sigma`.
33     gradient : bool, optional
34         If True, also return the gradient with respect to im2.
35     data_range : float, optional
36         The data range of the input image (distance between minimum and
37         maximum possible values). By default, this is estimated from the image
38         data type. This estimate may be wrong for floating-point image data.
39         Therefore it is recommended to always pass this value explicitly
40         (see note below).
41     channel_axis : int or None, optional
42         If None, the image is assumed to be a grayscale (single channel) image.
43         Otherwise, this parameter indicates which axis of the array corresponds
44         to channels.
45 
46         .. versionadded:: 0.19
47            ``channel_axis`` was added in 0.19.
48     multichannel : bool, optional
49         If True, treat the last dimension of the array as channels. Similarity
50         calculations are done independently for each channel then averaged.
51         This argument is deprecated: specify `channel_axis` instead.
52     gaussian_weights : bool, optional
53         If True, each patch has its mean and variance spatially weighted by a
54         normalized Gaussian kernel of width sigma=1.5.
55     full : bool, optional
56         If True, also return the full structural similarity image.
57 
58     Other Parameters
59     ----------------
60     use_sample_covariance : bool
61         If True, normalize covariances by N-1 rather than, N where N is the
62         number of pixels within the sliding window.
63     K1 : float
64         Algorithm parameter, K1 (small constant, see [1]_).
65     K2 : float
66         Algorithm parameter, K2 (small constant, see [1]_).
67     sigma : float
68         Standard deviation for the Gaussian when `gaussian_weights` is True.
69 
70     Returns
71     -------
72     mssim : float
73         The mean structural similarity index over the image.
74     grad : ndarray
75         The gradient of the structural similarity between im1 and im2 [2]_.
76         This is only returned if `gradient` is set to True.
77     S : ndarray
78         The full SSIM image.  This is only returned if `full` is set to True.
79 
80     Notes
81     -----
82     If `data_range` is not specified, the range is automatically guessed
83     based on the image data type. However for floating-point image data, this
84     estimate yields a result double the value of the desired range, as the
85     `dtype_range` in `skimage.util.dtype.py` has defined intervals from -1 to
86     +1. This yields an estimate of 2, instead of 1, which is most often
87     required when working with image data (as negative light intentsities are
88     nonsensical). In case of working with YCbCr-like color data, note that
89     these ranges are different per channel (Cb and Cr have double the range
90     of Y), so one cannot calculate a channel-averaged SSIM with a single call
91     to this function, as identical ranges are assumed for each channel.
92 
93     To match the implementation of Wang et al. [1]_, set `gaussian_weights`
94     to True, `sigma` to 1.5, `use_sample_covariance` to False, and
95     specify the `data_range` argument.
96 
97     .. versionchanged:: 0.16
98         This function was renamed from ``skimage.measure.compare_ssim`` to
99         ``skimage.metrics.structural_similarity``.
100 
101     References
102     ----------
103     .. [1] Wang, Z., Bovik, A. C., Sheikh, H. R., & Simoncelli, E. P.
104        (2004). Image quality assessment: From error visibility to
105        structural similarity. IEEE Transactions on Image Processing,
106        13, 600-612.
107        https://ece.uwaterloo.ca/~z70wang/publications/ssim.pdf,
108        :DOI:`10.1109/TIP.2003.819861`
109 
110     .. [2] Avanaki, A. N. (2009). Exact global histogram specification
111        optimized for structural similarity. Optical Review, 16, 613-621.
112        :arxiv:`0901.0065`
113        :DOI:`10.1007/s10043-009-0119-z`
114 
115     """
116     check_shape_equality(im1, im2)
117     float_type = _supported_float_type(im1.dtype)
118 
119     if channel_axis is not None:
120         # loop over channels
121         args = dict(win_size=win_size,
122                     gradient=gradient,
123                     data_range=data_range,
124                     channel_axis=None,
125                     gaussian_weights=gaussian_weights,
126                     full=full)
127         args.update(kwargs)
128         nch = im1.shape[channel_axis]
129         mssim = np.empty(nch, dtype=float_type)
130 
131         if gradient:
132             G = np.empty(im1.shape, dtype=float_type)
133         if full:
134             S = np.empty(im1.shape, dtype=float_type)
135         channel_axis = channel_axis % im1.ndim
136         _at = functools.partial(utils.slice_at_axis, axis=channel_axis)
137         for ch in range(nch):
138             ch_result = structural_similarity(im1[_at(ch)],
139                                               im2[_at(ch)], **args)
140             if gradient and full:
141                 mssim[ch], G[_at(ch)], S[_at(ch)] = ch_result
142             elif gradient:
143                 mssim[ch], G[_at(ch)] = ch_result
144             elif full:
145                 mssim[ch], S[_at(ch)] = ch_result
146             else:
147                 mssim[ch] = ch_result
148         mssim = mssim.mean()
149         if gradient and full:
150             return mssim, G, S
151         elif gradient:
152             return mssim, G
153         elif full:
154             return mssim, S
155         else:
156             return mssim
157 
158     K1 = kwargs.pop('K1', 0.01)
159     K2 = kwargs.pop('K2', 0.03)
160     sigma = kwargs.pop('sigma', 1.5)
161     if K1 < 0:
162         raise ValueError("K1 must be positive")
163     if K2 < 0:
164         raise ValueError("K2 must be positive")
165     if sigma < 0:
166         raise ValueError("sigma must be positive")
167     use_sample_covariance = kwargs.pop('use_sample_covariance', True)
168 
169     if gaussian_weights:
170         # Set to give an 11-tap filter with the default sigma of 1.5 to match
171         # Wang et. al. 2004.
172         truncate = 3.5
173 
174     if win_size is None:
175         if gaussian_weights:
176             # set win_size used by crop to match the filter size
177             r = int(truncate * sigma + 0.5)  # radius as in ndimage
178             win_size = 2 * r + 1
179         else:
180             win_size = 7   # backwards compatibility
181 
182     if np.any((np.asarray(im1.shape) - win_size) < 0):
183         raise ValueError(
184             'win_size exceeds image extent. '
185             'Either ensure that your images are '
186             'at least 7x7; or pass win_size explicitly '
187             'in the function call, with an odd value '
188             'less than or equal to the smaller side of your '
189             'images. If your images are multichannel '
190             '(with color channels), set channel_axis to '
191             'the axis number corresponding to the channels.')
192 
193     if not (win_size % 2 == 1):
194         raise ValueError('Window size must be odd.')
195 
196     if data_range is None:
197         if im1.dtype != im2.dtype:
198             warn("Inputs have mismatched dtype.  Setting data_range based on "
199                  "im1.dtype.", stacklevel=2)
200         dmin, dmax = dtype_range[im1.dtype.type]
201         data_range = dmax - dmin
202 
203     ndim = im1.ndim
204 
205     if gaussian_weights:
206         filter_func = gaussian
207         filter_args = {'sigma': sigma, 'truncate': truncate, 'mode': 'reflect'}
208     else:
209         filter_func = uniform_filter
210         filter_args = {'size': win_size}
211 
212     # ndimage filters need floating point data
213     im1 = im1.astype(float_type, copy=False)
214     im2 = im2.astype(float_type, copy=False)
215 
216     NP = win_size ** ndim
217 
218     # filter has already normalized by NP
219     if use_sample_covariance:
220         cov_norm = NP / (NP - 1)  # sample covariance
221     else:
222         cov_norm = 1.0  # population covariance to match Wang et. al. 2004
223 
224     # compute (weighted) means
225     ux = filter_func(im1, **filter_args)
226     uy = filter_func(im2, **filter_args)
227 
228     # compute (weighted) variances and covariances
229     uxx = filter_func(im1 * im1, **filter_args)
230     uyy = filter_func(im2 * im2, **filter_args)
231     uxy = filter_func(im1 * im2, **filter_args)
232     vx = cov_norm * (uxx - ux * ux)
233     vy = cov_norm * (uyy - uy * uy)
234     vxy = cov_norm * (uxy - ux * uy)
235 
236     R = data_range
237     C1 = (K1 * R) ** 2
238     C2 = (K2 * R) ** 2
239 
240     A1, A2, B1, B2 = ((2 * ux * uy + C1,
241                        2 * vxy + C2,
242                        ux ** 2 + uy ** 2 + C1,
243                        vx + vy + C2))
244     D = B1 * B2
245     S = (A1 * A2) / D
246 
247     # to avoid edge effects will ignore filter radius strip around edges
248     pad = (win_size - 1) // 2
249 
250     # compute (weighted) mean of ssim. Use float64 for accuracy.
251     mssim = crop(S, pad).mean(dtype=np.float64)
252 
253     if gradient:
254         # The following is Eqs. 7-8 of Avanaki 2009.
255         grad = filter_func(A1 / D, **filter_args) * im1
256         grad += filter_func(-S / B2, **filter_args) * im2
257         grad += filter_func((ux * (A2 - A1) - uy * (B2 - B1) * S) / D,
258                             **filter_args)
259         grad *= (2 / im1.size)
260 
261         if full:
262             return mssim, grad, S
263         else:
264             return mssim, grad
265     else:
266         if full:
267             return mssim, S
268         else:
269             return mssim
270
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/skimage/metrics/_structural_similarity.py b/skimage/metrics/_structural_similarity.py
--- a/skimage/metrics/_structural_similarity.py
+++ b/skimage/metrics/_structural_similarity.py
@@ -194,11 +194,22 @@
         raise ValueError('Window size must be odd.')
 
     if data_range is None:
+        if (np.issubdtype(im1.dtype, np.floating) or
+            np.issubdtype(im2.dtype, np.floating)):
+            raise ValueError(
+                'Since image dtype is floating point, you must specify '
+                'the data_range parameter. Please read the documentation '
+                'carefully (including the note). It is recommended that '
+                'you always specify the data_range anyway.')
         if im1.dtype != im2.dtype:
-            warn("Inputs have mismatched dtype.  Setting data_range based on "
-                 "im1.dtype.", stacklevel=2)
+            warn("Inputs have mismatched dtypes. Setting data_range based on im1.dtype.",
+                 stacklevel=2)
         dmin, dmax = dtype_range[im1.dtype.type]
         data_range = dmax - dmin
+        if np.issubdtype(im1.dtype, np.integer) and (im1.dtype != np.uint8):
+            warn("Setting data_range based on im1.dtype. " +
+                 ("data_range = %.0f. " % data_range) +
+                 "Please specify data_range explicitly to avoid mistakes.", stacklevel=2)
 
     ndim = im1.ndim

{"golden_diff": "diff --git a/skimage/metrics/_structural_similarity.py b/skimage/metrics/_structural_similarity.py\n--- a/skimage/metrics/_structural_similarity.py\n+++ b/skimage/metrics/_structural_similarity.py\n@@ -194,11 +194,22 @@\n         raise ValueError('Window size must be odd.')\n \n     if data_range is None:\n+        if (np.issubdtype(im1.dtype, np.floating) or\n+            np.issubdtype(im2.dtype, np.floating)):\n+            raise ValueError(\n+                'Since image dtype is floating point, you must specify '\n+                'the data_range parameter. Please read the documentation '\n+                'carefully (including the note). It is recommended that '\n+                'you always specify the data_range anyway.')\n         if im1.dtype != im2.dtype:\n-            warn(\"Inputs have mismatched dtype.  Setting data_range based on \"\n-                 \"im1.dtype.\", stacklevel=2)\n+            warn(\"Inputs have mismatched dtypes. Setting data_range based on im1.dtype.\",\n+                 stacklevel=2)\n         dmin, dmax = dtype_range[im1.dtype.type]\n         data_range = dmax - dmin\n+        if np.issubdtype(im1.dtype, np.integer) and (im1.dtype != np.uint8):\n+            warn(\"Setting data_range based on im1.dtype. \" +\n+                 (\"data_range = %.0f. \" % data_range) +\n+                 \"Please specify data_range explicitly to avoid mistakes.\", stacklevel=2)\n \n     ndim = im1.ndim\n", "issue": "SSIM should fail unless data_range specified (especially for float)\n### Description:\n\nSee https://github.com/scikit-image/scikit-image/pull/6595\n\n### Way to reproduce:\n\n_No response_\n\n### Traceback or output:\n\n_No response_\n\n### Version information:\n\n_No response_\n", "before_files": [{"content": "import functools\n\nimport numpy as np\nfrom scipy.ndimage import uniform_filter\n\nfrom .._shared import utils\nfrom .._shared.filters import gaussian\nfrom .._shared.utils import _supported_float_type, check_shape_equality, warn\nfrom ..util.arraycrop import crop\nfrom ..util.dtype import dtype_range\n\n__all__ = ['structural_similarity']\n\n\n@utils.deprecate_multichannel_kwarg()\ndef structural_similarity(im1, im2,\n                          *,\n                          win_size=None, gradient=False, data_range=None,\n                          channel_axis=None, multichannel=False,\n                          gaussian_weights=False, full=False, **kwargs):\n    \"\"\"\n    Compute the mean structural similarity index between two images.\n    Please pay attention to the `data_range` parameter with floating-point images.\n\n    Parameters\n    ----------\n    im1, im2 : ndarray\n        Images. Any dimensionality with same shape.\n    win_size : int or None, optional\n        The side-length of the sliding window used in comparison. Must be an\n        odd value. If `gaussian_weights` is True, this is ignored and the\n        window size will depend on `sigma`.\n    gradient : bool, optional\n        If True, also return the gradient with respect to im2.\n    data_range : float, optional\n        The data range of the input image (distance between minimum and\n        maximum possible values). By default, this is estimated from the image\n        data type. This estimate may be wrong for floating-point image data.\n        Therefore it is recommended to always pass this value explicitly\n        (see note below).\n    channel_axis : int or None, optional\n        If None, the image is assumed to be a grayscale (single channel) image.\n        Otherwise, this parameter indicates which axis of the array corresponds\n        to channels.\n\n        .. versionadded:: 0.19\n           ``channel_axis`` was added in 0.19.\n    multichannel : bool, optional\n        If True, treat the last dimension of the array as channels. Similarity\n        calculations are done independently for each channel then averaged.\n        This argument is deprecated: specify `channel_axis` instead.\n    gaussian_weights : bool, optional\n        If True, each patch has its mean and variance spatially weighted by a\n        normalized Gaussian kernel of width sigma=1.5.\n    full : bool, optional\n        If True, also return the full structural similarity image.\n\n    Other Parameters\n    ----------------\n    use_sample_covariance : bool\n        If True, normalize covariances by N-1 rather than, N where N is the\n        number of pixels within the sliding window.\n    K1 : float\n        Algorithm parameter, K1 (small constant, see [1]_).\n    K2 : float\n        Algorithm parameter, K2 (small constant, see [1]_).\n    sigma : float\n        Standard deviation for the Gaussian when `gaussian_weights` is True.\n\n    Returns\n    -------\n    mssim : float\n        The mean structural similarity index over the image.\n    grad : ndarray\n        The gradient of the structural similarity between im1 and im2 [2]_.\n        This is only returned if `gradient` is set to True.\n    S : ndarray\n        The full SSIM image.  This is only returned if `full` is set to True.\n\n    Notes\n    -----\n    If `data_range` is not specified, the range is automatically guessed\n    based on the image data type. However for floating-point image data, this\n    estimate yields a result double the value of the desired range, as the\n    `dtype_range` in `skimage.util.dtype.py` has defined intervals from -1 to\n    +1. This yields an estimate of 2, instead of 1, which is most often\n    required when working with image data (as negative light intentsities are\n    nonsensical). In case of working with YCbCr-like color data, note that\n    these ranges are different per channel (Cb and Cr have double the range\n    of Y), so one cannot calculate a channel-averaged SSIM with a single call\n    to this function, as identical ranges are assumed for each channel.\n\n    To match the implementation of Wang et al. [1]_, set `gaussian_weights`\n    to True, `sigma` to 1.5, `use_sample_covariance` to False, and\n    specify the `data_range` argument.\n\n    .. versionchanged:: 0.16\n        This function was renamed from ``skimage.measure.compare_ssim`` to\n        ``skimage.metrics.structural_similarity``.\n\n    References\n    ----------\n    .. [1] Wang, Z., Bovik, A. C., Sheikh, H. R., & Simoncelli, E. P.\n       (2004). Image quality assessment: From error visibility to\n       structural similarity. IEEE Transactions on Image Processing,\n       13, 600-612.\n       https://ece.uwaterloo.ca/~z70wang/publications/ssim.pdf,\n       :DOI:`10.1109/TIP.2003.819861`\n\n    .. [2] Avanaki, A. N. (2009). Exact global histogram specification\n       optimized for structural similarity. Optical Review, 16, 613-621.\n       :arxiv:`0901.0065`\n       :DOI:`10.1007/s10043-009-0119-z`\n\n    \"\"\"\n    check_shape_equality(im1, im2)\n    float_type = _supported_float_type(im1.dtype)\n\n    if channel_axis is not None:\n        # loop over channels\n        args = dict(win_size=win_size,\n                    gradient=gradient,\n                    data_range=data_range,\n                    channel_axis=None,\n                    gaussian_weights=gaussian_weights,\n                    full=full)\n        args.update(kwargs)\n        nch = im1.shape[channel_axis]\n        mssim = np.empty(nch, dtype=float_type)\n\n        if gradient:\n            G = np.empty(im1.shape, dtype=float_type)\n        if full:\n            S = np.empty(im1.shape, dtype=float_type)\n        channel_axis = channel_axis % im1.ndim\n        _at = functools.partial(utils.slice_at_axis, axis=channel_axis)\n        for ch in range(nch):\n            ch_result = structural_similarity(im1[_at(ch)],\n                                              im2[_at(ch)], **args)\n            if gradient and full:\n                mssim[ch], G[_at(ch)], S[_at(ch)] = ch_result\n            elif gradient:\n                mssim[ch], G[_at(ch)] = ch_result\n            elif full:\n                mssim[ch], S[_at(ch)] = ch_result\n            else:\n                mssim[ch] = ch_result\n        mssim = mssim.mean()\n        if gradient and full:\n            return mssim, G, S\n        elif gradient:\n            return mssim, G\n        elif full:\n            return mssim, S\n        else:\n            return mssim\n\n    K1 = kwargs.pop('K1', 0.01)\n    K2 = kwargs.pop('K2', 0.03)\n    sigma = kwargs.pop('sigma', 1.5)\n    if K1 < 0:\n        raise ValueError(\"K1 must be positive\")\n    if K2 < 0:\n        raise ValueError(\"K2 must be positive\")\n    if sigma < 0:\n        raise ValueError(\"sigma must be positive\")\n    use_sample_covariance = kwargs.pop('use_sample_covariance', True)\n\n    if gaussian_weights:\n        # Set to give an 11-tap filter with the default sigma of 1.5 to match\n        # Wang et. al. 2004.\n        truncate = 3.5\n\n    if win_size is None:\n        if gaussian_weights:\n            # set win_size used by crop to match the filter size\n            r = int(truncate * sigma + 0.5)  # radius as in ndimage\n            win_size = 2 * r + 1\n        else:\n            win_size = 7   # backwards compatibility\n\n    if np.any((np.asarray(im1.shape) - win_size) < 0):\n        raise ValueError(\n            'win_size exceeds image extent. '\n            'Either ensure that your images are '\n            'at least 7x7; or pass win_size explicitly '\n            'in the function call, with an odd value '\n            'less than or equal to the smaller side of your '\n            'images. If your images are multichannel '\n            '(with color channels), set channel_axis to '\n            'the axis number corresponding to the channels.')\n\n    if not (win_size % 2 == 1):\n        raise ValueError('Window size must be odd.')\n\n    if data_range is None:\n        if im1.dtype != im2.dtype:\n            warn(\"Inputs have mismatched dtype.  Setting data_range based on \"\n                 \"im1.dtype.\", stacklevel=2)\n        dmin, dmax = dtype_range[im1.dtype.type]\n        data_range = dmax - dmin\n\n    ndim = im1.ndim\n\n    if gaussian_weights:\n        filter_func = gaussian\n        filter_args = {'sigma': sigma, 'truncate': truncate, 'mode': 'reflect'}\n    else:\n        filter_func = uniform_filter\n        filter_args = {'size': win_size}\n\n    # ndimage filters need floating point data\n    im1 = im1.astype(float_type, copy=False)\n    im2 = im2.astype(float_type, copy=False)\n\n    NP = win_size ** ndim\n\n    # filter has already normalized by NP\n    if use_sample_covariance:\n        cov_norm = NP / (NP - 1)  # sample covariance\n    else:\n        cov_norm = 1.0  # population covariance to match Wang et. al. 2004\n\n    # compute (weighted) means\n    ux = filter_func(im1, **filter_args)\n    uy = filter_func(im2, **filter_args)\n\n    # compute (weighted) variances and covariances\n    uxx = filter_func(im1 * im1, **filter_args)\n    uyy = filter_func(im2 * im2, **filter_args)\n    uxy = filter_func(im1 * im2, **filter_args)\n    vx = cov_norm * (uxx - ux * ux)\n    vy = cov_norm * (uyy - uy * uy)\n    vxy = cov_norm * (uxy - ux * uy)\n\n    R = data_range\n    C1 = (K1 * R) ** 2\n    C2 = (K2 * R) ** 2\n\n    A1, A2, B1, B2 = ((2 * ux * uy + C1,\n                       2 * vxy + C2,\n                       ux ** 2 + uy ** 2 + C1,\n                       vx + vy + C2))\n    D = B1 * B2\n    S = (A1 * A2) / D\n\n    # to avoid edge effects will ignore filter radius strip around edges\n    pad = (win_size - 1) // 2\n\n    # compute (weighted) mean of ssim. Use float64 for accuracy.\n    mssim = crop(S, pad).mean(dtype=np.float64)\n\n    if gradient:\n        # The following is Eqs. 7-8 of Avanaki 2009.\n        grad = filter_func(A1 / D, **filter_args) * im1\n        grad += filter_func(-S / B2, **filter_args) * im2\n        grad += filter_func((ux * (A2 - A1) - uy * (B2 - B1) * S) / D,\n                            **filter_args)\n        grad *= (2 / im1.size)\n\n        if full:\n            return mssim, grad, S\n        else:\n            return mssim, grad\n    else:\n        if full:\n            return mssim, S\n        else:\n            return mssim\n", "path": "skimage/metrics/_structural_similarity.py"}], "after_files": [{"content": "import functools\n\nimport numpy as np\nfrom scipy.ndimage import uniform_filter\n\nfrom .._shared import utils\nfrom .._shared.filters import gaussian\nfrom .._shared.utils import _supported_float_type, check_shape_equality, warn\nfrom ..util.arraycrop import crop\nfrom ..util.dtype import dtype_range\n\n__all__ = ['structural_similarity']\n\n\n@utils.deprecate_multichannel_kwarg()\ndef structural_similarity(im1, im2,\n                          *,\n                          win_size=None, gradient=False, data_range=None,\n                          channel_axis=None, multichannel=False,\n                          gaussian_weights=False, full=False, **kwargs):\n    \"\"\"\n    Compute the mean structural similarity index between two images.\n    Please pay attention to the `data_range` parameter with floating-point images.\n\n    Parameters\n    ----------\n    im1, im2 : ndarray\n        Images. Any dimensionality with same shape.\n    win_size : int or None, optional\n        The side-length of the sliding window used in comparison. Must be an\n        odd value. If `gaussian_weights` is True, this is ignored and the\n        window size will depend on `sigma`.\n    gradient : bool, optional\n        If True, also return the gradient with respect to im2.\n    data_range : float, optional\n        The data range of the input image (distance between minimum and\n        maximum possible values). By default, this is estimated from the image\n        data type. This estimate may be wrong for floating-point image data.\n        Therefore it is recommended to always pass this value explicitly\n        (see note below).\n    channel_axis : int or None, optional\n        If None, the image is assumed to be a grayscale (single channel) image.\n        Otherwise, this parameter indicates which axis of the array corresponds\n        to channels.\n\n        .. versionadded:: 0.19\n           ``channel_axis`` was added in 0.19.\n    multichannel : bool, optional\n        If True, treat the last dimension of the array as channels. Similarity\n        calculations are done independently for each channel then averaged.\n        This argument is deprecated: specify `channel_axis` instead.\n    gaussian_weights : bool, optional\n        If True, each patch has its mean and variance spatially weighted by a\n        normalized Gaussian kernel of width sigma=1.5.\n    full : bool, optional\n        If True, also return the full structural similarity image.\n\n    Other Parameters\n    ----------------\n    use_sample_covariance : bool\n        If True, normalize covariances by N-1 rather than, N where N is the\n        number of pixels within the sliding window.\n    K1 : float\n        Algorithm parameter, K1 (small constant, see [1]_).\n    K2 : float\n        Algorithm parameter, K2 (small constant, see [1]_).\n    sigma : float\n        Standard deviation for the Gaussian when `gaussian_weights` is True.\n\n    Returns\n    -------\n    mssim : float\n        The mean structural similarity index over the image.\n    grad : ndarray\n        The gradient of the structural similarity between im1 and im2 [2]_.\n        This is only returned if `gradient` is set to True.\n    S : ndarray\n        The full SSIM image.  This is only returned if `full` is set to True.\n\n    Notes\n    -----\n    If `data_range` is not specified, the range is automatically guessed\n    based on the image data type. However for floating-point image data, this\n    estimate yields a result double the value of the desired range, as the\n    `dtype_range` in `skimage.util.dtype.py` has defined intervals from -1 to\n    +1. This yields an estimate of 2, instead of 1, which is most often\n    required when working with image data (as negative light intentsities are\n    nonsensical). In case of working with YCbCr-like color data, note that\n    these ranges are different per channel (Cb and Cr have double the range\n    of Y), so one cannot calculate a channel-averaged SSIM with a single call\n    to this function, as identical ranges are assumed for each channel.\n\n    To match the implementation of Wang et al. [1]_, set `gaussian_weights`\n    to True, `sigma` to 1.5, `use_sample_covariance` to False, and\n    specify the `data_range` argument.\n\n    .. versionchanged:: 0.16\n        This function was renamed from ``skimage.measure.compare_ssim`` to\n        ``skimage.metrics.structural_similarity``.\n\n    References\n    ----------\n    .. [1] Wang, Z., Bovik, A. C., Sheikh, H. R., & Simoncelli, E. P.\n       (2004). Image quality assessment: From error visibility to\n       structural similarity. IEEE Transactions on Image Processing,\n       13, 600-612.\n       https://ece.uwaterloo.ca/~z70wang/publications/ssim.pdf,\n       :DOI:`10.1109/TIP.2003.819861`\n\n    .. [2] Avanaki, A. N. (2009). Exact global histogram specification\n       optimized for structural similarity. Optical Review, 16, 613-621.\n       :arxiv:`0901.0065`\n       :DOI:`10.1007/s10043-009-0119-z`\n\n    \"\"\"\n    check_shape_equality(im1, im2)\n    float_type = _supported_float_type(im1.dtype)\n\n    if channel_axis is not None:\n        # loop over channels\n        args = dict(win_size=win_size,\n                    gradient=gradient,\n                    data_range=data_range,\n                    channel_axis=None,\n                    gaussian_weights=gaussian_weights,\n                    full=full)\n        args.update(kwargs)\n        nch = im1.shape[channel_axis]\n        mssim = np.empty(nch, dtype=float_type)\n\n        if gradient:\n            G = np.empty(im1.shape, dtype=float_type)\n        if full:\n            S = np.empty(im1.shape, dtype=float_type)\n        channel_axis = channel_axis % im1.ndim\n        _at = functools.partial(utils.slice_at_axis, axis=channel_axis)\n        for ch in range(nch):\n            ch_result = structural_similarity(im1[_at(ch)],\n                                              im2[_at(ch)], **args)\n            if gradient and full:\n                mssim[ch], G[_at(ch)], S[_at(ch)] = ch_result\n            elif gradient:\n                mssim[ch], G[_at(ch)] = ch_result\n            elif full:\n                mssim[ch], S[_at(ch)] = ch_result\n            else:\n                mssim[ch] = ch_result\n        mssim = mssim.mean()\n        if gradient and full:\n            return mssim, G, S\n        elif gradient:\n            return mssim, G\n        elif full:\n            return mssim, S\n        else:\n            return mssim\n\n    K1 = kwargs.pop('K1', 0.01)\n    K2 = kwargs.pop('K2', 0.03)\n    sigma = kwargs.pop('sigma', 1.5)\n    if K1 < 0:\n        raise ValueError(\"K1 must be positive\")\n    if K2 < 0:\n        raise ValueError(\"K2 must be positive\")\n    if sigma < 0:\n        raise ValueError(\"sigma must be positive\")\n    use_sample_covariance = kwargs.pop('use_sample_covariance', True)\n\n    if gaussian_weights:\n        # Set to give an 11-tap filter with the default sigma of 1.5 to match\n        # Wang et. al. 2004.\n        truncate = 3.5\n\n    if win_size is None:\n        if gaussian_weights:\n            # set win_size used by crop to match the filter size\n            r = int(truncate * sigma + 0.5)  # radius as in ndimage\n            win_size = 2 * r + 1\n        else:\n            win_size = 7   # backwards compatibility\n\n    if np.any((np.asarray(im1.shape) - win_size) < 0):\n        raise ValueError(\n            'win_size exceeds image extent. '\n            'Either ensure that your images are '\n            'at least 7x7; or pass win_size explicitly '\n            'in the function call, with an odd value '\n            'less than or equal to the smaller side of your '\n            'images. If your images are multichannel '\n            '(with color channels), set channel_axis to '\n            'the axis number corresponding to the channels.')\n\n    if not (win_size % 2 == 1):\n        raise ValueError('Window size must be odd.')\n\n    if data_range is None:\n        if (np.issubdtype(im1.dtype, np.floating) or\n            np.issubdtype(im2.dtype, np.floating)):\n            raise ValueError(\n                'Since image dtype is floating point, you must specify '\n                'the data_range parameter. Please read the documentation '\n                'carefully (including the note). It is recommended that '\n                'you always specify the data_range anyway.')\n        if im1.dtype != im2.dtype:\n            warn(\"Inputs have mismatched dtypes. Setting data_range based on im1.dtype.\",\n                 stacklevel=2)\n        dmin, dmax = dtype_range[im1.dtype.type]\n        data_range = dmax - dmin\n        if np.issubdtype(im1.dtype, np.integer) and (im1.dtype != np.uint8):\n            warn(\"Setting data_range based on im1.dtype. \" +\n                 (\"data_range = %.0f. \" % data_range) +\n                 \"Please specify data_range explicitly to avoid mistakes.\", stacklevel=2)\n\n    ndim = im1.ndim\n\n    if gaussian_weights:\n        filter_func = gaussian\n        filter_args = {'sigma': sigma, 'truncate': truncate, 'mode': 'reflect'}\n    else:\n        filter_func = uniform_filter\n        filter_args = {'size': win_size}\n\n    # ndimage filters need floating point data\n    im1 = im1.astype(float_type, copy=False)\n    im2 = im2.astype(float_type, copy=False)\n\n    NP = win_size ** ndim\n\n    # filter has already normalized by NP\n    if use_sample_covariance:\n        cov_norm = NP / (NP - 1)  # sample covariance\n    else:\n        cov_norm = 1.0  # population covariance to match Wang et. al. 2004\n\n    # compute (weighted) means\n    ux = filter_func(im1, **filter_args)\n    uy = filter_func(im2, **filter_args)\n\n    # compute (weighted) variances and covariances\n    uxx = filter_func(im1 * im1, **filter_args)\n    uyy = filter_func(im2 * im2, **filter_args)\n    uxy = filter_func(im1 * im2, **filter_args)\n    vx = cov_norm * (uxx - ux * ux)\n    vy = cov_norm * (uyy - uy * uy)\n    vxy = cov_norm * (uxy - ux * uy)\n\n    R = data_range\n    C1 = (K1 * R) ** 2\n    C2 = (K2 * R) ** 2\n\n    A1, A2, B1, B2 = ((2 * ux * uy + C1,\n                       2 * vxy + C2,\n                       ux ** 2 + uy ** 2 + C1,\n                       vx + vy + C2))\n    D = B1 * B2\n    S = (A1 * A2) / D\n\n    # to avoid edge effects will ignore filter radius strip around edges\n    pad = (win_size - 1) // 2\n\n    # compute (weighted) mean of ssim. Use float64 for accuracy.\n    mssim = crop(S, pad).mean(dtype=np.float64)\n\n    if gradient:\n        # The following is Eqs. 7-8 of Avanaki 2009.\n        grad = filter_func(A1 / D, **filter_args) * im1\n        grad += filter_func(-S / B2, **filter_args) * im2\n        grad += filter_func((ux * (A2 - A1) - uy * (B2 - B1) * S) / D,\n                            **filter_args)\n        grad *= (2 / im1.size)\n\n        if full:\n            return mssim, grad, S\n        else:\n            return mssim, grad\n    else:\n        if full:\n            return mssim, S\n        else:\n            return mssim\n", "path": "skimage/metrics/_structural_similarity.py"}]}