Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/imageio-2.35.1-py3.8.egg/imageio/core/v3_plugin_api.py: 81%

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

36 statements  

1from . import Request 

2from ..typing import ArrayLike 

3import numpy as np 

4from typing import Optional, Dict, Any, Tuple, Union, List, Iterator 

5from dataclasses import dataclass 

6 

7 

8@dataclass 

9class ImageProperties: 

10 """Standardized Metadata 

11 

12 ImageProperties represent a set of standardized metadata that is available 

13 under the same name for every supported format. If the ImageResource (or 

14 format) does not specify the value, a sensible default value is chosen 

15 instead. 

16 

17 Attributes 

18 ---------- 

19 shape : Tuple[int, ...] 

20 The shape of the loaded ndimage. 

21 dtype : np.dtype 

22 The dtype of the loaded ndimage. 

23 n_images : int 

24 Number of images in the file if ``index=...``, `None` for single images. 

25 is_batch : bool 

26 If True, the first dimension of the ndimage represents a batch dimension 

27 along which several images are stacked. 

28 spacing : Tuple 

29 A tuple describing the spacing between pixels along each axis of the 

30 ndimage. If the spacing is uniform along an axis the value corresponding 

31 to that axis is a single float. If the spacing is non-uniform, the value 

32 corresponding to that axis is a tuple in which the i-th element 

33 indicates the spacing between the i-th and (i+1)-th pixel along that 

34 axis. 

35 

36 """ 

37 

38 shape: Tuple[int, ...] 

39 dtype: np.dtype 

40 n_images: Optional[int] = None 

41 is_batch: bool = False 

42 spacing: Optional[tuple] = None 

43 

44 

45class PluginV3: 

46 """A ImageIO Plugin. 

47 

48 This is an abstract plugin that documents the v3 plugin API interface. A 

49 plugin is an adapter/wrapper around a backend that converts a request from 

50 iio.core (e.g., read an image from file) into a sequence of instructions for 

51 the backend that fulfill the request. 

52 

53 Plugin authors may choose to subclass this class when implementing a new 

54 plugin, but aren't obliged to do so. As long as the plugin class implements 

55 the interface (methods) described below the ImageIO core will treat it just 

56 like any other plugin. 

57 

58 

59 Parameters 

60 ---------- 

61 request : iio.Request 

62 A request object that represents the users intent. It provides a 

63 standard interface to access the various ImageResources and serves them 

64 to the plugin as a file object (or file). Check the docs for details. 

65 **kwargs : Any 

66 Additional configuration arguments for the plugin or backend. Usually 

67 these match the configuration arguments available on the backend and 

68 are forwarded to it. 

69 

70 

71 Raises 

72 ------ 

73 InitializationError 

74 During ``__init__`` the plugin tests if it can fulfill the request. If 

75 it can't, e.g., because the request points to a file in the wrong 

76 format, then it should raise an ``InitializationError`` and provide a 

77 reason for failure. This reason may be reported to the user. 

78 ImportError 

79 Plugins will be imported dynamically when listed in 

80 ``iio.config.known_plugins`` to fulfill requests. This way, users only 

81 have to load plugins/backends they actually use. If this plugin's backend 

82 is not installed, it should raise an ``ImportError`` either during 

83 module import or during class construction. 

84 

85 Notes 

86 ----- 

87 Upon successful construction the plugin takes ownership of the provided 

88 request. This means that it is the plugin's responsibility to call 

89 request.finish() to close the resource when it is no longer needed. 

90 

91 Plugins _must_ implement a context manager that closes and cleans any 

92 resources held by the plugin upon exit. 

93 

94 """ 

95 

96 def __init__(self, request: Request) -> None: 

97 """Initialize a new Plugin Instance. 

98 

99 See Plugin's docstring for detailed documentation. 

100 

101 Notes 

102 ----- 

103 The implementation here stores the request as a local variable that is 

104 exposed using a @property below. If you inherit from PluginV3, remember 

105 to call ``super().__init__(request)``. 

106 

107 """ 

108 

109 self._request = request 

110 

111 def read(self, *, index: int = 0) -> np.ndarray: 

