Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/imageio-2.35.1-py3.8.egg/imageio/plugins/opencv.py: 5%

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

86 statements  

1"""Read/Write images using OpenCV. 

2 

3Backend Library: `OpenCV <https://opencv.org/>`_ 

4 

5This plugin wraps OpenCV (also known as ``cv2``), a popular image processing 

6library. Currently, it exposes OpenCVs image reading capability (no video or GIF 

7support yet); however, this may be added in future releases. 

8 

9Methods 

10------- 

11.. note:: 

12 Check the respective function for a list of supported kwargs and their 

13 documentation. 

14 

15.. autosummary:: 

16 :toctree: 

17 

18 OpenCVPlugin.read 

19 OpenCVPlugin.iter 

20 OpenCVPlugin.write 

21 OpenCVPlugin.properties 

22 OpenCVPlugin.metadata 

23 

24Pixel Formats (Colorspaces) 

25--------------------------- 

26 

27OpenCV is known to process images in BGR; however, most of the python ecosystem 

28(in particular matplotlib and other pydata libraries) use the RGB. As such, 

29images are converted to RGB, RGBA, or grayscale (where applicable) by default. 

30 

31""" 

32 

33import warnings 

34from pathlib import Path 

35from typing import Any, Dict, List, Optional, Union 

36 

37import cv2 

38import numpy as np 

39 

40from ..core import Request 

41from ..core.request import URI_BYTES, InitializationError, IOMode 

42from ..core.v3_plugin_api import ImageProperties, PluginV3 

43from ..typing import ArrayLike 

44 

45 

46class OpenCVPlugin(PluginV3): 

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

48 super().__init__(request) 

49 

50 self.file_handle = request.get_local_filename() 

51 if request._uri_type is URI_BYTES: 

52 self.filename = "<bytes>" 

53 else: 

54 self.filename = request.raw_uri 

55 

56 mode = request.mode.io_mode 

57 if mode == IOMode.read and not cv2.haveImageReader(self.file_handle): 

58 raise InitializationError(f"OpenCV can't read `{self.filename}`.") 

59 elif mode == IOMode.write and not cv2.haveImageWriter(self.file_handle): 

60 raise InitializationError(f"OpenCV can't write to `{self.filename}`.") 

61 

62 def read( 

63 self, 

64 *, 

65 index: int = None, 

66 colorspace: Union[int, str] = None, 

67 flags: int = cv2.IMREAD_COLOR, 

68 ) -> np.ndarray: 

69 """Read an image from the ImageResource. 

70 

71 Parameters 

72 ---------- 

73 index : int, Ellipsis 

74 If int, read the index-th image from the ImageResource. If ``...``, 

75 read all images from the ImageResource and stack them along a new, 

76 prepended, batch dimension. If None (default), use ``index=0`` if 

77 the image contains exactly one image and ``index=...`` otherwise. 

78 colorspace : str, int 

79 The colorspace to convert into after loading and before returning 

80 the image. If None (default) keep grayscale images as is, convert 

81 images with an alpha channel to ``RGBA`` and all other images to 

82 ``RGB``. If int, interpret ``colorspace`` as one of OpenCVs 

83 `conversion flags 

84 <https://docs.opencv.org/4.x/d8/d01/group__imgproc__color__conversions.html>`_ 

85 and use it for conversion. If str, convert the image into the given 

86 colorspace. Possible string values are: ``"RGB"``, ``"BGR"``, 

87 ``"RGBA"``, ``"BGRA"``, ``"GRAY"``, ``"HSV"``, or ``"LAB"``. 

88 flags : int 

89 The OpenCV flag(s) to pass to the reader. Refer to the `OpenCV docs 

90 <https://docs.opencv.org/4.x/d4/da8/group__imgcodecs.html#ga288b8b3da0892bd651fce07b3bbd3a56>`_ 

91 for details. 

92 

93 Returns 

94 ------- 

95 ndimage : np.ndarray 

96 The decoded image as a numpy array. 

97 

98 """ 

99 

100 if index is None: 

101 n_images = cv2.imcount(self.file_handle, flags) 

102 index = 0 if n_images == 1 else ... 

103 

104 if index is ...: 

105 retval, img = cv2.imreadmulti(self.file_handle, flags=flags) 

106 is_batch = True 

