Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/imageio-2.36.1-py3.10.egg/imageio/v2.py: 30%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

244 statements  

1# -*- coding: utf-8 -*- 

2# imageio is distributed under the terms of the (new) BSD License. 

3 

4import re 

5import warnings 

6from numbers import Number 

7from pathlib import Path 

8from typing import Dict 

9 

10import numpy as np 

11 

12from imageio.core.legacy_plugin_wrapper import LegacyPlugin 

13from imageio.core.util import Array 

14from imageio.core.v3_plugin_api import PluginV3 

15 

16from . import formats 

17from .config import known_extensions, known_plugins 

18from .core import RETURN_BYTES 

19from .core.imopen import imopen 

20 

21MEMTEST_DEFAULT_MIM = "256MB" 

22MEMTEST_DEFAULT_MVOL = "1GB" 

23 

24 

25mem_re = re.compile(r"^(\d+\.?\d*)\s*([kKMGTPEZY]?i?)B?$") 

26sizes = {"": 1, None: 1} 

27for i, si in enumerate([""] + list("kMGTPEZY")): 

28 sizes[si] = 1000**i 

29 if si: 

30 sizes[si.upper() + "i"] = 1024**i 

31 

32 

33def to_nbytes(arg, default=None): 

34 if not arg: 

35 arg = float("inf") 

36 

37 if arg is True: 

38 arg = default 

39 

40 if isinstance(arg, Number): 

41 return arg 

42 

43 match = mem_re.match(arg) 

44 if match is None: 

45 raise ValueError( 

46 "Memory size could not be parsed " 

47 "(is your capitalisation correct?): {}".format(arg) 

48 ) 

49 

50 num, unit = match.groups() 

51 

52 try: 

53 return float(num) * sizes[unit] 

54 except KeyError: # pragma: no cover 

55 # Note: I don't think we can reach this 

56 raise ValueError( 

57 "Memory size unit not recognised " 

58 "(is your capitalisation correct?): {}".format(unit) 

59 ) 

60 

61 

62def help(name=None): 

63 """help(name=None) 

64 

65 Print the documentation of the format specified by name, or a list 

66 of supported formats if name is omitted. 

67 

68 Parameters 

69 ---------- 

70 name : str 

71 Can be the name of a format, a filename extension, or a full 

72 filename. See also the :doc:`formats page <../formats/index>`. 

73 """ 

74 if not name: 

75 print(formats) 

76 else: 

77 print(formats[name]) 

78 

79 

80def decypher_format_arg(format_name: str) -> Dict[str, str]: 

81 """Split format into plugin and format 

82 

83 The V2 API aliases plugins and supported formats. This function 

84 splits these so that they can be fed separately to `iio.imopen`. 

85 

86 """ 

87 

88 plugin = None 

89 extension = None 

90 

91 if format_name is None: 

92 pass # nothing to do 

93 elif Path(format_name).suffix.lower() in known_extensions: 

94 extension = Path(format_name).suffix.lower() 

95 elif format_name in known_plugins: 

96 plugin = format_name 

97 elif format_name.upper() in known_plugins: 

98 plugin = format_name.upper() 

99 elif format_name.lower() in known_extensions: 

100 extension = format_name.lower() 

101 elif "." + format_name.lower() in known_extensions: 

102 extension = "." + format_name.lower() 

103 else: 

104 raise IndexError(f"No format known by name `{plugin}`.") 

105 

106 return {"plugin": plugin, "extension": extension} 

107 

108 

109class LegacyReader: 

110 def __init__(self, plugin_instance: PluginV3, **kwargs): 

111 self.instance = plugin_instance 

112 self.last_index = 0 

113 self.closed = False 

114 

115 if ( 

116 type(self.instance).__name__ == "PillowPlugin" 

117 and kwargs.get("pilmode") is not None 

118 ): 

119 kwargs["mode"] = kwargs["pilmode"] 

120 del kwargs["pilmode"] 

121 

122 self.read_args = kwargs 

123 

124 def close(self): 