112 """Read a ndimage. 

113 

114 The ``read`` method loads a (single) ndimage, located at ``index`` from 

115 the requested ImageResource. 

116 

117 It is at the plugin's descretion to decide (and document) what 

118 constitutes a single ndimage. A sensible way to make this decision is to 

119 choose based on the ImageResource's format and on what users will expect 

120 from such a format. For example, a sensible choice for a TIFF file 

121 produced by an ImageJ hyperstack is to read it as a volumetric ndimage 

122 (1 color dimension followed by 3 spatial dimensions). On the other hand, 

123 a sensible choice for a MP4 file produced by Davinci Resolve is to treat 

124 each frame as a ndimage (2 spatial dimensions followed by 1 color 

125 dimension). 

126 

127 The value ``index=None`` is special. It requests the plugin to load all 

128 ndimages in the file and stack them along a new first axis. For example, 

129 if a MP4 file is read with ``index=None`` and the plugin identifies 

130 single frames as ndimages, then the plugin should read all frames and 

131 stack them into a new ndimage which now contains a time axis as its 

132 first axis. If a PNG file (single image format) is read with 

133 ``index=None`` the plugin does a very similar thing: It loads all 

134 ndimages in the file (here it's just one) and stacks them along a new 

135 first axis, effectively prepending an axis with size 1 to the image. If 

136 a plugin does not wish to support ``index=None`` it should set a more 

137 sensible default and raise a ``ValueError`` when requested to read using 

138 ``index=None``. 

139 

140 Parameters 

141 ---------- 

142 index : int 

143 If the ImageResource contains multiple ndimages, and index is an 

144 integer, select the index-th ndimage from among them and return it. 

145 If index is an ellipsis (...), read all ndimages in the file and 

146 stack them along a new batch dimension. If index is None, let the 

147 plugin decide. If the index is out of bounds a ``ValueError`` is 

148 raised. 

149 **kwargs : Any 

150 The read method may accept any number of plugin-specific keyword 

151 arguments to further customize the read behavior. Usually these 

152 match the arguments available on the backend and are forwarded to 

153 it. 

154 

155 Returns 

156 ------- 

157 ndimage : np.ndarray 

158 A ndimage containing decoded pixel data (sometimes called bitmap). 

159 

160 Notes 

161 ----- 

162 The ImageResource from which the plugin should read is managed by the 

163 provided request object. Directly accessing the managed ImageResource is 

164 _not_ permitted. Instead, you can get FileLike access to the 

165 ImageResource via request.get_file(). 

166 

167 If the backend doesn't support reading from FileLike objects, you can 

168 request a temporary file to pass to the backend via 

169 ``request.get_local_filename()``. This is, however, not very performant 

170 (involves copying the Request's content into a temporary file), so you 

171 should avoid doing this whenever possible. Consider it a fallback method 

172 in case all else fails. 

173 

174 """ 

175 raise NotImplementedError() 

176 

177 def write(self, ndimage: Union[ArrayLike, List[ArrayLike]]) -> Optional[bytes]: 

178 """Write a ndimage to a ImageResource. 

179 

180 The ``write`` method encodes the given ndimage into the format handled 

181 by the backend and writes it to the ImageResource. It overwrites 

182 any content that may have been previously stored in the file. 

183 

184 If the backend supports only a single format then it must check if 

185 the ImageResource matches that format and raise an exception if not. 

186 Typically, this should be done during initialization in the form of a 

187 ``InitializationError``. 

188 

189 If the backend supports more than one format it must determine the 

190 requested/desired format. Usually this can be done by inspecting the 

191 ImageResource (e.g., by checking ``request.extension``), or by providing 

192 a mechanism to explicitly set the format (perhaps with a - sensible - 

193 default value). If the plugin can not determine the desired format, it 

194 **must not** write to the ImageResource, but raise an exception instead. 

195 

196 If the backend supports at least one format that can hold multiple 

197 ndimages it should be capable of handling ndimage batches and lists of 

198 ndimages. If the ``ndimage`` input is a list of ndimages, the plugin 

199 should not assume that the ndimages are not stackable, i.e., ndimages 

200 may have different shapes. Otherwise, the ``ndimage`` may be a batch of 

201 multiple ndimages stacked along the first axis of the array. The plugin 

202 must be able to discover this, either automatically or via additional 

203 `kwargs`. If there is ambiguity in the process, the plugin must clearly 

204 document what happens in such cases and, if possible, describe how to 

205 resolve this ambiguity. 

206 

207 Parameters 

208 ---------- 

209 ndimage : ArrayLike 

210 The ndimage to encode and write to the current ImageResource. 

211 **kwargs : Any 

212 The write method may accept any number of plugin-specific keyword 

213 arguments to customize the writing behavior. Usually these match the 

214 arguments available on the backend and are forwarded to it. 

215 

216 Returns 

217 ------- 

218 encoded_image : bytes or None 

219 If the chosen ImageResource is the special target ``"<bytes>"`` then 

220 write should return a byte string containing the encoded image data. 

221 Otherwise, it returns None. 

222 

223 Notes 

224 ----- 

225 The ImageResource to which the plugin should write to is managed by the 

226 provided request object. Directly accessing the managed ImageResource is 

227 _not_ permitted. Instead, you can get FileLike access to the 

228 ImageResource via request.get_file(). 

229 

230 If the backend doesn't support writing to FileLike objects, you can 

231 request a temporary file to pass to the backend via 

232 ``request.get_local_filename()``. This is, however, not very performant 

233 (involves copying the Request's content from a temporary file), so you 

234 should avoid doing this whenever possible. Consider it a fallback method 

235 in case all else fails. 

236 

237 """ 