107 else: 

108 retval, img = cv2.imreadmulti(self.file_handle, index, 1, flags=flags) 

109 is_batch = False 

110 

111 if retval is False: 

112 raise ValueError(f"Could not read index `{index}` from `{self.filename}`.") 

113 

114 if img[0].ndim == 2: 

115 in_colorspace = "GRAY" 

116 out_colorspace = colorspace or "GRAY" 

117 elif img[0].shape[-1] == 4: 

118 in_colorspace = "BGRA" 

119 out_colorspace = colorspace or "RGBA" 

120 else: 

121 in_colorspace = "BGR" 

122 out_colorspace = colorspace or "RGB" 

123 

124 if isinstance(colorspace, int): 

125 cvt_space = colorspace 

126 elif in_colorspace == out_colorspace.upper(): 

127 cvt_space = None 

128 else: 

129 out_colorspace = out_colorspace.upper() 

130 cvt_space = getattr(cv2, f"COLOR_{in_colorspace}2{out_colorspace}") 

131 

132 if cvt_space is not None: 

133 img = np.stack([cv2.cvtColor(x, cvt_space) for x in img]) 

134 else: 

135 img = np.stack(img) 

136 

137 return img if is_batch else img[0] 

138 

139 def iter( 

140 self, 

141 colorspace: Union[int, str] = None, 

142 flags: int = cv2.IMREAD_COLOR, 

143 ) -> np.ndarray: 

144 """Yield images from the ImageResource. 

145 

146 Parameters 

147 ---------- 

148 colorspace : str, int 

149 The colorspace to convert into after loading and before returning 

150 the image. If None (default) keep grayscale images as is, convert 

151 images with an alpha channel to ``RGBA`` and all other images to 

152 ``RGB``. If int, interpret ``colorspace`` as one of OpenCVs 

153 `conversion flags 

154 <https://docs.opencv.org/4.x/d8/d01/group__imgproc__color__conversions.html>`_ 

155 and use it for conversion. If str, convert the image into the given 

156 colorspace. Possible string values are: ``"RGB"``, ``"BGR"``, 

157 ``"RGBA"``, ``"BGRA"``, ``"GRAY"``, ``"HSV"``, or ``"LAB"``. 

158 flags : int 

159 The OpenCV flag(s) to pass to the reader. Refer to the `OpenCV docs 

160 <https://docs.opencv.org/4.x/d4/da8/group__imgcodecs.html#ga288b8b3da0892bd651fce07b3bbd3a56>`_ 

161 for details. 

162 

163 Yields 

164 ------ 

165 ndimage : np.ndarray 

166 The decoded image as a numpy array. 

167 

168 """ 

169 for idx in range(cv2.imcount(self.file_handle)): 

170 yield self.read(index=idx, flags=flags, colorspace=colorspace) 

171 

172 def write( 

173 self, 

174 ndimage: Union[ArrayLike, List[ArrayLike]], 

175 is_batch: bool = False, 

176 params: List[int] = None, 

177 ) -> Optional[bytes]: 

178 """Save an ndimage in the ImageResource. 

179 

180 Parameters 

181 ---------- 

182 ndimage : ArrayLike, List[ArrayLike] 

183 The image data that will be written to the file. It is either a 

184 single image, a batch of images, or a list of images. 

185 is_batch : bool 

186 If True, the provided ndimage is a batch of images. If False (default), the 

187 provided ndimage is a single image. If the provided ndimage is a list of images, 

188 this parameter has no effect. 

189 params : List[int] 

190 A list of parameters that will be passed to OpenCVs imwrite or 

191 imwritemulti functions. Possible values are documented in the 

192 `OpenCV documentation 

193 <https://docs.opencv.org/4.x/d4/da8/group__imgcodecs.html#gabbc7ef1aa2edfaa87772f1202d67e0ce>`_. 

194 

195 Returns 

196 ------- 

197 encoded_image : bytes, None 

198 If the ImageResource is ``"<bytes>"`` the call to write returns the 

199 encoded image as a bytes string. Otherwise it returns None. 

200 

201 """ 

202 

203 if isinstance(ndimage, list): 

204 ndimage = np.stack(ndimage, axis=0) 

205 elif not is_batch: 

206 ndimage = ndimage[None, ...] 

207 

