Coverage for .tox/py39/lib/python3.9/site-packages/cows/dtype.py: 33.33%
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1'''
2Contains code from scikit-image v0.18.3
3'''
6import numpy as np
7from warnings import warn
9# For integers Numpy uses `_integer_types` basis internally, and builds a leaky
10# `np.XintYY` abstraction on top of it. This leads to situations when, for
11# example, there are two np.Xint64 dtypes with the same attributes but
12# different object references. In order to avoid any potential issues,
13# we use the basis dtypes here. For more information, see:
14# - https://github.com/scikit-image/scikit-image/issues/3043
15# For convenience, for these dtypes we indicate also the possible bit depths
16# (some of them are platform specific). For the details, see:
17# http://www.unix.org/whitepapers/64bit.html
18_integer_types = (np.byte, np.ubyte, # 8 bits
19 np.short, np.ushort, # 16 bits
20 np.intc, np.uintc, # 16 or 32 or 64 bits
21 int, np.int_, np.uint, # 32 or 64 bits
22 np.longlong, np.ulonglong) # 64 bits
23_integer_ranges = {t: (np.iinfo(t).min, np.iinfo(t).max)
24 for t in _integer_types}
25dtype_range = {bool: (False, True),
26 np.bool_: (False, True),
27 np.bool8: (False, True),
28 float: (-1, 1),
29 np.float_: (-1, 1),
30 np.float16: (-1, 1),
31 np.float32: (-1, 1),
32 np.float64: (-1, 1)}
33dtype_range.update(_integer_ranges)
35_supported_types = list(dtype_range.keys())
38def dtype_limits(image, clip_negative=False):
39 """Return intensity limits, i.e. (min, max) tuple, of the image's dtype.
40 Parameters
41 ----------
42 image : ndarray
43 Input image.
44 clip_negative : bool, optional
45 If True, clip the negative range (i.e. return 0 for min intensity)
46 even if the image dtype allows negative values.
47 Returns
48 -------
49 imin, imax : tuple
50 Lower and upper intensity limits.
51 """
52 imin, imax = dtype_range[image.dtype.type]
53 if clip_negative:
54 imin = 0
55 return imin, imax
58def _dtype_itemsize(itemsize, *dtypes):
59 """Return first of `dtypes` with itemsize greater than `itemsize`
60 Parameters
61 ----------
62 itemsize: int
63 The data type object element size.
64 Other Parameters
65 ----------------
66 *dtypes:
67 Any Object accepted by `np.dtype` to be converted to a data
68 type object
69 Returns
70 -------
71 dtype: data type object
72 First of `dtypes` with itemsize greater than `itemsize`.
73 """
74 return next(dt for dt in dtypes if np.dtype(dt).itemsize >= itemsize) 74 ↛ exitline 74 didn't finish the generator expression on line 74
77def _dtype_bits(kind, bits, itemsize=1):
78 """Return dtype of `kind` that can store a `bits` wide unsigned int
79 Parameters:
80 kind: str
81 Data type kind.
82 bits: int
83 Desired number of bits.
84 itemsize: int
85 The data type object element size.
86 Returns
87 -------
88 dtype: data type object
89 Data type of `kind` that can store a `bits` wide unsigned int
90 """
92 s = next(i for i in (itemsize, ) + (2, 4, 8) if
93 bits < (i * 8) or (bits == (i * 8) and kind == 'u'))
95 return np.dtype(kind + str(s))
98def _scale(a, n, m, copy=True):
99 """Scale an array of unsigned/positive integers from `n` to `m` bits.
100 Numbers can be represented exactly only if `m` is a multiple of `n`.
101 Parameters
102 ----------
103 a : ndarray
104 Input image array.
105 n : int
106 Number of bits currently used to encode the values in `a`.
107 m : int
108 Desired number of bits to encode the values in `out`.
109 copy : bool, optional
110 If True, allocates and returns new array. Otherwise, modifies
111 `a` in place.
112 Returns
113 -------
114 out : array
115 Output image array. Has the same kind as `a`.
116 """
117 kind = a.dtype.kind
118 if n > m and a.max() < 2 ** m:
119 mnew = int(np.ceil(m / 2) * 2)
120 if mnew > m:
121 dtype = "int{}".format(mnew)
122 else:
123 dtype = "uint{}".format(mnew)
124 n = int(np.ceil(n / 2) * 2)
125 warn("Downcasting {} to {} without scaling because max "
126 "value {} fits in {}".format(a.dtype, dtype, a.max(), dtype),
127 stacklevel=3)
128 return a.astype(_dtype_bits(kind, m))
129 elif n == m:
130 return a.copy() if copy else a
131 elif n > m:
132 # downscale with precision loss
133 if copy:
134 b = np.empty(a.shape, _dtype_bits(kind, m))
135 np.floor_divide(a, 2**(n - m), out=b, dtype=a.dtype,
136 casting='unsafe')
137 return b
138 else:
139 a //= 2**(n - m)
140 return a
141 elif m % n == 0:
142 # exact upscale to a multiple of `n` bits
143 if copy:
144 b = np.empty(a.shape, _dtype_bits(kind, m))
145 np.multiply(a, (2**m - 1) // (2**n - 1), out=b, dtype=b.dtype)
146 return b
147 else:
148 a = a.astype(_dtype_bits(kind, m, a.dtype.itemsize), copy=False)
149 a *= (2**m - 1) // (2**n - 1)
150 return a
151 else:
152 # upscale to a multiple of `n` bits,
153 # then downscale with precision loss
154 o = (m // n + 1) * n
155 if copy:
156 b = np.empty(a.shape, _dtype_bits(kind, o))
157 np.multiply(a, (2**o - 1) // (2**n - 1), out=b, dtype=b.dtype)
158 b //= 2**(o - m)
159 return b
160 else:
161 a = a.astype(_dtype_bits(kind, o, a.dtype.itemsize), copy=False)
162 a *= (2**o - 1) // (2**n - 1)
163 a //= 2**(o - m)
164 return a
167def _convert(image, dtype, force_copy=False, uniform=False):
168 """
169 Convert an image to the requested data-type.
170 Warnings are issued in case of precision loss, or when negative values
171 are clipped during conversion to unsigned integer types (sign loss).
172 Floating point values are expected to be normalized and will be clipped
173 to the range [0.0, 1.0] or [-1.0, 1.0] when converting to unsigned or
174 signed integers respectively.
175 Numbers are not shifted to the negative side when converting from
176 unsigned to signed integer types. Negative values will be clipped when
177 converting to unsigned integers.
178 Parameters
179 ----------
180 image : ndarray
181 Input image.
182 dtype : dtype
183 Target data-type.
184 force_copy : bool, optional
185 Force a copy of the data, irrespective of its current dtype.
186 uniform : bool, optional
187 Uniformly quantize the floating point range to the integer range.
188 By default (uniform=False) floating point values are scaled and
189 rounded to the nearest integers, which minimizes back and forth
190 conversion errors.
191 .. versionchanged :: 0.15
192 ``_convert`` no longer warns about possible precision or sign
193 information loss. See discussions on these warnings at:
194 https://github.com/scikit-image/scikit-image/issues/2602
195 https://github.com/scikit-image/scikit-image/issues/543#issuecomment-208202228
196 https://github.com/scikit-image/scikit-image/pull/3575
197 References
198 ----------
199 .. [1] DirectX data conversion rules.
200 https://msdn.microsoft.com/en-us/library/windows/desktop/dd607323%28v=vs.85%29.aspx
201 .. [2] Data Conversions. In "OpenGL ES 2.0 Specification v2.0.25",
202 pp 7-8. Khronos Group, 2010.
203 .. [3] Proper treatment of pixels as integers. A.W. Paeth.
204 In "Graphics Gems I", pp 249-256. Morgan Kaufmann, 1990.
205 .. [4] Dirty Pixels. J. Blinn. In "Jim Blinn's corner: Dirty Pixels",
206 pp 47-57. Morgan Kaufmann, 1998.
207 """
208 image = np.asarray(image)
209 dtypeobj_in = image.dtype
210 if dtype is np.floating: 210 ↛ 211line 210 didn't jump to line 211, because the condition on line 210 was never true
211 dtypeobj_out = np.dtype('float64')
212 else:
213 dtypeobj_out = np.dtype(dtype)
214 dtype_in = dtypeobj_in.type
215 dtype_out = dtypeobj_out.type
216 kind_in = dtypeobj_in.kind
217 kind_out = dtypeobj_out.kind
218 itemsize_in = dtypeobj_in.itemsize
219 itemsize_out = dtypeobj_out.itemsize
221 # Below, we do an `issubdtype` check. Its purpose is to find out
222 # whether we can get away without doing any image conversion. This happens
223 # when:
224 #
225 # - the output and input dtypes are the same or
226 # - when the output is specified as a type, and the input dtype
227 # is a subclass of that type (e.g. `np.floating` will allow
228 # `float32` and `float64` arrays through)
230 if np.issubdtype(dtype_in, np.obj2sctype(dtype)):
231 if force_copy: 231 ↛ 232line 231 didn't jump to line 232, because the condition on line 231 was never true
232 image = image.copy()
233 return image
235 if not (dtype_in in _supported_types and dtype_out in _supported_types): 235 ↛ 236line 235 didn't jump to line 236, because the condition on line 235 was never true
236 raise ValueError("Can not convert from {} to {}."
237 .format(dtypeobj_in, dtypeobj_out))
239 if kind_in in 'ui': 239 ↛ 240line 239 didn't jump to line 240, because the condition on line 239 was never true
240 imin_in = np.iinfo(dtype_in).min
241 imax_in = np.iinfo(dtype_in).max
242 if kind_out in 'ui': 242 ↛ 247line 242 didn't jump to line 247, because the condition on line 242 was never false
243 imin_out = np.iinfo(dtype_out).min
244 imax_out = np.iinfo(dtype_out).max
246 # any -> binary
247 if kind_out == 'b': 247 ↛ 248line 247 didn't jump to line 248, because the condition on line 247 was never true
248 return image > dtype_in(dtype_range[dtype_in][1] / 2)
250 # binary -> any
251 if kind_in == 'b': 251 ↛ 252line 251 didn't jump to line 252, because the condition on line 251 was never true
252 result = image.astype(dtype_out)
253 if kind_out != 'f':
254 result *= dtype_out(dtype_range[dtype_out][1])
255 return result
257 # float -> any
258 if kind_in == 'f': 258 ↛ 292line 258 didn't jump to line 292, because the condition on line 258 was never false
259 if kind_out == 'f': 259 ↛ 261line 259 didn't jump to line 261, because the condition on line 259 was never true
260 # float -> float
261 return image.astype(dtype_out)
263 if np.min(image) < -1.0 or np.max(image) > 1.0: 263 ↛ 264line 263 didn't jump to line 264, because the condition on line 263 was never true
264 raise ValueError("Images of type float must be between -1 and 1.")
265 # floating point -> integer
266 # use float type that can represent output integer type
267 computation_type = _dtype_itemsize(itemsize_out, dtype_in,
268 np.float32, np.float64)
270 if not uniform: 270 ↛ 280line 270 didn't jump to line 280, because the condition on line 270 was never false
271 if kind_out == 'u': 271 ↛ 275line 271 didn't jump to line 275, because the condition on line 271 was never false
272 image_out = np.multiply(image, imax_out,
273 dtype=computation_type)
274 else:
275 image_out = np.multiply(image, (imax_out - imin_out) / 2,
276 dtype=computation_type)
277 image_out -= 1.0 / 2.
278 np.rint(image_out, out=image_out)
279 np.clip(image_out, imin_out, imax_out, out=image_out)
280 elif kind_out == 'u':
281 image_out = np.multiply(image, imax_out + 1,
282 dtype=computation_type)
283 np.clip(image_out, 0, imax_out, out=image_out)
284 else:
285 image_out = np.multiply(image, (imax_out - imin_out + 1.0) / 2.0,
286 dtype=computation_type)
287 np.floor(image_out, out=image_out)
288 np.clip(image_out, imin_out, imax_out, out=image_out)
289 return image_out.astype(dtype_out)
291 # signed/unsigned int -> float
292 if kind_out == 'f':
293 # use float type that can exactly represent input integers
294 computation_type = _dtype_itemsize(itemsize_in, dtype_out,
295 np.float32, np.float64)
297 if kind_in == 'u':
298 # using np.divide or np.multiply doesn't copy the data
299 # until the computation time
300 image = np.multiply(image, 1. / imax_in,
301 dtype=computation_type)
302 # DirectX uses this conversion also for signed ints
303 # if imin_in:
304 # np.maximum(image, -1.0, out=image)
305 else:
306 image = np.add(image, 0.5, dtype=computation_type)
307 image *= 2 / (imax_in - imin_in)
309 return np.asarray(image, dtype_out)
311 # unsigned int -> signed/unsigned int
312 if kind_in == 'u':
313 if kind_out == 'i':
314 # unsigned int -> signed int
315 image = _scale(image, 8 * itemsize_in, 8 * itemsize_out - 1)
316 return image.view(dtype_out)
317 else:
318 # unsigned int -> unsigned int
319 return _scale(image, 8 * itemsize_in, 8 * itemsize_out)
321 # signed int -> unsigned int
322 if kind_out == 'u':
323 image = _scale(image, 8 * itemsize_in - 1, 8 * itemsize_out)
324 result = np.empty(image.shape, dtype_out)
325 np.maximum(image, 0, out=result, dtype=image.dtype, casting='unsafe')
326 return result
328 # signed int -> signed int
329 if itemsize_in > itemsize_out:
330 return _scale(image, 8 * itemsize_in - 1, 8 * itemsize_out - 1)
332 image = image.astype(_dtype_bits('i', itemsize_out * 8))
333 image -= imin_in
334 image = _scale(image, 8 * itemsize_in, 8 * itemsize_out, copy=False)
335 image += imin_out
336 return image.astype(dtype_out)
339def convert(image, dtype, force_copy=False, uniform=False):
340 warn("The use of this function is discouraged as its behavior may change "
341 "dramatically in scikit-image 1.0. This function will be removed"
342 "in scikit-image 1.0.", FutureWarning, stacklevel=2)
343 return _convert(image=image, dtype=dtype,
344 force_copy=force_copy, uniform=uniform)
347if _convert.__doc__ is not None: 347 ↛ 359line 347 didn't jump to line 359, because the condition on line 347 was never false
348 convert.__doc__ = _convert.__doc__ + """
349 Warns
350 -----
351 FutureWarning:
352 .. versionadded:: 0.17
353 The use of this function is discouraged as its behavior may change
354 dramatically in scikit-image 1.0. This function will be removed
355 in scikit-image 1.0.
356 """
359def img_as_float32(image, force_copy=False):
360 """Convert an image to single-precision (32-bit) floating point format.
361 Parameters
362 ----------
363 image : ndarray
364 Input image.
365 force_copy : bool, optional
366 Force a copy of the data, irrespective of its current dtype.
367 Returns
368 -------
369 out : ndarray of float32
370 Output image.
371 Notes
372 -----
373 The range of a floating point image is [0.0, 1.0] or [-1.0, 1.0] when
374 converting from unsigned or signed datatypes, respectively.
375 If the input image has a float type, intensity values are not modified
376 and can be outside the ranges [0.0, 1.0] or [-1.0, 1.0].
377 """
378 return _convert(image, np.float32, force_copy)
381def img_as_float64(image, force_copy=False):
382 """Convert an image to double-precision (64-bit) floating point format.
383 Parameters
384 ----------
385 image : ndarray
386 Input image.
387 force_copy : bool, optional
388 Force a copy of the data, irrespective of its current dtype.
389 Returns
390 -------
391 out : ndarray of float64
392 Output image.
393 Notes
394 -----
395 The range of a floating point image is [0.0, 1.0] or [-1.0, 1.0] when
396 converting from unsigned or signed datatypes, respectively.
397 If the input image has a float type, intensity values are not modified
398 and can be outside the ranges [0.0, 1.0] or [-1.0, 1.0].
399 """
400 return _convert(image, np.float64, force_copy)
403def img_as_float(image, force_copy=False):
404 """Convert an image to floating point format.
405 This function is similar to `img_as_float64`, but will not convert
406 lower-precision floating point arrays to `float64`.
407 Parameters
408 ----------
409 image : ndarray
410 Input image.
411 force_copy : bool, optional
412 Force a copy of the data, irrespective of its current dtype.
413 Returns
414 -------
415 out : ndarray of float
416 Output image.
417 Notes
418 -----
419 The range of a floating point image is [0.0, 1.0] or [-1.0, 1.0] when
420 converting from unsigned or signed datatypes, respectively.
421 If the input image has a float type, intensity values are not modified
422 and can be outside the ranges [0.0, 1.0] or [-1.0, 1.0].
423 """
424 return _convert(image, np.floating, force_copy)
427def img_as_uint(image, force_copy=False):
428 """Convert an image to 16-bit unsigned integer format.
429 Parameters
430 ----------
431 image : ndarray
432 Input image.
433 force_copy : bool, optional
434 Force a copy of the data, irrespective of its current dtype.
435 Returns
436 -------
437 out : ndarray of uint16
438 Output image.
439 Notes
440 -----
441 Negative input values will be clipped.
442 Positive values are scaled between 0 and 65535.
443 """
444 return _convert(image, np.uint16, force_copy)
447def img_as_int(image, force_copy=False):
448 """Convert an image to 16-bit signed integer format.
449 Parameters
450 ----------
451 image : ndarray
452 Input image.
453 force_copy : bool, optional
454 Force a copy of the data, irrespective of its current dtype.
455 Returns
456 -------
457 out : ndarray of int16
458 Output image.
459 Notes
460 -----
461 The values are scaled between -32768 and 32767.
462 If the input data-type is positive-only (e.g., uint8), then
463 the output image will still only have positive values.
464 """
465 return _convert(image, np.int16, force_copy)
468def img_as_ubyte(image, force_copy=False):
469 """Convert an image to 8-bit unsigned integer format.
470 Parameters
471 ----------
472 image : ndarray
473 Input image.
474 force_copy : bool, optional
475 Force a copy of the data, irrespective of its current dtype.
476 Returns
477 -------
478 out : ndarray of ubyte (uint8)
479 Output image.
480 Notes
481 -----
482 Negative input values will be clipped.
483 Positive values are scaled between 0 and 255.
484 """
485 return _convert(image, np.uint8, force_copy)
488def img_as_bool(image, force_copy=False):
489 """Convert an image to boolean format.
490 Parameters
491 ----------
492 image : ndarray
493 Input image.
494 force_copy : bool, optional
495 Force a copy of the data, irrespective of its current dtype.
496 Returns
497 -------
498 out : ndarray of bool (`bool_`)
499 Output image.
500 Notes
501 -----
502 The upper half of the input dtype's positive range is True, and the lower
503 half is False. All negative values (if present) are False.
504 """
505 return _convert(image, bool, force_copy)