Skip to content

Commit 009cfe6

Browse files
authored
fileio related interfaces should keep bc (open-mmlab#2539)
* fileio related interfaces should keep bc * fix format * update unit tests of transforms * update format * lazily infer client until calling get function of fileio
1 parent 34bdf44 commit 009cfe6

File tree

4 files changed

+175
-17
lines changed

4 files changed

+175
-17
lines changed

mmcv/image/io.py

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ def imread(img_or_path: Union[np.ndarray, str, Path],
145145
flag: str = 'color',
146146
channel_order: str = 'bgr',
147147
backend: Optional[str] = None,
148+
file_client_args: Optional[dict] = None,
148149
*,
149150
backend_args: Optional[dict] = None) -> np.ndarray:
150151
"""Read an image.
@@ -166,12 +167,18 @@ def imread(img_or_path: Union[np.ndarray, str, Path],
166167
`cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`.
167168
If backend is None, the global imread_backend specified by
168169
``mmcv.use_backend()`` will be used. Default: None.
170+
file_client_args (dict, optional): Arguments to instantiate a
171+
FileClient. See :class:`mmengine.fileio.FileClient` for details.
172+
Default: None. It will be deprecated in future. Please use
173+
``backend_args`` instead.
174+
Deprecated in version 2.0.0rc4.
169175
backend_args (dict, optional): Instantiates the corresponding file
170176
backend. It may contain `backend` key to specify the file
171177
backend. If it contains, the file backend corresponding to this
172178
value will be used and initialized with the remaining values,
173179
otherwise the corresponding file backend will be selected
174180
based on the prefix of the file path. Defaults to None.
181+
New in version 2.0.0rc4.
175182
176183
Returns:
177184
ndarray: Loaded image array.
@@ -195,13 +202,27 @@ def imread(img_or_path: Union[np.ndarray, str, Path],
195202
>>> img = mmcv.imread(http_img_path, backend_args={
196203
... 'backend': 'http'})
197204
"""
205+
if file_client_args is not None:
206+
warnings.warn(
207+
'"file_client_args" will be deprecated in future. '
208+
'Please use "backend_args" instead', DeprecationWarning)
209+
if backend_args is not None:
210+
raise ValueError(
211+
'"file_client_args" and "backend_args" cannot be set at the '
212+
'same time.')
213+
198214
if isinstance(img_or_path, Path):
199215
img_or_path = str(img_or_path)
200216

201217
if isinstance(img_or_path, np.ndarray):
202218
return img_or_path
203219
elif is_str(img_or_path):
204-
img_bytes = fileio.get(img_or_path, backend_args=backend_args)
220+
if file_client_args is not None:
221+
file_client = fileio.FileClient.infer_client(
222+
file_client_args, img_or_path)
223+
img_bytes = file_client.get(img_or_path)
224+
else:
225+
img_bytes = fileio.get(img_or_path, backend_args=backend_args)
205226
return imfrombytes(img_bytes, flag, channel_order, backend)
206227
else:
207228
raise TypeError('"img" must be a numpy array or a str or '
@@ -271,6 +292,7 @@ def imwrite(img: np.ndarray,
271292
file_path: str,
272293
params: Optional[list] = None,
273294
auto_mkdir: Optional[bool] = None,
295+
file_client_args: Optional[dict] = None,
274296
*,
275297
backend_args: Optional[dict] = None) -> bool:
276298
"""Write image to file.
@@ -285,12 +307,18 @@ def imwrite(img: np.ndarray,
285307
params (None or list): Same as opencv :func:`imwrite` interface.
286308
auto_mkdir (bool): If the parent folder of `file_path` does not exist,
287309
whether to create it automatically. It will be deprecated.
310+
file_client_args (dict, optional): Arguments to instantiate a
311+
FileClient. See :class:`mmengine.fileio.FileClient` for details.
312+
Default: None. It will be deprecated in future. Please use
313+
``backend_args`` instead.
314+
Deprecated in version 2.0.0rc4.
288315
backend_args (dict, optional): Instantiates the corresponding file
289316
backend. It may contain `backend` key to specify the file
290317
backend. If it contains, the file backend corresponding to this
291318
value will be used and initialized with the remaining values,
292319
otherwise the corresponding file backend will be selected
293320
based on the prefix of the file path. Defaults to None.
321+
New in version 2.0.0rc4.
294322
295323
Returns:
296324
bool: Successful or not.
@@ -304,6 +332,15 @@ def imwrite(img: np.ndarray,
304332
>>> ret = mmcv.imwrite(img, 's3://bucket/img.jpg', backend_args={
305333
... 'backend': 'petrel'})
306334
"""
335+
if file_client_args is not None:
336+
warnings.warn(
337+
'"file_client_args" will be deprecated in future. '
338+
'Please use "backend_args" instead', DeprecationWarning)
339+
if backend_args is not None:
340+
raise ValueError(
341+
'"file_client_args" and "backend_args" cannot be set at the '
342+
'same time.')
343+
307344
assert is_filepath(file_path)
308345
file_path = str(file_path)
309346
if auto_mkdir is not None:
@@ -317,6 +354,11 @@ def imwrite(img: np.ndarray,
317354
# format is '.jpg'.
318355
flag, img_buff = cv2.imencode(img_ext, img, params)
319356

320-
fileio.put(img_buff.tobytes(), file_path, backend_args=backend_args)
357+
if file_client_args is not None:
358+
file_client = fileio.FileClient.infer_client(file_client_args,
359+
file_path)
360+
file_client.put(img_buff.tobytes(), file_path)
361+
else:
362+
fileio.put(img_buff.tobytes(), file_path, backend_args=backend_args)
321363

322364
return flag

mmcv/transforms/loading.py

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Copyright (c) OpenMMLab. All rights reserved.
2+
import warnings
23
from typing import Optional
34

45
import mmengine.fileio as fileio
@@ -33,6 +34,11 @@ class LoadImageFromFile(BaseTransform):
3334
argument for :func:`mmcv.imfrombytes`.
3435
See :func:`mmcv.imfrombytes` for details.
3536
Defaults to 'cv2'.
37+
file_client_args (dict, optional): Arguments to instantiate a
38+
FileClient. See :class:`mmengine.fileio.FileClient` for details.
39+
Defaults to None. It will be deprecated in future. Please use
40+
``backend_args`` instead.
41+
Deprecated in version 2.0.0rc4.
3642
ignore_empty (bool): Whether to allow loading empty image or file path
3743
not existent. Defaults to False.
3844
backend_args (dict, optional): Instantiates the corresponding file
@@ -41,20 +47,36 @@ class LoadImageFromFile(BaseTransform):
4147
value will be used and initialized with the remaining values,
4248
otherwise the corresponding file backend will be selected
4349
based on the prefix of the file path. Defaults to None.
50+
New in version 2.0.0rc4.
4451
"""
4552

4653
def __init__(self,
4754
to_float32: bool = False,
4855
color_type: str = 'color',
4956
imdecode_backend: str = 'cv2',
57+
file_client_args: Optional[dict] = None,
5058
ignore_empty: bool = False,
5159
*,
5260
backend_args: Optional[dict] = None) -> None:
5361
self.ignore_empty = ignore_empty
5462
self.to_float32 = to_float32
5563
self.color_type = color_type
5664
self.imdecode_backend = imdecode_backend
57-
self.backend_args = backend_args
65+
66+
self.file_client_args: Optional[dict] = None
67+
self.backend_args: Optional[dict] = None
68+
if file_client_args is not None:
69+
warnings.warn(
70+
'"file_client_args" will be deprecated in future. '
71+
'Please use "backend_args" instead', DeprecationWarning)
72+
if backend_args is not None:
73+
raise ValueError(
74+
'"file_client_args" and "backend_args" cannot be set '
75+
'at the same time.')
76+
77+
self.file_client_args = file_client_args.copy()
78+
if backend_args is not None:
79+
self.backend_args = backend_args.copy()
5880

5981
def transform(self, results: dict) -> Optional[dict]:
6082
"""Functions to load image.
@@ -69,7 +91,13 @@ def transform(self, results: dict) -> Optional[dict]:
6991

7092
filename = results['img_path']
7193
try:
72-
img_bytes = fileio.get(filename, backend_args=self.backend_args)
94+
if self.file_client_args is not None:
95+
file_client = fileio.FileClient.infer_client(
96+
self.file_client_args, filename)
97+
img_bytes = file_client.get(filename)
98+
else:
99+
img_bytes = fileio.get(
100+
filename, backend_args=self.backend_args)
73101
img = mmcv.imfrombytes(
74102
img_bytes, flag=self.color_type, backend=self.imdecode_backend)
75103
except Exception as e:
@@ -90,12 +118,12 @@ def __repr__(self):
90118
f'ignore_empty={self.ignore_empty}, '
91119
f'to_float32={self.to_float32}, '
92120
f"color_type='{self.color_type}', "
93-
f"imdecode_backend='{self.imdecode_backend}'")
121+
f"imdecode_backend='{self.imdecode_backend}', ")
94122

95-
if self.backend_args is not None:
96-
repr_str += f', backend_args={self.backend_args})'
123+
if self.file_client_args is not None:
124+
repr_str += f'file_client_args={self.file_client_args})'
97125
else:
98-
repr_str += ')'
126+
repr_str += f'backend_args={self.backend_args})'
99127

100128
return repr_str
101129

@@ -177,12 +205,18 @@ class LoadAnnotations(BaseTransform):
177205
argument for :func:`mmcv.imfrombytes`.
178206
See :func:`mmcv.imfrombytes` for details.
179207
Defaults to 'cv2'.
208+
file_client_args (dict, optional): Arguments to instantiate a
209+
FileClient. See :class:`mmengine.fileio.FileClient` for details.
210+
Defaults to None. It will be deprecated in future. Please use
211+
``backend_args`` instead.
212+
Deprecated in version 2.0.0rc4.
180213
backend_args (dict, optional): Instantiates the corresponding file
181214
backend. It may contain `backend` key to specify the file
182215
backend. If it contains, the file backend corresponding to this
183216
value will be used and initialized with the remaining values,
184217
otherwise the corresponding file backend will be selected
185218
based on the prefix of the file path. Defaults to None.
219+
New in version 2.0.0rc4.
186220
"""
187221

188222
def __init__(
@@ -192,6 +226,7 @@ def __init__(
192226
with_seg: bool = False,
193227
with_keypoints: bool = False,
194228
imdecode_backend: str = 'cv2',
229+
file_client_args: Optional[dict] = None,
195230
*,
196231
backend_args: Optional[dict] = None,
197232
) -> None:
@@ -201,7 +236,21 @@ def __init__(
201236
self.with_seg = with_seg
202237
self.with_keypoints = with_keypoints
203238
self.imdecode_backend = imdecode_backend
204-
self.backend_args = backend_args
239+
240+
self.file_client_args: Optional[dict] = None
241+
self.backend_args: Optional[dict] = None
242+
if file_client_args is not None:
243+
warnings.warn(
244+
'"file_client_args" will be deprecated in future. '
245+
'Please use "backend_args" instead', DeprecationWarning)
246+
if backend_args is not None:
247+
raise ValueError(
248+
'"file_client_args" and "backend_args" cannot be set '
249+
'at the same time.')
250+
251+
self.file_client_args = file_client_args.copy()
252+
if backend_args is not None:
253+
self.backend_args = backend_args.copy()
205254

206255
def _load_bboxes(self, results: dict) -> None:
207256
"""Private function to load bounding box annotations.
@@ -245,9 +294,14 @@ def _load_seg_map(self, results: dict) -> None:
245294
Returns:
246295
dict: The dict contains loaded semantic segmentation annotations.
247296
"""
297+
if self.file_client_args is not None:
298+
file_client = fileio.FileClient.infer_client(
299+
self.file_client_args, results['seg_map_path'])
300+
img_bytes = file_client.get(results['seg_map_path'])
301+
else:
302+
img_bytes = fileio.get(
303+
results['seg_map_path'], backend_args=self.backend_args)
248304

249-
img_bytes = fileio.get(
250-
results['seg_map_path'], backend_args=self.backend_args)
251305
results['gt_seg_map'] = mmcv.imfrombytes(
252306
img_bytes, flag='unchanged',
253307
backend=self.imdecode_backend).squeeze()
@@ -296,11 +350,11 @@ def __repr__(self) -> str:
296350
repr_str += f'with_label={self.with_label}, '
297351
repr_str += f'with_seg={self.with_seg}, '
298352
repr_str += f'with_keypoints={self.with_keypoints}, '
299-
repr_str += f"imdecode_backend='{self.imdecode_backend}'"
353+
repr_str += f"imdecode_backend='{self.imdecode_backend}', "
300354

301-
if self.backend_args is not None:
302-
repr_str += f', backend_args={self.backend_args})'
355+
if self.file_client_args is not None:
356+
repr_str += f'file_client_args={self.file_client_args})'
303357
else:
304-
repr_str += ')'
358+
repr_str += f'backend_args={self.backend_args})'
305359

306360
return repr_str

tests/test_image/test_io.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ def test_imread(self):
6060
# backend cv2
6161
mmcv.use_backend('cv2')
6262

63+
# file_client_args and backend_args can not be both set
64+
with pytest.raises(
65+
ValueError,
66+
match='"file_client_args" and "backend_args" cannot be set'):
67+
mmcv.imread(
68+
self.img_path,
69+
file_client_args={'backend': 'disk'},
70+
backend_args={'backend': 'disk'})
71+
6372
# HardDiskBackend
6473
img_cv2_color_bgr = mmcv.imread(self.img_path)
6574
assert img_cv2_color_bgr.shape == (300, 400, 3)
@@ -95,6 +104,16 @@ def test_imread(self):
95104
PetrelBackend, 'get',
96105
return_value=img_cv2_color_bgr) as mock_method:
97106
img_cv2_color_bgr_petrel = mmcv.imread(self.s3_path, backend='cv2')
107+
img_cv2_color_bgr_petrel_with_args = mmcv.imread(
108+
self.s3_path,
109+
backend='cv2',
110+
file_client_args={'backend': 'petrel'})
111+
mock_method.assert_called()
112+
assert_array_equal(img_cv2_color_bgr_petrel,
113+
img_cv2_color_bgr_petrel_with_args)
114+
115+
mock_method.reset_mock()
116+
98117
img_cv2_color_bgr_petrel_with_args = mmcv.imread(
99118
self.s3_path,
100119
backend='cv2',
@@ -109,6 +128,16 @@ def test_imread(self):
109128
HTTPBackend, 'get',
110129
return_value=img_cv2_color_bgr) as mock_method:
111130
img_cv2_color_bgr_http = mmcv.imread(self.http_path, backend='cv2')
131+
img_cv2_color_bgr_http_with_args = mmcv.imread(
132+
self.http_path,
133+
backend='cv2',
134+
file_client_args={'backend': 'http'})
135+
mock_method.assert_called()
136+
assert_array_equal(img_cv2_color_bgr_http,
137+
img_cv2_color_bgr_http_with_args)
138+
139+
mock_method.reset_mock()
140+
112141
img_cv2_color_bgr_http_with_args = mmcv.imread(
113142
self.http_path,
114143
backend='cv2',
@@ -358,6 +387,16 @@ def test_imwrite(self):
358387
img = mmcv.imread(self.img_path)
359388
out_file = osp.join(tempfile.gettempdir(), 'mmcv_test.jpg')
360389

390+
# file_client_args and backend_args can not be both set
391+
with pytest.raises(
392+
ValueError,
393+
match='"file_client_args" and "backend_args" cannot be set'):
394+
mmcv.imwrite(
395+
img,
396+
out_file,
397+
file_client_args={'backend': 'disk'},
398+
backend_args={'backend': 'disk'})
399+
361400
mmcv.imwrite(img, out_file)
362401
rewrite_img = mmcv.imread(out_file)
363402
os.remove(out_file)
@@ -367,7 +406,13 @@ def test_imwrite(self):
367406
with patch.object(
368407
PetrelBackend, 'put', return_value=None) as mock_method:
369408
ret = mmcv.imwrite(img, self.s3_path)
409+
ret_with_args = mmcv.imwrite(
410+
img, self.s3_path, file_client_args={'backend': 'petrel'})
370411
assert ret
412+
assert ret_with_args
413+
mock_method.assert_called()
414+
415+
mock_method.reset_mock()
371416

372417
ret_with_args = mmcv.imwrite(
373418
img, self.s3_path, backend_args={'backend': 'petrel'})

0 commit comments

Comments
 (0)