208 if ndimage[0].ndim == 2: 

209 n_channels = 1 

210 else: 

211 n_channels = ndimage[0].shape[-1] 

212 

213 if n_channels == 1: 

214 ndimage_cv2 = [x for x in ndimage] 

215 elif n_channels == 4: 

216 ndimage_cv2 = [cv2.cvtColor(x, cv2.COLOR_RGBA2BGRA) for x in ndimage] 

217 else: 

218 ndimage_cv2 = [cv2.cvtColor(x, cv2.COLOR_RGB2BGR) for x in ndimage] 

219 

220 retval = cv2.imwritemulti(self.file_handle, ndimage_cv2, params) 

221 

222 if retval is False: 

223 # not sure what scenario would trigger this, but 

224 # it can occur theoretically. 

225 raise IOError("OpenCV failed to write.") # pragma: no cover 

226 

227 if self.request._uri_type == URI_BYTES: 

228 return Path(self.file_handle).read_bytes() 

229 

230 def properties( 

231 self, 

232 index: int = None, 

233 colorspace: Union[int, str] = None, 

234 flags: int = cv2.IMREAD_COLOR, 

235 ) -> ImageProperties: 

236 """Standardized image metadata. 

237 

238 Parameters 

239 ---------- 

240 index : int, Ellipsis 

241 If int, get the properties of the index-th image in the 

242 ImageResource. If ``...``, get the properties of the image stack 

243 that contains all images. If None (default), use ``index=0`` if the 

244 image contains exactly one image and ``index=...`` otherwise. 

245 colorspace : str, int 

246 The colorspace to convert into after loading and before returning 

247 the image. If None (default) keep grayscale images as is, convert 

248 images with an alpha channel to ``RGBA`` and all other images to 

249 ``RGB``. If int, interpret ``colorspace`` as one of OpenCVs 

250 `conversion flags 

251 <https://docs.opencv.org/4.x/d8/d01/group__imgproc__color__conversions.html>`_ 

252 and use it for conversion. If str, convert the image into the given 

253 colorspace. Possible string values are: ``"RGB"``, ``"BGR"``, 

254 ``"RGBA"``, ``"BGRA"``, ``"GRAY"``, ``"HSV"``, or ``"LAB"``. 

255 flags : int 

256 The OpenCV flag(s) to pass to the reader. Refer to the `OpenCV docs 

257 <https://docs.opencv.org/4.x/d4/da8/group__imgcodecs.html#ga288b8b3da0892bd651fce07b3bbd3a56>`_ 

258 for details. 

259 

260 Returns 

261 ------- 

262 props : ImageProperties 

263 A dataclass filled with standardized image metadata. 

264 

265 Notes 

266 ----- 

267 Reading properties with OpenCV involves decoding pixel data, because 

268 OpenCV doesn't provide a direct way to access metadata. 

269 

270 """ 

271 

272 if index is None: 

273 n_images = cv2.imcount(self.file_handle, flags) 

274 is_batch = n_images > 1 

275 elif index is Ellipsis: 

276 n_images = cv2.imcount(self.file_handle, flags) 

277 is_batch = True 

278 else: 

279 is_batch = False 

280 

281 # unfortunately, OpenCV doesn't allow reading shape without reading pixel data 

282 if is_batch: 

283 img = self.read(index=0, flags=flags, colorspace=colorspace) 

284 return ImageProperties( 

285 shape=(n_images, *img.shape), 

286 dtype=img.dtype, 

287 n_images=n_images, 

288 is_batch=True, 

289 ) 

290 

291 img = self.read(index=index, flags=flags, colorspace=colorspace) 

292 return ImageProperties(shape=img.shape, dtype=img.dtype, is_batch=False) 

293 

294 def metadata( 

295 self, index: int = None, exclude_applied: bool = True 

296 ) -> Dict[str, Any]: 

297 """Format-specific metadata. 

298 

299 .. warning:: 

300 OpenCV does not support reading metadata. When called, this function 

301 will raise a ``NotImplementedError``. 

302 

303 Parameters 

304 ---------- 

305 index : int 

306 This parameter has no effect. 

307 exclude_applied : bool 

308 This parameter has no effect. 

309 

310 """ 

311 

312 warnings.warn("OpenCV does not support reading metadata.", UserWarning) 

313 return dict()