238 raise NotImplementedError() 

239 

240 def iter(self) -> Iterator[np.ndarray]: 

241 """Iterate the ImageResource. 

242 

243 This method returns a generator that yields ndimages in the order in which 

244 they appear in the file. This is roughly equivalent to:: 

245 

246 idx = 0 

247 while True: 

248 try: 

249 yield self.read(index=idx) 

250 except ValueError: 

251 break 

252 

253 It works very similar to ``read``, and you can consult the documentation 

254 of that method for additional information on desired behavior. 

255 

256 Parameters 

257 ---------- 

258 **kwargs : Any 

259 The iter method may accept any number of plugin-specific keyword 

260 arguments to further customize the reading/iteration behavior. 

261 Usually these match the arguments available on the backend and are 

262 forwarded to it. 

263 

264 Yields 

265 ------ 

266 ndimage : np.ndarray 

267 A ndimage containing decoded pixel data (sometimes called bitmap). 

268 

269 See Also 

270 -------- 

271 PluginV3.read 

272 

273 """ 

274 raise NotImplementedError() 

275 

276 def properties(self, index: int = 0) -> ImageProperties: 

277 """Standardized ndimage metadata. 

278 

279 Parameters 

280 ---------- 

281 index : int 

282 If the ImageResource contains multiple ndimages, and index is an 

283 integer, select the index-th ndimage from among them and return its 

284 properties. If index is an ellipsis (...), read all ndimages in the file 

285 and stack them along a new batch dimension and return their properties. 

286 If index is None, the plugin decides the default. 

287 

288 Returns 

289 ------- 

290 properties : ImageProperties 

291 A dataclass filled with standardized image metadata. 

292 

293 """ 

294 raise NotImplementedError() 

295 

296 def metadata(self, index: int = 0, exclude_applied: bool = True) -> Dict[str, Any]: 

297 """Format-Specific ndimage metadata. 

298 

299 The method reads metadata stored in the ImageResource and returns it as 

300 a python dict. The plugin is free to choose which name to give a piece 

301 of metadata; however, if possible, it should match the name given by the 

302 format. There is no requirement regarding the fields a plugin must 

303 expose; however, if a plugin does expose any,``exclude_applied`` applies 

304 to these fields. 

305 

306 If the plugin does return metadata items, it must check the value of 

307 ``exclude_applied`` before returning them. If ``exclude applied`` is 

308 True, then any metadata item that would be applied to an ndimage 

309 returned by ``read`` (or ``iter``) must not be returned. This is done to 

310 avoid confusion; for example, if an ImageResource defines the ExIF 

311 rotation tag, and the plugin applies the rotation to the data before 

312 returning it, then ``exclude_applied`` prevents confusion on whether the 

313 tag was already applied or not. 

314 

315 The `kwarg` ``index`` behaves similar to its counterpart in ``read`` 

316 with one exception: If the ``index`` is None, then global metadata is 

317 returned instead of returning a combination of all metadata items. If 

318 there is no global metadata, the Plugin should return an empty dict or 

319 raise an exception. 

320 

321 Parameters 

322 ---------- 

323 index : int 

324 If the ImageResource contains multiple ndimages, and index is an 

325 integer, select the index-th ndimage from among them and return its 

326 metadata. If index is an ellipsis (...), return global metadata. If 

327 index is None, the plugin decides the default. 

328 exclude_applied : bool 

329 If True (default), do not report metadata fields that the plugin 

330 would apply/consume while reading the image. 

331 

332 Returns 

333 ------- 

334 metadata : dict 

335 A dictionary filled with format-specific metadata fields and their 

336 values. 

337 

338 """ 

339 raise NotImplementedError() 

340 

341 def close(self) -> None: 

342 """Close the ImageResource. 

343 

344 This method allows a plugin to behave similar to the python built-in ``open``:: 

345 

346 image_file = my_plugin(Request, "r") 

347 ... 

348 image_file.close() 

349 

350 It is used by the context manager and deconstructor below to avoid leaking 

351 ImageResources. If the plugin has no other cleanup to do it doesn't have 

352 to overwrite this method itself and can rely on the implementation 

353 below. 

354 

355 """ 

356 

357 self.request.finish() 

358 

359 @property 

360 def request(self) -> Request: 

361 return self._request 

362 

363 def __enter__(self) -> "PluginV3": 

364 return self 

365 

366 def __exit__(self, type, value, traceback) -> None: 

367 self.close() 

368 

369 def __del__(self) -> None: 

370 self.close()