parent
ce69de70d3
commit
e35e9760a6
@ -1,4 +1,4 @@
|
|||||||
from .parser import Parser
|
from .parser import Parser
|
||||||
from .parser_image_folder import ParserImageFolder
|
from .parser_image_folder import ParserImageFolder
|
||||||
from .parser_image_tar import ParserImageTar
|
from .parser_image_tar import ParserImageTar
|
||||||
from .parser_in21k_tar import ParserIn21kTar
|
from .parser_image_class_in_tar import ParserImageClassInTar
|
@ -0,0 +1,107 @@
|
|||||||
|
import os
|
||||||
|
import tarfile
|
||||||
|
import pickle
|
||||||
|
from glob import glob
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from timm.utils.misc import natural_key
|
||||||
|
|
||||||
|
from .parser import Parser
|
||||||
|
from .class_map import load_class_map
|
||||||
|
from .constants import IMG_EXTENSIONS
|
||||||
|
|
||||||
|
|
||||||
|
def extract_tarinfos(root, class_name_to_idx=None, cache_filename=None, extensions=None):
|
||||||
|
tar_filenames = glob(os.path.join(root, '*.tar'), recursive=True)
|
||||||
|
assert len(tar_filenames)
|
||||||
|
num_tars = len(tar_filenames)
|
||||||
|
|
||||||
|
cache_path = ''
|
||||||
|
if cache_filename is not None:
|
||||||
|
cache_path = os.path.join(root, cache_filename)
|
||||||
|
if os.path.exists(cache_path):
|
||||||
|
with open(cache_path, 'rb') as pf:
|
||||||
|
tarinfo_map = pickle.load(pf)
|
||||||
|
else:
|
||||||
|
tarinfo_map = {}
|
||||||
|
for fi, fn in enumerate(tar_filenames):
|
||||||
|
if fi % 1000 == 0:
|
||||||
|
print(f'DEBUG: tar {fi}/{num_tars}')
|
||||||
|
# cannot keep this open across processes, reopen later
|
||||||
|
name = os.path.splitext(os.path.basename(fn))[0]
|
||||||
|
with tarfile.open(fn) as tf:
|
||||||
|
if extensions is None:
|
||||||
|
# assume all files are valid samples
|
||||||
|
class_tarinfos = tf.getmembers()
|
||||||
|
else:
|
||||||
|
class_tarinfos = [m for m in tf.getmembers() if os.path.splitext(m.name)[1].lower() in extensions]
|
||||||
|
tarinfo_map[name] = dict(tarinfos=class_tarinfos)
|
||||||
|
print(f'DEBUG: {len(class_tarinfos)} images for class {name}')
|
||||||
|
tarinfo_map = {k: v for k, v in sorted(tarinfo_map.items(), key=lambda k: natural_key(k[0]))}
|
||||||
|
if cache_path:
|
||||||
|
with open(cache_path, 'wb') as pf:
|
||||||
|
pickle.dump(tarinfo_map, pf, protocol=pickle.HIGHEST_PROTOCOL)
|
||||||
|
|
||||||
|
tarinfos = []
|
||||||
|
targets = []
|
||||||
|
build_class_map = False
|
||||||
|
if class_name_to_idx is None:
|
||||||
|
class_name_to_idx = {}
|
||||||
|
build_class_map = True
|
||||||
|
for i, (name, metadata) in enumerate(tarinfo_map.items()):
|
||||||
|
class_idx = i
|
||||||
|
if build_class_map:
|
||||||
|
class_name_to_idx[name] = i
|
||||||
|
else:
|
||||||
|
if name not in class_name_to_idx:
|
||||||
|
# only samples with class in class mapping are added
|
||||||
|
continue
|
||||||
|
class_idx = class_name_to_idx[name]
|
||||||
|
num_samples = len(metadata['tarinfos'])
|
||||||
|
tarinfos.extend(metadata['tarinfos'])
|
||||||
|
targets.extend([class_idx] * num_samples)
|
||||||
|
|
||||||
|
return tarinfos, np.array(targets), class_name_to_idx
|
||||||
|
|
||||||
|
|
||||||
|
class ParserImageClassInTar(Parser):
|
||||||
|
""" Multi-tarfile dataset parser where there is one .tar file per class
|
||||||
|
"""
|
||||||
|
|
||||||
|
CACHE_FILENAME = '_tarinfos.pickle'
|
||||||
|
|
||||||
|
def __init__(self, root, class_map=''):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
class_name_to_idx = None
|
||||||
|
if class_map:
|
||||||
|
class_name_to_idx = load_class_map(class_map, root)
|
||||||
|
assert os.path.isdir(root)
|
||||||
|
self.root = root
|
||||||
|
self.tarinfos, self.targets, self.class_name_to_idx = extract_tarinfos(
|
||||||
|
self.root, class_name_to_idx=class_name_to_idx,
|
||||||
|
cache_filename=self.CACHE_FILENAME, extensions=IMG_EXTENSIONS)
|
||||||
|
self.class_idx_to_name = {v: k for k, v in self.class_name_to_idx.items()}
|
||||||
|
self.tarfiles = {} # to open lazily
|
||||||
|
self.cache_tarfiles = False
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.tarinfos)
|
||||||
|
|
||||||
|
def __getitem__(self, index):
|
||||||
|
tarinfo = self.tarinfos[index]
|
||||||
|
target = self.targets[index]
|
||||||
|
class_name = self.class_idx_to_name[target]
|
||||||
|
if self.cache_tarfiles:
|
||||||
|
tf = self.tarfiles.setdefault(
|
||||||
|
class_name, tarfile.open(os.path.join(self.root, class_name + '.tar')))
|
||||||
|
else:
|
||||||
|
tf = tarfile.open(os.path.join(self.root, class_name + '.tar'))
|
||||||
|
fileobj = tf.extractfile(tarinfo)
|
||||||
|
return fileobj, target
|
||||||
|
|
||||||
|
def _filename(self, index, basename=False, absolute=False):
|
||||||
|
filename = self.tarinfos[index].name
|
||||||
|
if basename:
|
||||||
|
filename = os.path.basename(filename)
|
||||||
|
return filename
|
@ -1,104 +0,0 @@
|
|||||||
import os
|
|
||||||
import io
|
|
||||||
import re
|
|
||||||
import torch
|
|
||||||
import tarfile
|
|
||||||
import pickle
|
|
||||||
from glob import glob
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
import torch.utils.data as data
|
|
||||||
|
|
||||||
from timm.utils.misc import natural_key
|
|
||||||
|
|
||||||
from .constants import IMG_EXTENSIONS
|
|
||||||
|
|
||||||
|
|
||||||
def load_class_map(filename, root=''):
|
|
||||||
class_map_path = filename
|
|
||||||
if not os.path.exists(class_map_path):
|
|
||||||
class_map_path = os.path.join(root, filename)
|
|
||||||
assert os.path.exists(class_map_path), 'Cannot locate specified class map file (%s)' % filename
|
|
||||||
class_map_ext = os.path.splitext(filename)[-1].lower()
|
|
||||||
if class_map_ext == '.txt':
|
|
||||||
with open(class_map_path) as f:
|
|
||||||
class_to_idx = {v.strip(): k for k, v in enumerate(f)}
|
|
||||||
else:
|
|
||||||
assert False, 'Unsupported class map extension'
|
|
||||||
return class_to_idx
|
|
||||||
|
|
||||||
|
|
||||||
class ParserIn21kTar(data.Dataset):
|
|
||||||
|
|
||||||
CACHE_FILENAME = 'class_info.pickle'
|
|
||||||
|
|
||||||
def __init__(self, root, class_map=''):
|
|
||||||
|
|
||||||
class_to_idx = None
|
|
||||||
if class_map:
|
|
||||||
class_to_idx = load_class_map(class_map, root)
|
|
||||||
assert os.path.isdir(root)
|
|
||||||
self.root = root
|
|
||||||
tar_filenames = glob(os.path.join(self.root, '*.tar'), recursive=True)
|
|
||||||
assert len(tar_filenames)
|
|
||||||
num_tars = len(tar_filenames)
|
|
||||||
|
|
||||||
if os.path.exists(self.CACHE_FILENAME):
|
|
||||||
with open(self.CACHE_FILENAME, 'rb') as pf:
|
|
||||||
class_info = pickle.load(pf)
|
|
||||||
else:
|
|
||||||
class_info = {}
|
|
||||||
for fi, fn in enumerate(tar_filenames):
|
|
||||||
if fi % 1000 == 0:
|
|
||||||
print(f'DEBUG: tar {fi}/{num_tars}')
|
|
||||||
# cannot keep this open across processes, reopen later
|
|
||||||
name = os.path.splitext(os.path.basename(fn))[0]
|
|
||||||
img_tarinfos = []
|
|
||||||
with tarfile.open(fn) as tf:
|
|
||||||
img_tarinfos.extend(tf.getmembers())
|
|
||||||
class_info[name] = dict(img_tarinfos=img_tarinfos)
|
|
||||||
print(f'DEBUG: {len(img_tarinfos)} images for synset {name}')
|
|
||||||
class_info = {k: v for k, v in sorted(class_info.items())}
|
|
||||||
|
|
||||||
with open('class_info.pickle', 'wb') as pf:
|
|
||||||
pickle.dump(class_info, pf, protocol=pickle.HIGHEST_PROTOCOL)
|
|
||||||
|
|
||||||
if class_to_idx is not None:
|
|
||||||
out_dict = {}
|
|
||||||
for k, v in class_info.items():
|
|
||||||
if k in class_to_idx:
|
|
||||||
class_idx = class_to_idx[k]
|
|
||||||
v['class_idx'] = class_idx
|
|
||||||
out_dict[k] = v
|
|
||||||
class_info = {k: v for k, v in sorted(out_dict.items(), key=lambda x: x[1]['class_idx'])}
|
|
||||||
else:
|
|
||||||
for i, (k, v) in enumerate(class_info.items()):
|
|
||||||
v['class_idx'] = i
|
|
||||||
|
|
||||||
self.img_infos = []
|
|
||||||
self.targets = []
|
|
||||||
self.tarnames = []
|
|
||||||
for k, v in class_info.items():
|
|
||||||
num_samples = len(v['img_tarinfos'])
|
|
||||||
self.img_infos.extend(v['img_tarinfos'])
|
|
||||||
self.targets.extend([v['class_idx']] * num_samples)
|
|
||||||
self.tarnames.extend([k] * num_samples)
|
|
||||||
self.targets = np.array(self.targets) # separate, uniform np array are more memory efficient
|
|
||||||
self.tarnames = np.array(self.tarnames)
|
|
||||||
|
|
||||||
self.tarfiles = {} # to open lazily
|
|
||||||
del class_info
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
return len(self.img_infos)
|
|
||||||
|
|
||||||
def __getitem__(self, idx):
|
|
||||||
img_tarinfo = self.img_infos[idx]
|
|
||||||
name = self.tarnames[idx]
|
|
||||||
tf = self.tarfiles.setdefault(name, tarfile.open(os.path.join(self.root, name + '.tar')))
|
|
||||||
img_bytes = tf.extractfile(img_tarinfo)
|
|
||||||
if self.targets:
|
|
||||||
target = self.targets[idx]
|
|
||||||
else:
|
|
||||||
target = None
|
|
||||||
return img_bytes, target
|
|
Loading…
Reference in new issue