MMCV核心组件FileHandler
注意:在2.0.0版本以后,FileHandler
和FileClient
的功能已经转到mmengine中了(它们在mmengine.fileio
模块中),但是功能和用法大体不变,导入和使用方式如下:
1 2 3 4 5 6 7 8 9 10 11 12 from mmengine import Config cfg = Config.fromfile("/path_to_your_file" )from mmengine.fileio import FileClient, FileHandler >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>from mmcv.fileio import FileClientfrom mmcv import load
MMCV 整体概述
MMCV 从一开始的定位就是提供底层通用组件,故在设计之初就已经考虑到了灵活性和可扩展性,其主要特性是:
统一可扩展的 io api
支持非常丰富的图像/视频处理算子
图片/视频的标注文件可视化
常用的工具类例如 timer 和 progress bar 等等
上层框架需要的 hook 机制以及可以直接使用的 runner
高度灵活的 config 模式和注册器机制
高效高质量的 cuda operator
MMCV 核心组件FileHandler分析:
fileio
中有两个核心组件:涉及文件读写的 FileHandler
和文件获取后端 FileClient
FileHandler
的作用是对外提供统一的文件读写 API,其根据待读写的文件后缀名自动选择对应的 handler 进行具体操作
FileClient
的作用是对外提供统一的文件内容获取 API,主要用于训练过程中数据的读取,通过用户选择或者自定义不同的 FileClient
后端,可以轻松实现文件缓存、文件加速读取等功能
以上两个核心组件都是支持可扩展的
实现逻辑
要实现这个功能,mmcv采用面向接口编程的思想 ,核心代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 from abc import ABCMeta, abstractmethodclass BaseFileHandler (metaclass=ABCMeta): @abstractmethod def load_from_fileobj (self, file, **kwargs ): pass @abstractmethod def dump_to_fileobj (self, obj, file, **kwargs ): pass @abstractmethod def dump_to_str (self, obj, **kwargs ): pass def load_from_path (self, filepath, mode='r' , **kwargs ): with open (filepath, mode) as f: return self.load_from_fileobj(f, **kwargs) def dump_to_path (self, obj, filepath, mode='w' , **kwargs ): with open (filepath, mode) as f: self.dump_to_fileobj(obj, f, **kwargs)
上述核心就是先定义几个抽象方法,然后再定义几个对外调用 API 即可。考虑到不同的读写具体子类在进行读写操作时候可能参数不一样,故上述每个方法上面都加了 **kwargs
可变字典参数
以 json 读写为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class JsonHandler (BaseFileHandler ): def load_from_fileobj (self, file ): return json.load(file) def dump_to_fileobj (self, obj, file, **kwargs ): kwargs.setdefault('default' , set_default) json.dump(obj, file, **kwargs) def dump_to_str (self, obj, **kwargs ): kwargs.setdefault('default' , set_default) return json.dumps(obj, **kwargs)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 file_handlers = { 'json' : JsonHandler(), 'yaml' : YamlHandler(), 'yml' : YamlHandler(), 'pickle' : PickleHandler(), 'pkl' : PickleHandler() }def load (file, file_format=None , **kwargs ): if isinstance (file, Path): file = str (file) if file_format is None and is_str(file): file_format = file.split('.' )[-1 ] if file_format not in file_handlers: raise TypeError(f'Unsupported format: {file_format} ' ) handler = file_handlers[file_format] if is_str(file): obj = handler.load_from_path(file, **kwargs) elif hasattr (file, 'read' ): obj = handler.load_from_fileobj(file, **kwargs) else : raise TypeError('"file" must be a filepath str or a file-object' ) return objdef dump (obj, file=None , file_format=None , **kwargs ):
那么它的用法(对外接口)就十分简洁了:
1 2 3 4 5 6 7 8 import mmcv data = mmcv.load('test.json' ) data = mmcv.load('test.yaml' ) data = mmcv.load('test.pkl' ) mmcv.dump(data, 'out.pkl' )
自定义文件类型开发:
如果你需要的文件格式不在上述列表,如何进行自定义扩展开发呢?这里以读写 .npy 文件为例进行简要代码构建。
继承 BaseFileHandler,然后实现抽象方法,最后注册到 MMCV 中
1 2 3 4 5 6 7 8 9 10 11 @register_handler('npy' ) class NpyHandler (BaseFileHandler ): def load_from_fileobj (self, file, **kwargs ): return np.load(file) def dump_to_fileobj (self, obj, file, **kwargs ): np.save(file, obj) def dump_to_str (self, obj, **kwargs ): return obj.tobytes()
需要特别说明的是 @register_handler('npy')
,这是一个装饰器,目的是把我们刚才实现的 handler 注册到 MMCV 中,然后 MMCV 就可以直接找到该 handler 了,装饰器的核心代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 def register_handler (file_formats, **kwargs ): def wrap (cls ): _register_handler(cls(**kwargs), file_formats) return cls return wrap>>> file_handlers = { 'json' : JsonHandler(), 'yaml' : YamlHandler(), 'yml' : YamlHandler(), 'pickle' : PickleHandler(), 'pkl' : PickleHandler(), 'npy' : NpyHandler() }