125 if not self.closed: 

126 self.instance.close() 

127 self.closed = True 

128 

129 def __enter__(self): 

130 return self 

131 

132 def __exit__(self, type, value, traceback): 

133 self.close() 

134 

135 def __del__(self): 

136 self.close() 

137 

138 @property 

139 def request(self): 

140 return self.instance.request 

141 

142 @property 

143 def format(self): 

144 raise TypeError("V3 Plugins don't have a format.") 

145 

146 def get_length(self): 

147 return self.instance.properties(index=...).n_images 

148 

149 def get_data(self, index): 

150 self.last_index = index 

151 img = self.instance.read(index=index, **self.read_args) 

152 metadata = self.instance.metadata(index=index, exclude_applied=False) 

153 return Array(img, metadata) 

154 

155 def get_next_data(self): 

156 return self.get_data(self.last_index + 1) 

157 

158 def set_image_index(self, index): 

159 self.last_index = index - 1 

160 

161 def get_meta_data(self, index=None): 

162 return self.instance.metadata(index=index, exclude_applied=False) 

163 

164 def iter_data(self): 

165 for idx, img in enumerate(self.instance.iter()): 

166 metadata = self.instance.metadata(index=idx, exclude_applied=False) 

167 yield Array(img, metadata) 

168 

169 def __iter__(self): 

170 return self.iter_data() 

171 

172 def __len__(self): 

173 return self.get_length() 

174 

175 

176class LegacyWriter: 

177 def __init__(self, plugin_instance: PluginV3, **kwargs): 

178 self.instance = plugin_instance 

179 self.last_index = 0 

180 self.closed = False 

181 

182 if type(self.instance).__name__ == "PillowPlugin" and "pilmode" in kwargs: 

183 kwargs["mode"] = kwargs["pilmode"] 

184 del kwargs["pilmode"] 

185 

186 self.write_args = kwargs 

187 

188 def close(self): 

189 if not self.closed: 

190 self.instance.close() 

191 self.closed = True 

192 

193 def __enter__(self): 

194 return self 

195 

196 def __exit__(self, type, value, traceback): 

197 self.close() 

198 

199 def __del__(self): 

200 self.close() 

201 

202 @property 

203 def request(self): 

204 return self.instance.request 

205 

206 @property 

207 def format(self): 

208 raise TypeError("V3 Plugins don't have a format.") 

209 

210 def append_data(self, im, meta=None): 

211 # TODO: write metadata in the future; there is currently no 

212 # generic way to do this with v3 plugins :( 

213 if meta is not None: 

214 warnings.warn( 

215 "V3 Plugins currently don't have a uniform way to" 

216 " write metadata, so any metadata is ignored." 

217 ) 

218 

219 # total_meta = dict() 

220 # if meta is None: 

221 # meta = {} 

222 # if hasattr(im, "meta") and isinstance(im.meta, dict): 

223 # total_meta.update(im.meta) 

224 # total_meta.update(meta) 

225 

226 return self.instance.write(im, **self.write_args) 

227 

228 def set_meta_data(self, meta): 

229 # TODO: write metadata 

230 raise NotImplementedError( 

231 "V3 Plugins don't have a uniform way to write metadata (yet)." 

232 ) 

233 

234 

235def is_batch(ndimage): 

236 if isinstance(ndimage, (list, tuple)): 

237 return True 

238 

239 ndimage = np.asarray(ndimage) 

240 if ndimage.ndim <= 2: 

241 return False 

242 elif ndimage.ndim == 3 and ndimage.shape[2] < 5: 

243 return False 

244 

245 return True 

246 

247 

248def is_volume(ndimage): 

249 ndimage = np.asarray(ndimage) 

250 if not is_batch(ndimage): 

251 return False 

252 

253 if ndimage.ndim == 3 and ndimage.shape[2] >= 5: 

254 return True 

255 elif ndimage.ndim == 4 and ndimage.shape[3] < 5: 

256 return True 

257 else: 

258 return False 

259 

260 

