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

148 statements  

1''' 

2Contains code from scikit-image v0.18.3 

3''' 

4 

5 

6import numpy as np 

7from warnings import warn 

8 

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) 

34 

35_supported_types = list(dtype_range.keys()) 

36 

37 

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 

56 

57 

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

75 

76 

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 """ 

91 

92 s = next(i for i in (itemsize, ) + (2, 4, 8) if 

93 bits < (i * 8) or (bits == (i * 8) and kind == 'u')) 

94 

95 return np.dtype(kind + str(s)) 

96 

97 

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 

165 

166 

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 

220 

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) 

229 

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 

234 

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)) 

238 

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 

245 

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) 

249 

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 

256 

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) 

262 

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) 

269 

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) 

290 

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) 

296 

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) 

308 

309 return np.asarray(image, dtype_out) 

310 

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) 

320 

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 

327 

328 # signed int -> signed int 

329 if itemsize_in > itemsize_out: 

330 return _scale(image, 8 * itemsize_in - 1, 8 * itemsize_out - 1) 

331 

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) 

337 

338 

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) 

345 

346 

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 """ 

357 

358 

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) 

379 

380 

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) 

401 

402 

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) 

425 

426 

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) 

445 

446 

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) 

466 

467 

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) 

486 

487 

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)