261# Base functions that return a reader/writer 

262 

263 

264def get_reader(uri, format=None, mode="?", **kwargs): 

265 """get_reader(uri, format=None, mode='?', **kwargs) 

266 

267 Returns a :class:`.Reader` object which can be used to read data 

268 and meta data from the specified file. 

269 

270 Parameters 

271 ---------- 

272 uri : {str, pathlib.Path, bytes, file} 

273 The resource to load the image from, e.g. a filename, pathlib.Path, 

274 http address or file object, see the docs for more info. 

275 format : str 

276 The format to use to read the file. By default imageio selects 

277 the appropriate for you based on the filename and its contents. 

278 mode : {'i', 'I', 'v', 'V', '?'} 

279 Used to give the reader a hint on what the user expects (default "?"): 

280 "i" for an image, "I" for multiple images, "v" for a volume, 

281 "V" for multiple volumes, "?" for don't care. 

282 kwargs : ... 

283 Further keyword arguments are passed to the reader. See :func:`.help` 

284 to see what arguments are available for a particular format. 

285 """ 

286 

287 imopen_args = decypher_format_arg(format) 

288 imopen_args["legacy_mode"] = True 

289 

290 image_file = imopen(uri, "r" + mode, **imopen_args) 

291 

292 if isinstance(image_file, LegacyPlugin): 

293 return image_file.legacy_get_reader(**kwargs) 

294 else: 

295 return LegacyReader(image_file, **kwargs) 

296 

297 

298def get_writer(uri, format=None, mode="?", **kwargs): 

299 """get_writer(uri, format=None, mode='?', **kwargs) 

300 

301 Returns a :class:`.Writer` object which can be used to write data 

302 and meta data to the specified file. 

303 

304 Parameters 

305 ---------- 

306 uri : {str, pathlib.Path, file} 

307 The resource to write the image to, e.g. a filename, pathlib.Path 

308 or file object, see the docs for more info. 

309 format : str 

310 The format to use to write the file. By default imageio selects 

311 the appropriate for you based on the filename. 

312 mode : {'i', 'I', 'v', 'V', '?'} 

313 Used to give the writer a hint on what the user expects (default '?'): 

314 "i" for an image, "I" for multiple images, "v" for a volume, 

315 "V" for multiple volumes, "?" for don't care. 

316 kwargs : ... 

317 Further keyword arguments are passed to the writer. See :func:`.help` 

318 to see what arguments are available for a particular format. 

319 """ 

320 

321 imopen_args = decypher_format_arg(format) 

322 imopen_args["legacy_mode"] = True 

323 

324 image_file = imopen(uri, "w" + mode, **imopen_args) 

325 if isinstance(image_file, LegacyPlugin): 

326 return image_file.legacy_get_writer(**kwargs) 

327 else: 

328 return LegacyWriter(image_file, **kwargs) 

329 

330 

331# Images 

332 

333 

334def imread(uri, format=None, **kwargs): 

335 """imread(uri, format=None, **kwargs) 

336 

337 Reads an image from the specified file. Returns a numpy array, which 

338 comes with a dict of meta data at its 'meta' attribute. 

339 

340 Note that the image data is returned as-is, and may not always have 

341 a dtype of uint8 (and thus may differ from what e.g. PIL returns). 

342 

343 Parameters 

344 ---------- 

345 uri : {str, pathlib.Path, bytes, file} 

346 The resource to load the image from, e.g. a filename, pathlib.Path, 

347 http address or file object, see the docs for more info. 

348 format : str 

349 The format to use to read the file. By default imageio selects 

350 the appropriate for you based on the filename and its contents. 

351 kwargs : ... 

352 Further keyword arguments are passed to the reader. See :func:`.help` 

353 to see what arguments are available for a particular format. 

354 """ 

355 

356 imopen_args = decypher_format_arg(format) 

357 imopen_args["legacy_mode"] = True 

358 

359 with imopen(uri, "ri", **imopen_args) as file: 

360 result = file.read(index=0, **kwargs) 

361 

362 return result 

363 

364 

365def imwrite(uri, im, format=None, **kwargs): 

366 """imwrite(uri, im, format=None, **kwargs) 

367 

368 Write an image to the specified file. 

369 

370 Parameters 

371 ---------- 

372 uri : {str, pathlib.Path, file} 

373 The resource to write the image to, e.g. a filename, pathlib.Path 

374 or file object, see the docs for more info. 

375 im : numpy.ndarray 

376 The image data. Must be NxM, NxMx3 or NxMx4. 

377 format : str 

378 The format to use to write the file. By default imageio selects 

379 the appropriate for you based on the filename and its contents. 

380 kwargs : ... 

381 Further keyword arguments are passed to the writer. See :func:`.help` 

382 to see what arguments are available for a particular format. 

383 """ 

384 

385 # Test image 

386 imt = type(im) 

387 im = np.asarray(im) 

388 if not np.issubdtype(im.dtype, np.number): 

389 raise ValueError("Image is not numeric, but {}.".format(imt.__name__)) 

390 

391 if is_batch(im) or im.ndim < 2: 

392 raise ValueError("Image must be 2D (grayscale, RGB, or RGBA).") 

393 

394 imopen_args = decypher_format_arg(format) 

395 imopen_args["legacy_mode"] = True 

396 with imopen(uri, "wi", **imopen_args) as file: 

397 return file.write(im, **kwargs) 

398 

399 

400# Multiple images 

401 

402 

403def mimread(uri, format=None, memtest=MEMTEST_DEFAULT_MIM, **kwargs): 

404 """mimread(uri, format=None, memtest="256MB", **kwargs) 

405 

406 Reads multiple images from the specified file. Returns a list of 

407 numpy arrays, each with a dict of meta data at its 'meta' attribute. 

408 

409 Parameters 

410 ---------- 

411 uri : {str, pathlib.Path, bytes, file} 

412 The resource to load the images from, e.g. a filename,pathlib.Path, 

413 http address or file object, see the docs for more info. 

414 format : str 

415 The format to use to read the file. By default imageio selects 

416 the appropriate for you based on the filename and its contents. 

417 memtest : {bool, int, float, str} 

418 If truthy, this function will raise an error if the resulting 

419 list of images consumes greater than the amount of memory specified. 

420 This is to protect the system from using so much memory that it needs 

421 to resort to swapping, and thereby stall the computer. E.g. 

422 ``mimread('hunger_games.avi')``. 

423 

424 If the argument is a number, that will be used as the threshold number 

425 of bytes. 

426 

427 If the argument is a string, it will be interpreted as a number of bytes with 

428 SI/IEC prefixed units (e.g. '1kB', '250MiB', '80.3YB'). 

429 

430 - Units are case sensitive 

431 - k, M etc. represent a 1000-fold change, where Ki, Mi etc. represent 1024-fold 

432 - The "B" is optional, but if present, must be capitalised 

433 

434 If the argument is True, the default will be used, for compatibility reasons. 

435 

436 Default: '256MB' 

437 kwargs : ... 

438 Further keyword arguments are passed to the reader. See :func:`.help` 

439 to see what arguments are available for a particular format. 

440 """ 

441 

442 # used for mimread and mvolread 

443 nbyte_limit = to_nbytes(memtest, MEMTEST_DEFAULT_MIM) 

444 

445 images = list() 

446 nbytes = 0 

447 

448 imopen_args = decypher_format_arg(format) 

449 imopen_args["legacy_mode"] = True 

450 with imopen(uri, "rI", **imopen_args) as file: 

451 for image in file.iter(**kwargs): 

452 images.append(image) 

453 nbytes += image.nbytes 

454 if nbytes > nbyte_limit: 

455 raise RuntimeError( 

456 "imageio.mimread() has read over {}B of " 

457 "image data.\nStopped to avoid memory problems." 

458 " Use imageio.get_reader(), increase threshold, or memtest=False".format( 

459 int(nbyte_limit) 

460 ) 

461 ) 

462 

463 if len(images) == 1 and is_batch(images[0]): 

464 images = [*images[0]] 

465 

466 return images 

467 

468 

469def mimwrite(uri, ims, format=None, **kwargs): 

470 """mimwrite(uri, ims, format=None, **kwargs) 

471 

472 Write multiple images to the specified file. 

473 

474 Parameters 

475 ---------- 

476 uri : {str, pathlib.Path, file} 

477 The resource to write the images to, e.g. a filename, pathlib.Path 

478 or file object, see the docs for more info. 

479 ims : sequence of numpy arrays 

480 The image data. Each array must be NxM, NxMx3 or NxMx4. 

481 format : str 

482 The format to use to read the file. By default imageio selects 

483 the appropriate for you based on the filename and its contents. 

484 kwargs : ... 

485 Further keyword arguments are passed to the writer. See :func:`.help` 

486 to see what arguments are available for a particular format. 

487 """ 

488 

489 if not is_batch(ims): 

490 raise ValueError("Image data must be a sequence of ndimages.") 

491 

492 imopen_args = decypher_format_arg(format) 

493 imopen_args["legacy_mode"] = True 

494 with imopen(uri, "wI", **imopen_args) as file: 

495 return file.write(ims, is_batch=True, **kwargs) 

496 

497 

498# Volumes 

499 

500 

501def volread(uri, format=None, **kwargs): 

502 """volread(uri, format=None, **kwargs) 

503 

504 Reads a volume from the specified file. Returns a numpy array, which 

505 comes with a dict of meta data at its 'meta' attribute. 

506 

507 Parameters 

508 ---------- 

509 uri : {str, pathlib.Path, bytes, file} 

510 The resource to load the volume from, e.g. a filename, pathlib.Path, 

511 http address or file object, see the docs for more info. 

512 format : str 

513 The format to use to read the file. By default imageio selects 

514 the appropriate for you based on the filename and its contents. 

515 kwargs : ... 

516 Further keyword arguments are passed to the reader. See :func:`.help` 

517 to see what arguments are available for a particular format. 

518 """ 

519 

520 imopen_args = decypher_format_arg(format) 

521 imopen_args["legacy_mode"] = True 

522 with imopen(uri, "rv", **imopen_args) as file: 

523 return file.read(index=0, **kwargs) 

524 

525 

526def volwrite(uri, im, format=None, **kwargs): 

527 """volwrite(uri, vol, format=None, **kwargs) 

528 

529 Write a volume to the specified file. 

530 

531 Parameters 

532 ---------- 

533 uri : {str, pathlib.Path, file} 

534 The resource to write the image to, e.g. a filename, pathlib.Path 

535 or file object, see the docs for more info. 

536 vol : numpy.ndarray 

537 The image data. Must be NxMxL (or NxMxLxK if each voxel is a tuple). 

538 format : str 

539 The format to use to read the file. By default imageio selects 

540 the appropriate for you based on the filename and its contents. 

541 kwargs : ... 

542 Further keyword arguments are passed to the writer. See :func:`.help` 

543 to see what arguments are available for a particular format. 

544 """ 

545 

546 # Test image 

547 im = np.asarray(im) 

548 if not is_volume(im): 

549 raise ValueError("Image must be 3D, or 4D if each voxel is a tuple.") 

550 

551 imopen_args = decypher_format_arg(format) 

552 imopen_args["legacy_mode"] = True 

553 

554 with imopen(uri, "wv", **imopen_args) as file: 

555 return file.write(im, is_batch=False, **kwargs) 

556 

557 

558# Multiple volumes 

559 

560 

561def mvolread(uri, format=None, memtest=MEMTEST_DEFAULT_MVOL, **kwargs): 

562 """mvolread(uri, format=None, memtest='1GB', **kwargs) 

563 

564 Reads multiple volumes from the specified file. Returns a list of 

565 numpy arrays, each with a dict of meta data at its 'meta' attribute. 

566 

567 Parameters 

568 ---------- 

569 uri : {str, pathlib.Path, bytes, file} 

570 The resource to load the volumes from, e.g. a filename, pathlib.Path, 

571 http address or file object, see the docs for more info. 

572 format : str 

573 The format to use to read the file. By default imageio selects 

574 the appropriate for you based on the filename and its contents. 

575 memtest : {bool, int, float, str} 

576 If truthy, this function will raise an error if the resulting 

577 list of images consumes greater than the amount of memory specified. 

578 This is to protect the system from using so much memory that it needs 

579 to resort to swapping, and thereby stall the computer. E.g. 

580 ``mimread('hunger_games.avi')``. 

581 

582 If the argument is a number, that will be used as the threshold number 

583 of bytes. 

584 

585 If the argument is a string, it will be interpreted as a number of bytes with 

586 SI/IEC prefixed units (e.g. '1kB', '250MiB', '80.3YB'). 

587 

588 - Units are case sensitive 

589 - k, M etc. represent a 1000-fold change, where Ki, Mi etc. represent 1024-fold 

590 - The "B" is optional, but if present, must be capitalised 

591 

592 If the argument is True, the default will be used, for compatibility reasons. 

593 

594 Default: '1GB' 

595 kwargs : ... 

596 Further keyword arguments are passed to the reader. See :func:`.help` 

597 to see what arguments are available for a particular format. 

598 """ 

599 

600 # used for mimread and mvolread 

601 nbyte_limit = to_nbytes(memtest, MEMTEST_DEFAULT_MVOL) 

602 

603 images = list() 

604 nbytes = 0 

605 imopen_args = decypher_format_arg(format) 

606 imopen_args["legacy_mode"] = True 

607 with imopen(uri, "rV", **imopen_args) as file: 

608 for image in file.iter(**kwargs): 

609 images.append(image) 

610 nbytes += image.nbytes 

611 if nbytes > nbyte_limit: 

612 raise RuntimeError( 

613 "imageio.mimread() has read over {}B of " 

614 "image data.\nStopped to avoid memory problems." 

615 " Use imageio.get_reader(), increase threshold, or memtest=False".format( 

616 int(nbyte_limit) 

617 ) 

618 ) 

619 

620 return images 

621 

622 

623def mvolwrite(uri, ims, format=None, **kwargs): 

624 """mvolwrite(uri, vols, format=None, **kwargs) 

625 

626 Write multiple volumes to the specified file. 

627 

628 Parameters 

629 ---------- 

630 uri : {str, pathlib.Path, file} 

631 The resource to write the volumes to, e.g. a filename, pathlib.Path 

632 or file object, see the docs for more info. 

633 ims : sequence of numpy arrays 

634 The image data. Each array must be NxMxL (or NxMxLxK if each 

635 voxel is a tuple). 

636 format : str 

637 The format to use to read the file. By default imageio selects 

638 the appropriate for you based on the filename and its contents. 

639 kwargs : ... 

640 Further keyword arguments are passed to the writer. See :func:`.help` 

641 to see what arguments are available for a particular format. 

642 """ 

643 

644 for im in ims: 

645 if not is_volume(im): 

646 raise ValueError("Image must be 3D, or 4D if each voxel is a tuple.") 

647 

648 imopen_args = decypher_format_arg(format) 

649 imopen_args["legacy_mode"] = True 

650 with imopen(uri, "wV", **imopen_args) as file: 

651 return file.write(ims, is_batch=True, **kwargs) 

652 

653 

654# aliases 

655read = get_reader 

656save = get_writer 

657imsave = imwrite 

658mimsave = mimwrite 

659volsave = volwrite 

660mvolsave = mvolwrite 

661 

662__all__ = [ 

663 "imread", 

664 "mimread", 

665 "volread", 

666 "mvolread", 

667 "imwrite", 

668 "mimwrite", 

669 "volwrite", 

670 "mvolwrite", 

671 # misc 

672 "help", 

673 "get_reader", 

674 "get_writer", 

675 "RETURN_BYTES", 

676]