├── dataloader ├── __init__.py ├── eval │ ├── __init__.py │ └── metrics.py ├── file_io │ ├── __init__.py │ ├── get_path.py │ ├── dataset_index.py │ ├── dir_lister.py │ ├── README.md │ ├── dataset_scaler.py │ └── trainid_converter.py ├── definitions │ └── __init__.py ├── pt_data_loader │ ├── __init__.py │ ├── dataset_parameterset.py │ ├── specialdatasets.py │ ├── dataset_tester.py │ └── README.md └── data_preprocessing │ ├── __init__.py │ ├── Mapillary │ ├── __init__.py │ ├── README.md │ ├── convert_to_id.py │ ├── labels.py │ ├── labels_file.py │ └── config.json │ ├── KITTI │ ├── depth_read.m │ ├── README.md │ ├── getAllFiles.m │ ├── gen_kitti_interp.m │ ├── fill_depth_colorization.m │ ├── kitti_utils.py │ ├── download_kitti.py │ └── kitti_archives_to_download.txt │ ├── helpers │ └── tikz_standalone.py │ ├── synthia │ ├── convert_depth.py │ └── convert_segmentation.py │ ├── KITTI_2015 │ └── generate_depth.py │ └── make3d │ └── convert_depth.py ├── img └── dataloader.png ├── requirements.txt ├── example ├── cityscapes_dataloader.sh ├── cityscapes_preparation.sh ├── cityscapes_preparation_example.py ├── cityscapes_dataloader_example.py └── simple_mode_example.py ├── setup.py ├── LICENSE ├── .gitignore └── README.md /dataloader/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dataloader/eval/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /dataloader/file_io/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dataloader/definitions/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dataloader/pt_data_loader/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dataloader/data_preprocessing/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dataloader/data_preprocessing/Mapillary/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/dataloader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifnspaml/IFN_Dataloader/HEAD/img/dataloader.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy>=1.19.1 2 | pandas>=1.1.1 3 | pillow~=7.2.0 4 | torchvision~=0.7.0 5 | matplotlib~=3.3.1 6 | opencv>=4.4.0 7 | pandas>=1.1.1 8 | scipy>=1.5.2 -------------------------------------------------------------------------------- /dataloader/data_preprocessing/Mapillary/README.md: -------------------------------------------------------------------------------- 1 | #Create labels_file entry 2 | Execute labels.py in order to create the labels_file.py for the datasets Mapillary 3 | and Mapillary_by_ID -------------------------------------------------------------------------------- /dataloader/data_preprocessing/KITTI/depth_read.m: -------------------------------------------------------------------------------- 1 | function D = depth_read (filename) 2 | % loads depth map D from png file 3 | % for details see readme.txt 4 | 5 | I = imread(filename); 6 | D = double(I)/256; 7 | D(I==0) = 0; 8 | 9 | -------------------------------------------------------------------------------- /example/cityscapes_dataloader.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export IFN_DIR_DATASET=/home/simon/IFN-Cluster/beegfs/work/shared 4 | export IFN_DIR_CHECKPOINT=/home/simon/IFN_workspace/Checkpoints 5 | 6 | python3 cityscapes_dataloader_example.py 7 | 8 | 9 | echo "Completed job on "$(hostname) 10 | -------------------------------------------------------------------------------- /example/cityscapes_preparation.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export IFN_DIR_DATASET=/home/simon/IFN-Cluster/beegfs/work/bensberg 4 | export IFN_DIR_CHECKPOINT=/home/simon/IFN_workspace/Checkpoints 5 | 6 | python3 cityscapes_preparation_example.py 7 | 8 | 9 | echo "Completed job on "$(hostname) 10 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | 3 | setup( 4 | name='dataloader', 5 | version='0.1', 6 | packages=['dataloader/data_preprocessing','dataloader/definitions','dataloader/eval','dataloader/file_io','dataloader/pt_data_loader',], 7 | long_description=open('README.md').read(), 8 | ) -------------------------------------------------------------------------------- /dataloader/data_preprocessing/KITTI/README.md: -------------------------------------------------------------------------------- 1 | This is a guide on how to create the KITTI dataset from scratch: 2 | 3 | KITTI data is now downloaded and processed: 4 | 5 | python3 download_kitti.py 6 | 7 | This will download all necessary archives, bring them to a reasonable 8 | folder structure and generate the depth from the point clouds 9 | 10 | Postprocessing the depth maps with the Code from the NYU depth toolbox 11 | 12 | matlab -nodisplay -nosplash -nodesktop -r "run('gen_kitti_interp.m');exit;" 13 | 14 | Will generate interpolations for the sparse depth maps with the colorization method 15 | of Levin et al., the types of interpolation have to be realized inside the data loader 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /dataloader/data_preprocessing/KITTI/getAllFiles.m: -------------------------------------------------------------------------------- 1 | function fileList = getAllFiles(dirName) 2 | 3 | dirData = dir(dirName); %# Get the data for the current directory 4 | dirIndex = [dirData.isdir]; %# Find the index for directories 5 | fileList = {dirData(~dirIndex).name}'; %'# Get a list of the files 6 | if ~isempty(fileList) 7 | fileList = cellfun(@(x) fullfile(dirName,x),... %# Prepend path to files 8 | fileList,'UniformOutput',false); 9 | end 10 | subDirs = {dirData(dirIndex).name}; %# Get a list of the subdirectories 11 | validIndex = ~ismember(subDirs,{'.','..'}); %# Find index of subdirectories 12 | %# that are not '.' or '..' 13 | for iDir = find(validIndex) %# Loop over valid subdirectories 14 | nextDir = fullfile(dirName,subDirs{iDir}); %# Get the subdirectory path 15 | fileList = [fileList; getAllFiles(nextDir)]; %# Recursively call getAllFiles 16 | end 17 | 18 | end -------------------------------------------------------------------------------- /dataloader/data_preprocessing/helpers/tikz_standalone.py: -------------------------------------------------------------------------------- 1 | # this routine creates a standalone tikz image from the code of a single tikz image 2 | 3 | def create_standalone(filename): 4 | with open(filename, "r") as f: 5 | lines = f.readlines() 6 | f.close() 7 | with open(filename, "w") as f: 8 | f.write("\\documentclass[convert={convertexe={magick.exe}}]{standalone}\n") 9 | f.write("\\usepackage[utf8]{inputenc}\n") 10 | f.write("\\usepackage{tikz}\n") 11 | f.write("\\usepackage{amsmath}\n") 12 | f.write("\\usepackage{siunitx}\n") 13 | f.write("\\usetikzlibrary{calc}\n") 14 | f.write("\\usepackage{pgfplots}\n") 15 | f.write("\\pgfplotsset{compat=newest}\n") 16 | f.write("\\usepgfplotslibrary{groupplots}\n") 17 | f.write("\\usepgfplotslibrary{dateplot}\n") 18 | f.write("\\begin{document}\n") 19 | for line in lines: 20 | f.write(line) 21 | f.close() 22 | with open(filename, "a") as f: 23 | f.write("\n" + "\\end{document}") 24 | f.close() -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Marvin Klingner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /dataloader/data_preprocessing/synthia/convert_depth.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import numpy as np 4 | import cv2 5 | import PIL.Image as pil 6 | 7 | sys.path.append("../../../") 8 | import dataloader.file_io.dir_lister as dl 9 | import dataloader.file_io.get_path as gp 10 | 11 | """ 12 | The synthia dataset saves its depth information in 3 identical channels in a 16-Bit image. This format is not compatible 13 | with the transforms from mytransforms and therefore this preprocessing file has to be executed. It will create a new 14 | folder called "Depth_1_channel" which contains the 16-Bit depth images with just one channel. 15 | """ 16 | 17 | path_getter = gp.GetPath() 18 | path = path_getter.get_data_path() 19 | data_path = os.path.join(path, 'synthia', 'RAND_CITYSCAPES', 'Depth', 'Depth') 20 | out_path = os.path.join(path, 'synthia', 'RAND_CITYSCAPES', 'Depth_1_channel') 21 | if not os.path.isdir(out_path): 22 | os.mkdir(out_path) 23 | filelist = dl.DirLister.get_files_by_ending(data_path, '.png') 24 | print("{} files found".format(len(filelist))) 25 | for file in filelist: 26 | image = cv2.imread(file, -1) 27 | filename = os.path.split(file)[-1] 28 | image = image[:, :, 0].reshape(image.shape[0], image.shape[1]) 29 | cv2.imwrite(os.path.join(out_path, filename), image) 30 | -------------------------------------------------------------------------------- /dataloader/data_preprocessing/synthia/convert_segmentation.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import numpy as np 4 | import cv2 5 | import PIL.Image as pil 6 | 7 | sys.path.append("../../../") 8 | import dataloader.file_io.dir_lister as dl 9 | import dataloader.file_io.get_path as gp 10 | 11 | """ 12 | The synthia dataset saves its segmentation information in 3 channels in a 16-Bit image. This format is not compatible 13 | with the transforms from mytransforms and therefore this preprocessing file has to be executed. It will create a new 14 | folder called "GT_1_channel" which contains the 16-Bit ground truth segmentation images with just one channel. 15 | """ 16 | 17 | path_getter = gp.GetPath() 18 | path = path_getter.get_data_path() 19 | data_path = os.path.join(path, 'synthia', 'RAND_CITYSCAPES', 'GT', 'LABELS') 20 | out_path = os.path.join(path, 'synthia', 'RAND_CITYSCAPES', 'GT_1_channel', 'LABELS') 21 | if not os.path.isdir(out_path): 22 | os.makedirs(out_path) 23 | filelist = dl.DirLister.get_files_by_ending(data_path, '.png') 24 | print("{} files found".format(len(filelist))) 25 | for file in filelist: 26 | image = cv2.imread(file, -1) 27 | filename = os.path.split(file)[-1] 28 | image = image[:, :, 2].reshape(image.shape[0], image.shape[1]) 29 | cv2.imwrite(os.path.join(out_path, filename), image) 30 | -------------------------------------------------------------------------------- /example/cityscapes_preparation_example.py: -------------------------------------------------------------------------------- 1 | from dataloader.file_io.filelist_creator import DatasetCreator 2 | from dataloader.file_io.split_creator import DatasetSplitter 3 | from dataloader.file_io.dataset_index import create_parameter_files 4 | 5 | # In this example, all necessary preparation steps that are needed to use the cityscapes dataset with this 6 | # dataloader are performed. These steps consits of creating json files with the necessary information for the 7 | # dataloader to recognize all data correctly. 8 | dataset = 'cityscapes' 9 | 10 | # In order to prepare a dataset for the dataloader, the following three steps are necessary: 11 | # 1. Create the basic_files.json 12 | data_creator = DatasetCreator(dataset, rewrite=True) 13 | check = data_creator.check_state() # If rewrite=False, this will make sure that existing files will not be overwritten 14 | if not check: 15 | data_creator.create_dataset() 16 | 17 | # 2. Create the train.json, validation.json and test.json from the basic_files.json 18 | # If a dataset (e.g. KITTI) has more than one possible split in train, validation and test, tha variable split 19 | # should contain a name of the predefined splits. Since Cityscapes has fixed splits based on the folder structure, 20 | # we can set this variable to None. 21 | splits = None 22 | data_splitter = DatasetSplitter(dataset, splits) 23 | data_splitter.create_splits() 24 | 25 | # 3. Create the parameters.json. It is also possible to create parameters files for all existing datasets at once 26 | # by just executing the dataset_index.py 27 | create_parameter_files(dataset) -------------------------------------------------------------------------------- /dataloader/data_preprocessing/KITTI_2015/generate_depth.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | import os 4 | import sys 5 | 6 | sys.path.append('../../../') 7 | import dataloader.file_io.get_path as gp 8 | 9 | disp_names = ['disp_noc_0', 'disp_noc_1', 'disp_occ_0', 'disp_occ_1'] 10 | depth_names = ['depth_noc_0', 'depth_noc_1', 'depth_occ_0', 'depth_occ_1'] 11 | line_numbers = [20, 28, 20, 28] 12 | for disp_name, depth_name, line_number in zip(disp_names, depth_names, line_numbers): 13 | path_getter = gp.GetPath() 14 | data_path = path_getter.get_data_path() 15 | data_path = os.path.join(data_path, 'kitti_2015', 'training') 16 | disp_path = os.path.join(data_path, disp_name) 17 | depth_path = os.path.join(data_path, depth_name) 18 | if not os.path.isdir(depth_path): 19 | os.makedirs(depth_path) 20 | calib_path = os.path.join(data_path, 'calib_cam_to_cam') 21 | file_list_im = os.listdir(disp_path) 22 | file_list_cam = os.listdir(calib_path) 23 | for image_file, cam_file in zip(file_list_im, file_list_cam): 24 | im_file = os.path.join(disp_path, image_file) 25 | cam_file = os.path.join(calib_path, cam_file) 26 | disp = cv2.imread(im_file, -1).astype(np.float)/256. 27 | cam_matrix = open(cam_file).readlines()[:line_number][-1][6:].split() 28 | foc_length = (float(cam_matrix[0]) + float(cam_matrix[4]))/2.0 29 | depth = 0.54*foc_length/(disp + 0.00000000001) 30 | depth[disp == 0] = 0 31 | depth = (depth*256).astype(np.uint16) 32 | cv2.imwrite(os.path.join(depth_path, image_file), depth) 33 | -------------------------------------------------------------------------------- /dataloader/data_preprocessing/Mapillary/convert_to_id.py: -------------------------------------------------------------------------------- 1 | #+++++++++++++++++++++++++++++++++++++++++++++++ 2 | # 3 | # Author: Philipp Donn 4 | # 5 | # Date: 10/10/2019 6 | # 7 | # Supervisor: Marvin Klingner 8 | # 9 | #+++++++++++++++++++++++++++++++++++++++++++++++ 10 | # 11 | # Convert 3D colour ground truths to 2D id 12 | # ground truths. Do yourself a favour and run it 13 | # on a GPU. 14 | # 15 | #+++++++++++++++++++++++++++++++++++++++++++++++ 16 | 17 | import cv2 18 | import torch 19 | import os 20 | 21 | from dataloader.definitions.labels_file import labels_mapillary_seg_cityscapes_def 22 | import dataloader.file_io.get_path as gp 23 | 24 | labels = labels_mapillary_seg_cityscapes_def.getlabels() 25 | path_getter = gp.GetPath() 26 | path = path_getter.get_data_path() 27 | path_in = os.path.join(path, 'mapillary', 'train', 'Segmentation') 28 | path_out = os.path.join(path, 'mapillary', 'segmentation_trainid', 'train', 'Segmentation') 29 | 30 | if not os.path.isdir(path_out): 31 | os.makedirs(path_out) 32 | 33 | device = torch.device("cuda") 34 | 35 | files = os.listdir(path_in) 36 | 37 | n_labels = len(labels) 38 | label_color = torch.zeros(size=(n_labels, 3), dtype=torch.uint8) 39 | label_id = torch.zeros(size=(n_labels, 1), dtype=torch.uint8) 40 | i = 0 41 | 42 | for label in labels: 43 | label_color[i, :] = torch.tensor(label.color, dtype=torch.uint8) 44 | label_id[i, 0] = label.trainId 45 | i += 1 46 | 47 | label_color = label_color.to(device) 48 | label_id = label_id.to(device) 49 | 50 | for file in files: 51 | img = cv2.imread(os.path.join(path_in, file), -1) 52 | img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 53 | img = torch.tensor(img).to(device) 54 | img_label = torch.zeros(size=(img.shape[:2]), dtype=torch.uint8).to(device) 55 | 56 | for i in range(n_labels): 57 | img_label = torch.where((img == label_color[i, :]).all(dim=2), label_id[i, 0], img_label) 58 | cv2.imwrite(os.path.join(path_out, file), img_label.cpu().numpy()) 59 | -------------------------------------------------------------------------------- /dataloader/data_preprocessing/make3d/convert_depth.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import numpy as np 4 | import cv2 5 | import PIL.Image as pil 6 | import scipy.io as sio 7 | 8 | sys.path.append("../../../") 9 | import dataloader.file_io.dir_lister as dl 10 | import dataloader.file_io.get_path as gp 11 | """ 12 | The depth information for the make3d dataset is stored in .mat files. This script will convert them into PNG files 13 | containing the depth in cm as a uint16 number. The dimensions will be scaled to match the dimensions of the color image. 14 | """ 15 | 16 | # Information from the dataset readme: 17 | # 1) Train400Depth.tgz 18 | # Laser Range data with Ray Position 19 | # Data Format: Position3DGrid (55x305x4) 20 | # Position3DGrid(:,:,1) is Vertical axis in meters (Y) 21 | # Position3DGrid(:,:,2) is Horizontal axis in meters (X) 22 | # Position3DGrid(:,:,3) is Projective Depths in meters (Z) 23 | # Position3DGrid(:,:,4) is Depths in meters (d) 24 | # 25 | # 2) Train400Img.tar.gz 26 | # Images all in resolution 2272x1704 27 | 28 | for split in ('train', 'test'): 29 | path_getter = gp.GetPath() 30 | path = path_getter.get_data_path() 31 | data_path = os.path.join(path, 'make3d', split, 'Depth') 32 | out_path = os.path.join(path, 'make3d', split, 'Depth_PNG') 33 | if not os.path.isdir(out_path): 34 | os.mkdir(out_path) 35 | filelist = dl.DirLister.get_files_by_ending(data_path, '.mat') 36 | print("{}: {} files found".format(split, len(filelist))) 37 | for file in filelist: 38 | filename = os.path.split(file)[-1] 39 | filename = os.path.splitext(filename)[0] 40 | print(filename) 41 | depth_data = sio.loadmat(file, verify_compressed_data_integrity=False)['Position3DGrid'] 42 | # We use the 4th channel where the depth in meters is stored 43 | depth_data = depth_data[:, :, 3] 44 | mini = np.amin(depth_data) 45 | maxi = np.amax(depth_data) 46 | print('Minimum: {}, Maximum: {}'.format(mini, maxi)) 47 | for i in range(depth_data.shape[0]): 48 | # Set invalid pixels to zero 49 | for j in range(depth_data.shape[1]): 50 | if depth_data[i, j] > 81.9: 51 | depth_data[i, j] = 0 52 | depth_data = pil.fromarray(depth_data) 53 | depth_data = depth_data.resize((1704, 2272), pil.NEAREST) 54 | depth_data = (np.array(depth_data) * 256).astype(np.uint16) 55 | cv2.imwrite(os.path.join(out_path, filename+'.png'), depth_data) -------------------------------------------------------------------------------- /dataloader/file_io/get_path.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | import socket 4 | import json 5 | 6 | class GetPath: 7 | def __init__(self): 8 | """This class gives the paths that are needed for training and testing neural networks. 9 | 10 | Paths that need to be specified are the data path and the checkpoint path, where the models will be saved. 11 | The paths have to saved in environment variables called IFN_DIR_DATASET and IFN_DIR_CHECKPOINT, respectively. 12 | """ 13 | 14 | # Check if the user did explicitly set environment variables 15 | if self._guess_by_env(): 16 | return 17 | 18 | # Print a helpful text when no directories could be found 19 | if platform.system() == 'Windows': 20 | raise ValueError( 21 | 'Could not determine dataset/checkpoint directory. ' 22 | 'You can use environment variables to specify these directories ' 23 | 'by using the following commands:\n' 24 | 'setx IFN_DIR_DATASET \n' 25 | 'setx IFN_DIR_CHECKPOINT \n' 26 | ) 27 | 28 | else: 29 | raise ValueError( 30 | 'Could not determine dataset/checkpoint directory. ' 31 | 'You can use environment variables to specify these directories ' 32 | 'by adding lines like the following to your ~/.bashrc:\n' 33 | 'export IFN_DIR_DATASET=\n' 34 | 'export IFN_DIR_CHECKPOINT=' 35 | ) 36 | 37 | def _check_dirs(self): 38 | if self.dataset_base_path is None: 39 | return False 40 | 41 | if self.checkpoint_base_path is None: 42 | return False 43 | 44 | if not os.path.isdir(self.dataset_base_path): 45 | return False 46 | 47 | return True 48 | 49 | def _guess_by_env(self): 50 | dataset_base = os.environ.get('IFN_DIR_DATASET', None) 51 | checkpoint_base = os.environ.get('IFN_DIR_CHECKPOINT', None) 52 | 53 | self.dataset_base_path = dataset_base 54 | self.checkpoint_base_path = checkpoint_base 55 | 56 | return self._check_dirs() 57 | 58 | def get_data_path(self): 59 | """returns the path to the dataset folder""" 60 | 61 | return self.dataset_base_path 62 | 63 | def get_checkpoint_path(self): 64 | """returns the path to the checkpoints of the models""" 65 | 66 | return self.checkpoint_base_path 67 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | *~ 132 | .idea/ 133 | *__pycache__/* 134 | 135 | *.ini 136 | *.sbv1 137 | *.mat 138 | *.pyc 139 | 140 | *.csv 141 | -------------------------------------------------------------------------------- /dataloader/data_preprocessing/KITTI/gen_kitti_interp.m: -------------------------------------------------------------------------------- 1 | ground_path = 'C:\Users\Klingner\Desktop\Datasets\KITTI'; 2 | sparse_depth_fold = 'Depth'; 3 | interp_depth_fold = 'Depth_processed'; 4 | img_fold = 'Raw_data'; 5 | sparse_path = fullfile(ground_path, sparse_depth_fold); 6 | interp_path = fullfile(ground_path, interp_depth_fold); 7 | img_path = fullfile(ground_path, img_fold); 8 | FileList = dir(fullfile(sparse_path, '**', '*.png')); 9 | array_dim = size(FileList); 10 | num_imgs = array_dim(1); 11 | 12 | parfor i = 1:num_imgs 13 | sparse_depth = depth_read(fullfile(FileList(i).folder, FileList(i).name)); 14 | 15 | splitted_paths = strsplit(FileList(i).folder, sparse_depth_fold); 16 | end_path = splitted_paths{2}; 17 | 18 | img_file_path = fullfile(img_path, end_path, FileList(i).name); 19 | rgb_img = double(imread(img_file_path))/255.; 20 | 21 | interp_file_dir = fullfile(interp_path, end_path); 22 | interp_file_path = fullfile(interp_path, end_path, FileList(i).name); 23 | if ~isdir(interp_file_dir) 24 | mkdir(interp_file_dir) 25 | end 26 | depth_interp = fill_depth_colorization(rgb_img, sparse_depth, 1.0); 27 | depth_interp = uint16(depth_interp*256); 28 | 29 | sparse_depth = uint16(sparse_depth*256); 30 | imwrite(depth_interp, 'depth_interp.png') 31 | imwrite(sparse_depth, 'depth_gt.png') 32 | imwrite(rgb_img, 'depth_img.png') 33 | imwrite(depth_interp, interp_file_path); 34 | disp(i) 35 | end 36 | 37 | sparse_depth_fold = 'Depth_improved'; 38 | interp_depth_fold = 'Depth_processed_improved'; 39 | img_fold = 'Raw_data'; 40 | sparse_path = fullfile(ground_path, sparse_depth_fold); 41 | interp_path = fullfile(ground_path, interp_depth_fold); 42 | img_path = fullfile(ground_path, img_fold); 43 | FileList = dir(fullfile(sparse_path, '**', '*.png')); 44 | array_dim = size(FileList); 45 | num_imgs = array_dim(1); 46 | 47 | parfor i = 1:num_imgs 48 | sparse_depth = depth_read(fullfile(FileList(i).folder, FileList(i).name)); 49 | 50 | splitted_paths = strsplit(FileList(i).folder, sparse_depth_fold); 51 | end_path = splitted_paths{2}; 52 | 53 | img_file_path = fullfile(img_path, end_path, FileList(i).name); 54 | rgb_img = double(imread(img_file_path))/255.; 55 | 56 | interp_file_dir = fullfile(interp_path, end_path); 57 | interp_file_path = fullfile(interp_path, end_path, FileList(i).name); 58 | if ~isdir(interp_file_dir) 59 | mkdir(interp_file_dir) 60 | end 61 | depth_interp = fill_depth_colorization(rgb_img, sparse_depth, 1.0); 62 | depth_interp = uint16(depth_interp*256); 63 | 64 | sparse_depth = uint16(sparse_depth*256); 65 | imwrite(depth_interp, 'depth_interp.png') 66 | imwrite(sparse_depth, 'depth_gt.png') 67 | imwrite(rgb_img, 'depth_img.png') 68 | imwrite(depth_interp, interp_file_path); 69 | disp(i) 70 | end 71 | 72 | 73 | -------------------------------------------------------------------------------- /dataloader/pt_data_loader/dataset_parameterset.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | import numpy as np 5 | 6 | import dataloader.file_io.get_path as gp 7 | import dataloader.definitions.labels_file as lf 8 | 9 | 10 | class DatasetParameterset: 11 | """A class that contains all dataset-specific parameters 12 | 13 | - K: Extrinsic camera matrix as a Numpy array. If not available, take None 14 | - stereo_T: Distance between the two cameras (see e.g. http://www.cvlibs.net/datasets/kitti/setup.php, 0.54m) 15 | - labels: 16 | - labels_mode: 'fromid' or 'fromrgb', depending on which format the segmentation images have 17 | - depth_mode: 'uint_16' or 'uint_16_subtract_one' depending on which format the depth images have 18 | - flow_mode: specifies how the flow images are stored, e.g. 'kitti' 19 | - splits: List of splits that are available for this dataset 20 | """ 21 | def __init__(self, dataset): 22 | path_getter = gp.GetPath() 23 | dataset_folder = path_getter.get_data_path() 24 | path = os.path.join(dataset_folder, dataset, 'parameters.json') 25 | if not os.path.isdir(os.path.join(dataset_folder, dataset)): 26 | raise Exception('There is no dataset folder called {}'.format(dataset)) 27 | if not os.path.isfile(path): 28 | raise Exception('There is no parameters.json file in the dataset folder. Please create it using the ' 29 | 'dataset_index.py in the folder dataloader/file_io in order to load this dataset') 30 | with open(path) as file: 31 | param_dict = json.load(file) 32 | self._dataset = dataset 33 | self._K = param_dict['K'] 34 | if self._K is not None: 35 | self._K = np.array(self._K, dtype=np.float32) 36 | if param_dict['stereo_T'] is not None: 37 | self._stereo_T = np.eye(4, dtype=np.float32) 38 | self._stereo_T[0, 3] = param_dict['stereo_T'] 39 | else: 40 | self._stereo_T = None 41 | self._depth_mode = param_dict['depth_mode'] 42 | self._flow_mode = param_dict['flow_mode'] 43 | self._splits = param_dict['splits'] 44 | labels_name = param_dict['labels'] 45 | if labels_name in lf.dataset_labels.keys(): 46 | self.labels = lf.dataset_labels[labels_name].getlabels() 47 | self.labels_mode = param_dict['labels_mode'] 48 | else: 49 | self.labels = None 50 | self.labels_mode = None 51 | 52 | @property 53 | def dataset(self): 54 | return self._dataset 55 | 56 | @property 57 | def K(self): 58 | return self._K 59 | 60 | @property 61 | def stereo_T(self): 62 | return self._stereo_T 63 | 64 | @property 65 | def depth_mode(self): 66 | return self._depth_mode 67 | 68 | @property 69 | def flow_mode(self): 70 | return self._flow_mode 71 | 72 | @property 73 | def splits(self): 74 | return self._splits 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /dataloader/data_preprocessing/KITTI/fill_depth_colorization.m: -------------------------------------------------------------------------------- 1 | % Preprocesses the kinect depth image using a gray scale version of the 2 | % RGB image as a weighting for the smoothing. This code is a slight 3 | % adaptation of Anat Levin's colorization code: 4 | % 5 | % See: www.cs.huji.ac.il/~yweiss/Colorization/ 6 | % 7 | % Args: 8 | % imgRgb - HxWx3 matrix, the rgb image for the current frame. This must 9 | % be between 0 and 1. 10 | % imgDepth - HxW matrix, the depth image for the current frame in 11 | % absolute (meters) space. 12 | % alpha - a penalty value between 0 and 1 for the current depth values. 13 | function denoisedDepthImg = fill_depth_colorization(imgRgb, imgDepth, alpha) 14 | error(nargchk(2, 3, nargin)); 15 | if nargin < 3 16 | alpha = 1; 17 | end 18 | 19 | imgIsNoise = (imgDepth == 0 | imgDepth == 10); 20 | maxImgAbsDepth = max(imgDepth(~imgIsNoise)); 21 | imgDepth = imgDepth ./ maxImgAbsDepth; 22 | imgDepth(imgDepth > 1) = 1; 23 | 24 | assert(ndims(imgDepth) == 2); 25 | [H, W] = size(imgDepth); 26 | numPix = H * W; 27 | 28 | indsM = reshape(1:numPix, H, W); 29 | 30 | knownValMask = ~imgIsNoise; 31 | 32 | grayImg = rgb2gray(imgRgb); 33 | winRad = 1; 34 | 35 | len = 0; 36 | absImgNdx = 0; 37 | cols = zeros(numPix * (2*winRad+1)^2,1); 38 | rows = zeros(numPix * (2*winRad+1)^2,1); 39 | vals = zeros(numPix * (2*winRad+1)^2,1); 40 | gvals = zeros(1, (2*winRad+1)^2); 41 | 42 | for j = 1 : W 43 | for i = 1 : H 44 | absImgNdx = absImgNdx + 1; 45 | 46 | nWin = 0; % Counts the number of points in the current window. 47 | for ii = max(1, i-winRad) : min(i+winRad, H) 48 | for jj = max(1, j-winRad) : min(j+winRad, W) 49 | if ii == i && jj == j 50 | continue; 51 | end 52 | 53 | len = len+1; 54 | nWin = nWin+1; 55 | rows(len) = absImgNdx; 56 | cols(len) = indsM(ii,jj); 57 | gvals(nWin) = grayImg(ii, jj); 58 | end 59 | end 60 | 61 | curVal = double(grayImg(i, j)); 62 | gvals(nWin+1) = curVal; 63 | c_var = mean((gvals(1:nWin+1)-mean(gvals(1:nWin+1))).^2); 64 | csig = c_var*0.6; 65 | mgv = min((gvals(1:nWin)-curVal).^2); 66 | 67 | if csig < (-mgv/log(0.01)) 68 | csig=-mgv/log(0.01); 69 | end 70 | 71 | if csig < 0.000002 72 | csig = 0.000002; 73 | end 74 | 75 | gvals(1:nWin) = exp(-(gvals(1:nWin)-curVal).^2/csig); 76 | gvals(1:nWin) = gvals(1:nWin) / sum(gvals(1:nWin)); 77 | vals(len-nWin+1 : len) = -gvals(1:nWin); 78 | % Now the self-reference (along the diagonal). 79 | len = len + 1; 80 | rows(len) = absImgNdx; 81 | cols(len) = absImgNdx; 82 | vals(len) = 1; %sum(gvals(1:nWin)); 83 | end 84 | end 85 | 86 | vals = vals(1:len); 87 | cols = cols(1:len); 88 | rows = rows(1:len); 89 | A = sparse(rows, cols, vals, numPix, numPix); 90 | rows = 1:numel(knownValMask); 91 | cols = 1:numel(knownValMask); 92 | vals = knownValMask(:) * alpha; 93 | G = sparse(rows, cols, vals, numPix, numPix); 94 | 95 | imgDepth = double(imgDepth); 96 | new_vals = (A + G) \ (vals .* imgDepth(:)); 97 | new_vals = reshape(new_vals, [H, W]); 98 | maxImgAbsDepth = double(maxImgAbsDepth); 99 | denoisedDepthImg = new_vals * maxImgAbsDepth; 100 | end 101 | -------------------------------------------------------------------------------- /dataloader/data_preprocessing/KITTI/kitti_utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import cv2 4 | from collections import Counter 5 | 6 | 7 | """adapted from https://github.com/nianticlabs/monodepth2 """ 8 | 9 | def pcl_to_depth_map(calib_dir, velo_file_name, im_shape, cam=2, vel_depth=False): 10 | #vel_depth should be False as Eigen computed the results relative to velo 11 | # load calibration files 12 | cam2cam = read_calib_file(os.path.join(calib_dir, 'calib_cam_to_cam.txt')) 13 | velo2cam = read_calib_file(os.path.join(calib_dir, 'calib_velo_to_cam.txt')) 14 | velo2cam = np.hstack((velo2cam['R'].reshape(3, 3), velo2cam['T'][..., np.newaxis])) 15 | velo2cam = np.vstack((velo2cam, np.array([0, 0, 0, 1.0]))) 16 | 17 | # compute projection matrix velodyne->image plane 18 | R_cam2rect = np.eye(4) 19 | R_cam2rect[:3, :3] = cam2cam['R_rect_00'].reshape(3, 3) 20 | P_rect = cam2cam['P_rect_0' + str(cam)].reshape(3, 4) 21 | P_velo2im = np.dot(np.dot(P_rect, R_cam2rect), velo2cam) 22 | 23 | # load velodyne points and remove all behind image plane (approximation) 24 | # each row of the velodyne data is forward, left, up, reflectance 25 | velo = load_velodyne_points(velo_file_name) 26 | velo = velo[velo[:, 0] >= 0, :] 27 | 28 | # project the points to the camera 29 | velo_pts_im = np.dot(P_velo2im, velo.T).T 30 | velo_pts_im[:, :2] = velo_pts_im[:, :2] / velo_pts_im[:, 2][..., np.newaxis] 31 | 32 | if vel_depth: 33 | velo_pts_im[:, 2] = velo[:, 0] 34 | 35 | # check if in bounds 36 | # use minus 1 to get the exact same value as KITTI matlab code 37 | velo_pts_im[:, 0] = np.round(velo_pts_im[:, 0]) - 1 38 | velo_pts_im[:, 1] = np.round(velo_pts_im[:, 1]) - 1 39 | val_inds = (velo_pts_im[:, 0] >= 0) & (velo_pts_im[:, 1] >= 0) 40 | val_inds = val_inds & (velo_pts_im[:, 0] < im_shape[1]) & (velo_pts_im[:, 1] < im_shape[0]) 41 | velo_pts_im = velo_pts_im[val_inds, :] 42 | 43 | # project to image 44 | depth = np.zeros((im_shape)) 45 | depth[velo_pts_im[:, 1].astype(np.int), velo_pts_im[:, 0].astype(np.int)] = velo_pts_im[:, 2] 46 | 47 | #find the duplicate points and choose the closest depth 48 | inds = sub2ind(depth.shape, velo_pts_im[:, 1], velo_pts_im[:, 0]) 49 | dupe_inds = [item for item, count in Counter(inds).items() if count > 1] 50 | for dd in dupe_inds: 51 | pts = np.where(inds == dd)[0] 52 | x_loc = int(velo_pts_im[pts[0], 0]) 53 | y_loc = int(velo_pts_im[pts[0], 1]) 54 | depth[y_loc, x_loc] = velo_pts_im[pts, 2].min() 55 | depth[depth < 0] = 0 56 | 57 | return depth 58 | 59 | 60 | """adapted from https://github.com/hunse/kitti""" 61 | 62 | def read_calib_file(path): 63 | float_chars = set("0123456789.e+- ") 64 | data = {} 65 | with open(path, 'r') as f: 66 | for line in f.readlines(): 67 | key, value = line.split(':', 1) 68 | value = value.strip() 69 | data[key] = value 70 | if float_chars.issuperset(value): 71 | # try to cast to float array 72 | try: 73 | data[key] = np.array(list(map(float, value.split(' ')))) 74 | except ValueError: 75 | # casting error: data[key] already eq. value, so pass 76 | pass 77 | 78 | return data 79 | 80 | 81 | """adapted from https://github.com/hunse/kitti""" 82 | 83 | def load_velodyne_points(file_name): 84 | points = np.fromfile(file_name, dtype=np.float32).reshape(-1, 4) 85 | points[:, 3] = 1.0 # homogeneous 86 | return points 87 | 88 | 89 | """adapted from https://github.com/nianticlabs/monodepth2 """ 90 | 91 | def sub2ind(matrixSize, rowSub, colSub): 92 | m, n = matrixSize 93 | return rowSub * (n - 1) + colSub - 1 94 | 95 | 96 | """self written""" 97 | 98 | def read_depth(filename, factor=256.): 99 | depth_png = np.array(cv2.imread(filename, -1)).astype(np.float) 100 | # make sure we have a proper 16bit depth map here.. not 8bit! 101 | assert(np.max(depth_png) > 255) 102 | 103 | depth = depth_png.astype(np.float) / factor 104 | depth[depth_png == 0] = 0 105 | return depth 106 | -------------------------------------------------------------------------------- /example/cityscapes_dataloader_example.py: -------------------------------------------------------------------------------- 1 | from torch.utils.data import DataLoader 2 | 3 | from dataloader.pt_data_loader.specialdatasets import StandardDataset 4 | import dataloader.pt_data_loader.mytransforms as mytransforms 5 | import cv2 6 | import torch 7 | import numpy as np 8 | 9 | # This examples shows how to load the cityscapes dataset with this dataloader. 10 | dataset = 'cityscapes' # dataset name 11 | trainvaltest_split = 'train' # train, validation or test 12 | 13 | # If for a dataset several possible splits into train, validation and test exist, you have 14 | # to specify the split name (e.g. the KITTI dataset). This is not needed for cityscapes. 15 | split = None 16 | 17 | # Specify the keys you want to load. These correspond to the 'names' entries in the train.json, 18 | # validation.json and test.json files. In this example, we use all available keys for cityscapes. 19 | keys_to_load = ['color', 'color_right', 'depth', 'segmentation', 20 | 'camera_intrinsics', 'camera_intrinsics_right', 'velocity'] 21 | 22 | # When loading an image, some data transforms are performed on it. These transform will alter all 23 | # image category in the same way. At minimum, the CreateScaledImage() and CreateColoraug() have to 24 | # be included. For each image category like depth and segmentation, the corresponding Convert-tranform 25 | # ist also necessary. 26 | data_transforms = [mytransforms.CreateScaledImage(), 27 | mytransforms.RemoveOriginals(), 28 | mytransforms.RandomCrop((1024, 2048)), 29 | mytransforms.RandomHorizontalFlip(), 30 | mytransforms.CreateColoraug(new_element=True), 31 | mytransforms.ColorJitter(brightness=0.2, contrast=0.5, saturation=0.5, hue=0.5, gamma=0.5, fraction=1.0), # values for visualization only 32 | mytransforms.ConvertDepth(), # The convert transforms should come after the 33 | mytransforms.ConvertSegmentation(), # Scaling/Rotating/Cropping 34 | mytransforms.ToTensor(), 35 | ] 36 | 37 | # With the parameters specified above, a StandardDataset can now be created. You can interate through 38 | # it using the PyTorch DataLoader class. 39 | # There are several optional arguments in the my_dataset class that are not featured here for the sake of 40 | # simplicity. Note that, for example, it is possible to include the previous and/or subsequent frames 41 | # in a video sequence using tha parameter video_frames. 42 | my_dataset = StandardDataset(dataset, 43 | split=split, 44 | trainvaltest_split=trainvaltest_split, 45 | keys_to_load=keys_to_load, 46 | data_transforms=data_transforms, 47 | ) 48 | my_loader = DataLoader(my_dataset, batch_size=1, 49 | shuffle=False, num_workers=0, pin_memory=True, drop_last=True) 50 | 51 | # Print the sizes of the first 3 elements to show how the elements are indexed. Each element 52 | # of the dataset is a dictionary with the 3-tuples as keys. The first entry corrsponds to the image 53 | # category. The second is a video frame index which will be always zero or non-video datasets. 54 | # The third entry is a resolution parameter showing if it is a scaled image. 55 | for element, _ in zip(my_loader, range(3)): 56 | print("###########################################################################################################") 57 | #regular color image 58 | image_tensor = np.array(element[('color', 0, 0)]) # read image tensor to numpy array 59 | image_trans = image_tensor[0].transpose(1, 2, 0) # transpose: opencv needs color channels at last 60 | image = image_trans[..., ::-1] # Revert Color Channels 61 | 62 | #augmented color image 63 | aug_img_tensor = np.array(element[('color_aug', 0, 0)]) 64 | aug_img = aug_img_tensor[0].transpose(1, 2, 0) 65 | aug_img = aug_img[..., ::-1] 66 | 67 | # Fuse images together for displaying 68 | whole_img = np.concatenate((image, aug_img), 0) 69 | whole_img = cv2.resize(whole_img, (int(2048/2.2), int(1024*2/2.2))) #resize them for better visiablity 70 | 71 | cv2.imshow("augmented image", whole_img) 72 | cv2.waitKey() 73 | 74 | for key in element: 75 | print('Key: {}, Element Size: {}'.format(key, element[key].shape)) 76 | 77 | -------------------------------------------------------------------------------- /example/simple_mode_example.py: -------------------------------------------------------------------------------- 1 | from torch.utils.data import DataLoader 2 | 3 | from dataloader.pt_data_loader.specialdatasets import StandardDataset, BaseDataset, SimpleDataset 4 | import dataloader.pt_data_loader.mytransforms as mytransforms 5 | import cv2 6 | import torch 7 | import numpy as np 8 | 9 | 10 | # If for a dataset several possible splits into train, validation and test exist, you have 11 | # to specify the split name (e.g. the KITTI dataset). This is not needed for cityscapes. 12 | split = None 13 | 14 | dataset = 'simple_dataset' 15 | 16 | # Specify the keys you want to load. These correspond to the 'names' entries in the train.json, 17 | # validation.json and test.json files. In this example, we use all available keys for cityscapes. 18 | keys_to_load = ['color', 'segmentation'] 19 | 20 | # When loading an image, some data transforms are performed on it. These transform will alter all 21 | # image category in the same way. At minimum, the CreateScaledImage() and CreateColoraug() have to 22 | # be included. For each image category like depth and segmentation, the corresponding Convert-tranform 23 | # ist also necessary. 24 | data_transforms = [mytransforms.CreateScaledImage(), 25 | mytransforms.RemoveOriginals(), 26 | mytransforms.RandomCrop((720, 1280)), 27 | # mytransforms.RandomRotate(10.0, 1.0), 28 | mytransforms.RandomHorizontalFlip(), 29 | # mytransforms.RandomVerticalFlip(), 30 | mytransforms.CreateColoraug(new_element=True), 31 | mytransforms.ColorJitter(brightness=0.2, contrast=0.5, saturation=0.5, hue=0.5, gamma=0.5, fraction=1.0), # values for visualization only 32 | # mytransforms.Resize((1024, 2048), 33 | # aspect_ratio=False, 34 | # image_types=['color', 'camera_intrinsics', 'K', 'segmentation', 'depth'] 35 | # ), 36 | mytransforms.ConvertDepth(), # The convert transforms should come after the 37 | mytransforms.ConvertSegmentation(), # Scaling/Rotating/Cropping 38 | mytransforms.ToTensor(), 39 | ] 40 | 41 | # With the parameters specified above, a StandardDataset can now be created. You can interate through 42 | # it using the PyTorch DataLoader class. 43 | # There are several optional arguments in the my_dataset class that are not featured here for the sake of 44 | # simplicity. Note that, for example, it is possible to include the previous and/or subsequent frames 45 | # in a video sequence using tha parameter video_frames. 46 | my_test_dataset = SimpleDataset(dataset=dataset, trainvaltest_split='train', 47 | simple_mode=True, 48 | keys_to_load=['color', 'segmentation'], 49 | data_transforms=data_transforms 50 | ) 51 | 52 | my_loader = DataLoader(my_test_dataset, batch_size=1, 53 | shuffle=False, num_workers=0, pin_memory=True, drop_last=True) 54 | 55 | # Print the sizes of the first 3 elements to show how the elements are indexed. Each element 56 | # of the dataset is a dictionary with the 3-tuples as keys. The first entry corrsponds to the image 57 | # category. The second is a video frame index which will be always zero or non-video datasets. 58 | # The third entry is a resolution parameter showing if it is a scaled image. 59 | for element, _ in zip(my_loader, range(3)): 60 | print("###########################################################################################################") 61 | #regular color image 62 | image_tensor = np.array(element[('color', 0, 0)]) # read image tensor to numpy array 63 | image_trans = image_tensor[0].transpose(1, 2, 0) # transpose: opencv needs color channels at last 64 | image = image_trans[..., ::-1] # Revert Color Channels 65 | 66 | #augmented color image 67 | aug_img_tensor = np.array(element[('color_aug', 0, 0)]) 68 | aug_img = aug_img_tensor[0].transpose(1, 2, 0) 69 | aug_img = aug_img[..., ::-1] 70 | 71 | # Fuse images together for displaying 72 | whole_img = np.concatenate((image, aug_img), 0) 73 | whole_img = cv2.resize(whole_img, (int(2048/2.2), int(1024*2/2.2))) #resize them for better visiablity 74 | 75 | cv2.imshow("augmented image", whole_img) 76 | cv2.waitKey() 77 | 78 | for key in element: 79 | print('Key: {}, Element Size: {}'.format(key, element[key].shape)) 80 | 81 | -------------------------------------------------------------------------------- /dataloader/data_preprocessing/Mapillary/labels.py: -------------------------------------------------------------------------------- 1 | #+++++++++++++++++++++++++++++++++++++++++++++++ 2 | # 3 | # Author: Philipp Donn 4 | # 5 | # Date: 11/10/2019 6 | # 7 | # Supervisor: Marvin Klingner 8 | # 9 | #+++++++++++++++++++++++++++++++++++++++++++++++ 10 | # 11 | # Create labels_file.py from config.json 12 | # 13 | #+++++++++++++++++++++++++++++++++++++++++++++++ 14 | 15 | import json 16 | from operator import itemgetter 17 | 18 | 19 | def write_line(f, name, id, trainId, category, catId, hasInstances, ignoreInEval, color): 20 | f.write(" Label( {},{:3d} ,{:9d} , {}, {}, {}, {}, ({:3d},{:3d},{:3d}) ),\n".format("'{}'".format(name).ljust(27).lower(), id, trainId, "'{}'".format(category).ljust(18), '{}'.format(catId).ljust(8), str(hasInstances).ljust(13), ignoreInEval.ljust(12), color[0], color[1], color[2])) 21 | 22 | 23 | def create_labels_file(): 24 | with open('config.json', 'r') as f: 25 | config = json.load(f) 26 | 27 | labels = config['labels'] 28 | 29 | with open('labels_file.py', 'w') as f: 30 | id = 0 31 | train_id = 0 32 | animal = [] 33 | construction = [] 34 | human = [] 35 | marking = [] 36 | nature = [] 37 | object = [] 38 | void = [] 39 | for label in labels: 40 | if label['name'].split('-')[0] == 'animal': 41 | animal.append(label) 42 | elif label['name'].split('-')[0] == 'construction': 43 | construction.append(label) 44 | elif label['name'].split('-')[0] == 'human': 45 | human.append(label) 46 | elif label['name'].split('-')[0] == 'marking': 47 | marking.append(label) 48 | elif label['name'].split('-')[0] == 'nature': 49 | nature.append(label) 50 | elif label['name'].split('-')[0] == 'object': 51 | object.append(label) 52 | elif label['name'].split('-')[0] == 'void': 53 | void.append(label) 54 | else: 55 | print(label['name'].split('-')[0]) 56 | 57 | animal = sorted(animal, key=itemgetter('readable')) 58 | construction = sorted(construction, key=itemgetter('readable')) 59 | human = sorted(human, key=itemgetter('readable')) 60 | marking = sorted(marking, key=itemgetter('readable')) 61 | nature = sorted(nature, key=itemgetter('readable')) 62 | object = sorted(object, key=itemgetter('readable')) 63 | void = sorted(void, key=itemgetter('readable')) 64 | f.write('labels_mapillary_seg = ClassDefinitions([\n # name id trainId category catId hasInstances ignoreInEval color\n') 65 | for label in void: 66 | cat_id = 0 67 | write_line(f, label['readable'], id, 255, label['name'].split('-')[0], cat_id, label['instances'], 'True', label['color']) 68 | id += 1 69 | for label in construction: 70 | cat_id = 2 71 | write_line(f, label['readable'], id, train_id, label['name'].split('-')[0], cat_id, label['instances'], 'False', label['color']) 72 | id += 1 73 | train_id += 1 74 | for label in object: 75 | cat_id = 3 76 | write_line(f, label['readable'], id, train_id, label['name'].split('-')[0], cat_id, label['instances'], 'False', label['color']) 77 | id += 1 78 | train_id += 1 79 | for label in nature: 80 | cat_id = 4 81 | write_line(f, label['readable'], id, train_id, label['name'].split('-')[0], cat_id, label['instances'], 'False', label['color']) 82 | id += 1 83 | train_id += 1 84 | for label in human: 85 | cat_id = 6 86 | write_line(f, label['readable'], id, train_id, label['name'].split('-')[0], cat_id, label['instances'], 'False', label['color']) 87 | id += 1 88 | train_id += 1 89 | for label in marking: 90 | cat_id = 8 91 | write_line(f, label['readable'], id, train_id, label['name'].split('-')[0], cat_id, label['instances'], 'False', label['color']) 92 | id += 1 93 | train_id += 1 94 | for label in animal: 95 | cat_id = 9 96 | write_line(f, label['readable'], id, train_id, label['name'].split('-')[0], cat_id, label['instances'], 'False', label['color']) 97 | id += 1 98 | train_id += 1 99 | f.write('])\n') 100 | 101 | 102 | if __name__ == '__main__': 103 | create_labels_file() 104 | -------------------------------------------------------------------------------- /dataloader/pt_data_loader/specialdatasets.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, division, print_function 2 | 3 | import sys 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | from torch.utils.data import DataLoader 7 | 8 | from dataloader.pt_data_loader.basedataset import BaseDataset 9 | import dataloader.pt_data_loader.mytransforms as mytransforms 10 | import dataloader.definitions.labels_file as lf 11 | 12 | 13 | class StandardDataset(BaseDataset): 14 | def __init__(self, *args, **kwargs): 15 | super(StandardDataset, self).__init__(*args, **kwargs) 16 | 17 | if self.disable_const_items is False: 18 | assert self.parameters.K is not None and self.parameters.stereo_T is not None, '''There are no K matrix and 19 | stereo_T parameter available for this dataset.''' 20 | 21 | def add_const_dataset_items(self, sample): 22 | K = self.parameters.K.copy() 23 | 24 | native_key = ('color', 0, -1) if (('color', 0, -1) in sample) else ('color_right', 0, -1) 25 | native_im_shape = sample[native_key].shape 26 | 27 | K[0, :] *= native_im_shape[1] 28 | K[1, :] *= native_im_shape[0] 29 | 30 | sample["K", -1] = K 31 | sample["stereo_T"] = self.parameters.stereo_T 32 | 33 | return sample 34 | 35 | 36 | class KITTIDataset(StandardDataset): 37 | def __init__(self, *args, **kwargs): 38 | super(KITTIDataset, self).__init__(*args, **kwargs) 39 | 40 | 41 | class MapillaryDataset(StandardDataset): 42 | def __init__(self, *args, **kwargs): 43 | super(MapillaryDataset, self).__init__(*args, **kwargs) 44 | 45 | 46 | class CityscapesDataset(StandardDataset): 47 | def __init__(self, *args, **kwargs): 48 | super(CityscapesDataset, self).__init__(*args, **kwargs) 49 | 50 | 51 | class Gta5Dataset(StandardDataset): 52 | def __init__(self, *args, **kwargs): 53 | super(Gta5Dataset, self).__init__(*args, **kwargs) 54 | 55 | 56 | class SimpleDataset(BaseDataset): 57 | ''' 58 | Dataset that uses the Simple Mode. keys_to_load must be specified. 59 | ''' 60 | def __init__(self, *args, **kwargs): 61 | super(SimpleDataset, self).__init__(*args, **kwargs) 62 | 63 | def add_const_dataset_items(self, sample): 64 | return sample 65 | 66 | 67 | if __name__ == '__main__': 68 | """ 69 | The following code is an example of how a dataloader object can be created for a specific dataset. In this case, 70 | the cityscapes dataset is used. 71 | 72 | Every dataset should be created using the StandardDataset class. Necessary arguments for its constructor are the 73 | dataset name and the information whether to load the train, validation or test split. In this standard setting, 74 | for every dataset only the color images are loaded. The user can pass a list of keys_to_load in order to also use 75 | other data categories, depending on what is available in the dataset. It is also possible to define a list of 76 | transforms that are performed every time an image is loaded from the dataset. 77 | """ 78 | def print_dataset(dataloader, num_elements=3): 79 | """ 80 | This little function prints the size of every element in a certain amount of dataloader samples. 81 | 82 | :param dataloader: dataloader object that yields the samples 83 | :param num_elements: number of samples of which the sizes are to be printed 84 | """ 85 | for element, i in zip(dataloader, range(num_elements)): 86 | print('+++ Image {} +++'.format(i)) 87 | for key in element.keys(): 88 | print(key, element[key].shape) 89 | plt.imshow(np.array(element[('color', 0, 0)])[0, :, :, :].transpose(1, 2, 0)) 90 | 91 | # Simple example of how to load a dataset. Every supported dataset can be loaded that way. 92 | dataset = 'cityscapes' 93 | trainvaltest_split = 'train' 94 | keys_to_load = ['color', 'depth', 'segmentation', 'camera_intrinsics'] # Optional; standard is just 'color' 95 | 96 | # The following parameters and the data_transforms list are optional. Standard is just the transform ToTensor() 97 | width = 640 98 | height = 192 99 | scales = [0, 1, 2, 3] 100 | data_transforms = [#mytransforms.RandomExchangeStereo(), # (color, 0, -1) 101 | mytransforms.RandomHorizontalFlip(), 102 | mytransforms.RandomVerticalFlip(), 103 | mytransforms.CreateScaledImage(), # (color, 0, 0) 104 | mytransforms.RandomRotate(0.0), 105 | mytransforms.RandomTranslate(0), 106 | mytransforms.RandomRescale(scale=1.1, fraction=0.5), 107 | mytransforms.RandomCrop((320, 1088)), 108 | mytransforms.Resize((height, width)), 109 | mytransforms.MultiResize(scales), 110 | mytransforms.CreateColoraug(new_element=True, scales=scales), # (color_aug, 0, 0) 111 | mytransforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, 112 | hue=0.1, gamma=0.0), 113 | mytransforms.GaussianBlurr(fraction=0.5), 114 | mytransforms.RemoveOriginals(), 115 | mytransforms.ToTensor(), 116 | mytransforms.NormalizeZeroMean(), 117 | ] 118 | 119 | print('Loading {} dataset, {} split'.format(dataset, trainvaltest_split)) 120 | traindataset = StandardDataset(dataset, 121 | trainvaltest_split, 122 | keys_to_load=keys_to_load, 123 | stereo_mode='mono', 124 | keys_to_stereo=['color', 'depth', 'segmentation'], 125 | data_transforms=data_transforms 126 | ) 127 | trainloader = DataLoader(traindataset, batch_size=1, 128 | shuffle=False, num_workers=1, pin_memory=True, drop_last=True) 129 | print(traindataset.stereo_mode) 130 | print_dataset(trainloader) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | logo_ifn
2 | 3 | # Dataloader 4 | 5 | ## About 6 | This is the dataloader mainly developed by [Marvin Klingner](https://www.tu-braunschweig.de/en/ifn/institute/team/sv/klingner) and Marcel Mross at the Institute for Communications Technology. It currently supports the loading of images, depth maps, segmentation masks and (experimental) optical flow maps. Furthermore the data can be loaded as single images, stereo image pairs or image sequences of arbitrary user-defined lengths. Additionally, various augmentations are pre-implemented. 7 | 8 | ## Support 9 | Currently among others, the following datasets are supported: supported datasets 10 | - Cityscapes 11 | - KITTI 12 | - BDD100k 13 | - Mapillary 14 | - SYNTHIA 15 | - Virtual KITTI 16 | - Gta5 17 | - Camvid 18 | 19 | ## Requirements 20 | We always intend to support the newest PyTorch release, as this code is actively used throughout various projects at the Institute for Communications Technology. At the moment our code is tested with. 21 | 22 | ``` 23 | pytorch>=1.1 24 | matplotlib 25 | opencv>=4.4.0 26 | pandas>=1.1.1 27 | scipy>=1.5.2 28 | numpy>=1.18.1 29 | ``` 30 | 31 | ## Installation 32 | 1. 'cd' into the dataloader directory 33 | 2. Execute `pip install . -e` in your environment 34 | 3. Use e.g. `from dataloader.pt_data_loader.specialdatasets import StandardDataset` in your code 35 | 36 | ## Environment Variables 37 | The dataloader is working with environment variables to guarantee a better portability between different systems (especially between Windows and Linux). For this reason you need export the following environment variables before starting your scripts/jobs, if they utilize our dataloader: 38 | 39 | ##### Linux 40 | ``` 41 | export IFN_DIR_DATASET=/path/to/datasets/ 42 | export IFN_DIR_CHECKPOINT=/path/to/Checkpoints 43 | ``` 44 | You may also add this to your .bashrc, if you want to store these variables permanently. 45 | 46 | ##### Windows 47 | ``` 48 | setx IFN_DIR_DATASET /path/to/datasets/ 49 | setx IFN_DIR_CHECKPOINT /path/to/Checkpoints 50 | ``` 51 | 52 | 53 | ## Dataset Preparation 54 | Our dataloader is using a ```.json``` files to determine the dependencies of files and annotations from different directories. 55 | To create them for a specific dataset please refer to `file_io/README.md`. 56 | Additionally an example is shown in ```example/cityscapes_preparation_example.py```. 57 | 58 | The json should be placed in the root folder of the dataset. The structure should look like this: 59 | 60 | 61 | 62 | basic_files.json 63 | parameters.json 64 | test.json 65 | train.json 66 | validation.json 67 | 68 | <...> 69 | 70 | <...> 71 | ... 72 | 73 | <...> 74 | 75 | basic_files.json 76 | parameters.json 77 | 78 | 79 | <...> 80 | ... 81 | 82 | <...> 83 | ... 84 | 85 | #### KITTI 86 | For KITTI a download script can be found in `data_preprocessing/KITTI/download_kitti.py`. The necessary directory structure 87 | is automatically created. 88 | ```python3 download_kitti.py``` 89 | 90 | 91 | 92 | ## Using the Dataloader 93 | For using the dataset simply create a ```StandardDataset``` object, which you can import from the ```dataloader/pt_data_loader/basedataset.py``` file. 94 | It is loadable with the custom ```DataLoader``` class supplied by PyTorch. 95 | Exemplary job scripts are supplied in the ```example``` directory, in particular for Cityscapes an example is shown in ```example/cityskapes_dataloader_example``` 96 | 97 | ## Dataset .json download 98 | 99 | We supply some pre-created json files, however you should be able to create them yourself using the scripts supplied in the ```dataloader/pt_data_loader/file_io``` folder. 100 | 101 | -Kitti: https://drive.google.com/drive/folders/1Qmb31xqcLAR9DgicN9Lo5GNxXujuyK5N?usp=sharing 102 | 103 | -Kitti eigen split: https://drive.google.com/drive/folders/1dnYSNYLrvB4K5Hu7A4qXsd1K9gN6FDYu?usp=sharing 104 | 105 | -Kitti zhou split: https://drive.google.com/drive/folders/1sLJQM3VZWFG44lHbPaTnr6EVu3d3ksMX?usp=sharing 106 | 107 | -Kitti zhou split left: https://drive.google.com/drive/folders/1fgNXAahOZapjQmJy-hMY8nGBwJw3lBbi?usp=sharing 108 | 109 | -Kitti zhou split right: https://drive.google.com/drive/folders/1FeELyyH7o7mgbPnBO4V5IrE5xC6QVq_-?usp=sharing 110 | 111 | -Kitti benchmark split: https://drive.google.com/drive/folders/1WamSihx64tjrjHqMDAD8N2eHxnefi6K5?usp=sharing 112 | 113 | -Cityscapes: https://drive.google.com/drive/folders/1Ky66UBRtMCBgu2qp6Nchd9psZkDgeuyT?usp=sharing 114 | 115 | -Gta5: https://drive.google.com/drive/folders/10dYTpFhg3hYFrTPaD2StCyYeffDus2qq?usp=sharing 116 | 117 | 118 | 119 | ## Simple Mode 120 | If your dataset is not (yet) supported you can either choose the simple mode or implement the dataset yourself (refer to the next section) 121 | For using the simple mode instead of using the `StandardDataset` class, use the `SimpleDataset` class and set `simple_mode=True` 122 | ``` 123 | dataset = SimpleDataset(dataset=dataset, trainvaltest_split='train', 124 | simple_mode=True, 125 | keys_to_load=['color', 'segmentation'], 126 | data_transforms=data_transforms 127 | ) 128 | ``` 129 | Folders should have the same name as keys. Folder structure is assumed to be as follows: 130 | 131 | 132 | color 133 | 134 | ... 135 | 136 | segmentation 137 | 138 | ... 139 | 140 | ... 141 | 142 | Additionally there needs to be a `parameters.json` file: 143 | 144 | ``` 145 | {"K": null, "stereo_T": null, "labels": "None", "labels_mode": "fromid", "depth_mode": null, "flow_mode": null, "splits": null} 146 | ``` 147 | The following parameters need to be defined: 148 | 149 | - K: Extrinsic camera matrix as a Numpy array. If not available, take None 150 | - stereo_T: Distance between the two cameras (see e.g. http://www.cvlibs.net/datasets/kitti/setup.php, 0.54m) 151 | - labels: 152 | - labels_mode: 'fromid' or 'fromrgb', depending on which format the segmentation images have 153 | - depth_mode: 'uint_16' or 'uint_16_subtract_one' depending on which format the depth images have 154 | - flow_mode: specifies how the flow images are stored, e.g. 'kitti' 155 | - splits: List of splits that are available for this dataset 156 | 157 | 158 | As of the others, an example script can be found in the `example` dir. 159 | 160 | ## Create Dataset Support 161 | For creating support for your dataset please refer to `dataloader/file_io/README.md` 162 | - (optional) open a pull request. Maybe other people want to use the same dataset as you. Thank you in advance! 163 | 164 | ## License 165 | 166 | This project is licensed under the MIT License. 167 | -------------------------------------------------------------------------------- /dataloader/file_io/dataset_index.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import numpy as np 4 | import json 5 | 6 | import dataloader.file_io.get_path as gp 7 | 8 | """ 9 | The following paramters have to be present in every dataset definition: 10 | - 'K': Extrinsic camera matrix as a Numpy array. If not available, take None 11 | - 'stereo_T': Distance between the two cameras (see e.g. http://www.cvlibs.net/datasets/kitti/setup.php, 0.54m) 12 | - 'labels_mode': 'fromid' or 'fromrgb', depending on which format the segmentation images have 13 | - 'depth_mode': 'uint_16' or 'uint_16_subtract_one' depending on which format the depth images have 14 | - 'splits': List of splits that are available for this dataset 15 | """ 16 | 17 | dataset_index = { 18 | 'a2d2': 19 | {'K': None, 20 | 'stereo_T': None, 21 | 'labels': 'a2d2', 22 | 'labels_mode': 'fromrgb', 23 | 'depth_mode': None, 24 | 'flow_mode': None, 25 | 'splits': ('andreas_split',) 26 | }, 27 | 'bdd100k': 28 | {'K': None, 29 | 'stereo_T': None, 30 | 'labels': 'bdd100k', 31 | 'labels_mode': 'fromtrainid', 32 | 'depth_mode': None, 33 | 'flow_mode': None, 34 | 'splits': None 35 | }, 36 | 'camvid': 37 | {'K': None, 38 | 'stereo_T': None, 39 | 'labels': 'camvid', 40 | 'labels_mode': 'fromrgb', 41 | 'depth_mode': None, 42 | 'flow_mode': None, 43 | 'splits': None 44 | }, 45 | 'cityscapes': 46 | {'K': [[1.10, 0, 0.5, 0], 47 | [0, 2.21, 0.5, 0], 48 | [0, 0, 1, 0], 49 | [0, 0, 0, 1]], 50 | 'stereo_T': 0.22, 51 | 'labels': 'cityscapes', 52 | 'labels_mode': 'fromid', 53 | 'depth_mode': 'uint_16_subtract_one', 54 | 'flow_mode': None, 55 | 'splits': None 56 | }, 57 | 'cityscapes_demo_video': 58 | {'K': [[1.10, 0, 0.5, 0], 59 | [0, 2.21, 0.5, 0], 60 | [0, 0, 1, 0], 61 | [0, 0, 0, 1]], 62 | 'stereo_T': 0.22, 63 | 'labels': 'cityscapes', 64 | 'labels_mode': 'fromid', 65 | 'depth_mode': None, 66 | 'flow_mode': None, 67 | 'splits': None 68 | }, 69 | 'cityscapes_extra': 70 | {'K': [[1.10, 0, 0.5, 0], 71 | [0, 2.21, 0.5, 0], 72 | [0, 0, 1, 0], 73 | [0, 0, 0, 1]], 74 | 'stereo_T': 0.22, 75 | 'labels': 'cityscapes', 76 | 'labels_mode': 'fromid', 77 | 'depth_mode': 'uint_16_subtract_one', 78 | 'flow_mode': None, 79 | 'splits': None 80 | }, 81 | 'cityscapes_sequence': 82 | {'K': [[1.10, 0, 0.5, 0], 83 | [0, 2.21, 0.5, 0], 84 | [0, 0, 1, 0], 85 | [0, 0, 0, 1]], 86 | 'stereo_T': 0.22, 87 | 'labels': 'cityscapes', 88 | 'labels_mode': 'fromid', 89 | 'depth_mode': 'uint_16_subtract_one', 90 | 'flow_mode': None, 91 | 'splits': None 92 | }, 93 | 'cityscapes_video': 94 | {'K': [[1.10, 0, 0.5, 0], 95 | [0, 2.21, 0.5, 0], 96 | [0, 0, 1, 0], 97 | [0, 0, 0, 1]], 98 | 'stereo_T': 0.22, 99 | 'labels': 'cityscapes', 100 | 'labels_mode': None, 101 | 'depth_mode': None, 102 | 'flow_mode': None, 103 | 'splits': None 104 | }, 105 | 'gta5': 106 | {'K': None, 107 | 'stereo_T': None, 108 | 'labels': 'gta5', 109 | 'labels_mode': 'fromrgb', 110 | 'depth_mode': None, 111 | 'flow_mode': None, 112 | 'splits': None 113 | }, 114 | 'kitti': 115 | {'K': [[0.58, 0, 0.5, 0], 116 | [0, 1.92, 0.5, 0], 117 | [0, 0, 1, 0], 118 | [0, 0, 0, 1]], 119 | 'stereo_T': 0.54, 120 | 'labels': 'kitti', 121 | 'labels_mode': 'fromid', 122 | 'depth_mode': 'uint_16', 123 | 'flow_mode': None, 124 | 'splits': ('eigen_split', 'zhou_split', 'zhou_split_left', 'zhou_split_right', 'benchmark_split', 125 | 'kitti_split', 'odom10_split', 'odom09_split', 'video_prediction_split', 'optical_flow_split'), 126 | }, 127 | 'kitti_2012': 128 | {'K': [[0.58, 0, 0.5, 0], 129 | [0, 1.92, 0.5, 0], 130 | [0, 0, 1, 0], 131 | [0, 0, 0, 1]], 132 | 'stereo_T': 0.54, 133 | 'labels': 'kitti', 134 | 'labels_mode': 'fromid', 135 | 'depth_mode': 'uint_16', 136 | 'flow_mode': None, 137 | 'splits': None 138 | }, 139 | 'kitti_2015': 140 | {'K': [[0.58, 0, 0.5, 0], 141 | [0, 1.92, 0.5, 0], 142 | [0, 0, 1, 0], 143 | [0, 0, 0, 1]], 144 | 'stereo_T': 0.54, 145 | 'labels': 'kitti', 146 | 'labels_mode': 'fromid', 147 | 'depth_mode': 'uint_16', 148 | 'flow_mode': 'kitti', 149 | 'splits': None 150 | }, 151 | 'lostandfound': 152 | {'K': None, 153 | 'stereo_T': None, 154 | 'labels': 'lostandfound', 155 | 'labels_mode': 'fromid', 156 | 'depth_mode': None, 157 | 'flow_mode': None, 158 | 'splits': None 159 | }, 160 | 'make3d': 161 | {'K': None, 162 | 'stereo_T': None, 163 | 'labels': None, 164 | 'labels_mode': None, 165 | 'depth_mode': 'uint_16', 166 | 'flow_mode': None, 167 | 'splits': None 168 | }, 169 | 'mapillary': 170 | {'K': None, 171 | 'stereo_T': None, 172 | 'labels': 'mapillary', 173 | 'labels_mode': 'fromrgb', 174 | 'depth_mode': None, 175 | 'flow_mode': None, 176 | 'splits': None 177 | }, 178 | 'synthia': 179 | {'K': None, 180 | 'stereo_T': None, 181 | 'labels': 'synthia', 182 | 'labels_mode': 'fromid', 183 | 'depth_mode': 'normalized_100', 184 | 'flow_mode': None, 185 | 'splits': None 186 | }, 187 | 'virtual_kitti': 188 | {'K': [[0.58, 0, 0.5, 0], 189 | [0, 1.92, 0.5, 0], 190 | [0, 0, 1, 0], 191 | [0, 0, 0, 1]], 192 | 'stereo_T': 0.54, 193 | 'labels': 'virtual_kitti', 194 | 'labels_mode': 'fromrgb', 195 | 'depth_mode': 'normalized_100', 196 | 'flow_mode': None, 197 | 'splits': ('full_split',) 198 | }, 199 | 'voc2012': 200 | {'K': None, 201 | 'stereo_T': None, 202 | 'labels': None, 203 | 'labels_mode': 'fromtrainid', 204 | 'depth_mode': None, 205 | 'flow_mode': None, 206 | 'splits': None 207 | }, 208 | } 209 | 210 | 211 | def create_parameter_files(datasets=None): 212 | if datasets is None: 213 | datasets = dataset_index.keys() 214 | parameters = dataset_index.values() 215 | else: 216 | if type(datasets) == str: 217 | datasets = [datasets] 218 | parameters = [] 219 | for set in datasets: 220 | assert set in dataset_index.keys(), '{} is not a valid dataset'.format(set) 221 | parameters.append(dataset_index[set]) 222 | path_getter = gp.GetPath() 223 | data_path = path_getter.get_data_path() 224 | for dataset, param in zip(datasets, parameters): 225 | dataset_path = os.path.join(data_path, dataset) 226 | if os.path.isdir(dataset_path): 227 | dump_location = os.path.join(dataset_path, 'parameters.json') 228 | with open(dump_location, 'w') as fp: 229 | json.dump(param, fp) 230 | print("{}: OK".format(dataset)) 231 | else: 232 | print("{}: not found".format(dataset)) 233 | 234 | 235 | if __name__ == '__main__': 236 | if len(sys.argv) > 1: 237 | datasets = sys.argv[1:] 238 | else: 239 | datasets = None 240 | create_parameter_files(datasets) 241 | 242 | 243 | -------------------------------------------------------------------------------- /dataloader/pt_data_loader/dataset_tester.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import torch 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | from torch.utils.data import DataLoader 6 | 7 | from dataloader.pt_data_loader.specialdatasets import StandardDataset 8 | import dataloader.pt_data_loader.mytransforms as mytransforms 9 | 10 | NUM_SAMPLES = 3 11 | 12 | def print_dataset(dataloader): 13 | """ Loads the first NUM_SAMPLES elements in the dataloder and prints their keys and shapes. 14 | 15 | :param dataloader: Dataloader object 16 | """ 17 | i = 0 18 | for element in dataloader: 19 | for key in element.keys(): 20 | if key == 'filename': 21 | print("Key: {}, First element: {}".format(key, element[key][list(element[key].keys())[0]])) 22 | else: 23 | print("Key: {}, Element Size: {}".format(key, element[key].shape)) 24 | i += 1 25 | if i >= NUM_SAMPLES: 26 | break 27 | 28 | 29 | def check_dataset(dataset_name, split=None, trainvaltest_split='train', keys_to_load=None, folders_to_load=None): 30 | """ Loads a dataset and prints name and shape of the first NUM_SAMPLES entries. Performs no transforms other than 31 | the necessary ones. 32 | 33 | :param dataset_name: Name of the dataset 34 | :param split: Name of the dataset split, if one exists 35 | :param trainvaltest_split: 'train', 'validation' or 'test' 36 | :param keys_to_load: keys that are supposed to be loaded, e.g. 'color', 'depth', 'segmentation', ... 37 | """ 38 | dataset = dataset_name 39 | data_transforms = [mytransforms.CreateScaledImage(), 40 | mytransforms.CreateColoraug(), 41 | mytransforms.ToTensor(), 42 | ] 43 | if keys_to_load is not None: 44 | if any('depth' in key for key in keys_to_load): 45 | data_transforms.insert(0, mytransforms.ConvertDepth()) 46 | if any('segmentation' in key for key in keys_to_load): 47 | data_transforms.insert(0, mytransforms.ConvertSegmentation()) 48 | if any('flow' in key for key in keys_to_load): 49 | data_transforms.insert(0, mytransforms.ConvertFlow()) 50 | print('\n Loading {} dataset'.format(dataset)) 51 | my_dataset = StandardDataset(dataset, 52 | split=split, 53 | trainvaltest_split=trainvaltest_split, 54 | keys_to_load=keys_to_load, 55 | data_transforms=data_transforms, 56 | folders_to_load=folders_to_load 57 | ) 58 | my_loader = DataLoader(my_dataset, batch_size=1, 59 | shuffle=False, num_workers=1, pin_memory=True, drop_last=True) 60 | print('Number of elements: {}'.format(len(my_dataset))) 61 | print_dataset(my_loader) 62 | 63 | 64 | def check_scaled_dataset(dataset_name, scaled_dataset_name, trainvaltest_split, keys_to_load, scaled_size, split=None): 65 | """ Checks whether the images in a dataset generated by the dataset_scaler are identical to the images that are 66 | generated by loading the original dataset and scaling them afterwards 67 | 68 | :param dataset_name: Name of the unscaled dataset 69 | :param scaled_dataset_name: Name of the scaled dataset 70 | :param trainvaltest_split: 'train', 'validation' or 'test' 71 | :param keys_to_load: keys that are supposed to be loaded, e.g. 'color', 'depth', 'segmentation', ... 72 | :param scaled_size: Size of the scaled image (h, w) 73 | :param split: Name of the dataset split, if one exists 74 | """ 75 | dataset = dataset_name 76 | data_transforms = [mytransforms.CreateScaledImage(), 77 | mytransforms.Resize(output_size=scaled_size), 78 | mytransforms.CreateColoraug(), 79 | mytransforms.ToTensor(), 80 | ] 81 | if keys_to_load is not None: 82 | if any('depth' in key for key in keys_to_load): 83 | data_transforms.insert(0, mytransforms.ConvertDepth()) 84 | if any('segmentation' in key for key in keys_to_load): 85 | data_transforms.insert(0, mytransforms.ConvertSegmentation()) 86 | print('\n Loading {} dataset'.format(dataset)) 87 | my_dataset = StandardDataset(dataset, 88 | split=split, 89 | trainvaltest_split=trainvaltest_split, 90 | keys_to_load=keys_to_load, 91 | data_transforms=data_transforms, 92 | output_filenames=True 93 | ) 94 | my_loader = DataLoader(my_dataset, batch_size=1, 95 | shuffle=False, num_workers=0, pin_memory=True, drop_last=True) 96 | print_dataset(my_loader) 97 | 98 | dataset_s = scaled_dataset_name 99 | data_transforms = [mytransforms.CreateScaledImage(), 100 | mytransforms.CreateColoraug(), 101 | mytransforms.ToTensor(), 102 | ] 103 | if keys_to_load is not None: 104 | if any('depth' in key for key in keys_to_load): 105 | data_transforms.insert(0, mytransforms.ConvertDepth()) 106 | if any('segmentation' in key for key in keys_to_load): 107 | data_transforms.insert(0, mytransforms.ConvertSegmentation()) 108 | print('\n Loading {} dataset'.format(dataset_s)) 109 | my_dataset_s = StandardDataset(dataset_s, 110 | split=split, 111 | trainvaltest_split=trainvaltest_split, 112 | keys_to_load=keys_to_load, 113 | data_transforms=data_transforms, 114 | output_filenames=True 115 | ) 116 | my_loader_s = DataLoader(my_dataset_s, batch_size=1, 117 | shuffle=False, num_workers=0, pin_memory=True, drop_last=True) 118 | print_dataset(my_loader_s) 119 | print("Testing dataset_scaler") 120 | samples = [] 121 | samples_s = [] 122 | iter_my_loader = iter(my_loader) 123 | iter_my_loader_s = iter(my_loader_s) 124 | for _ in range(2): 125 | samples.append(next(iter_my_loader).copy()) 126 | samples_s.append(next(iter_my_loader_s).copy()) 127 | for key in keys_to_load: 128 | print("Check if {} entries are equal:".format(key)) 129 | print(" Should be False: {}".format( 130 | torch.equal(samples[1][(key, 0, 0)], samples_s[0][(key, 0, 0)]))) 131 | print(" Should be True: {}".format( 132 | torch.equal(samples[0][(key, 0, 0)], samples_s[0][(key, 0, 0)]))) 133 | print(" Should be True: {}".format( 134 | torch.equal(samples[1][(key, 0, 0)], samples_s[1][(key, 0, 0)]))) 135 | 136 | 137 | if __name__ == '__main__': 138 | check_dataset('bdd100k', keys_to_load=['color', 'segmentation']) 139 | check_dataset('gta5') 140 | check_dataset('cityscapes', keys_to_load=['color', 'depth', 'segmentation'], 141 | folders_to_load=['bremen', 'darmstadt', 'erfurt']) 142 | check_dataset('cityscapes', keys_to_load=['color', 'depth', 'segmentation'], 143 | folders_to_load=['ulm', 'strasbourg', 'hanover']) 144 | check_dataset('cityscapes', keys_to_load=['color', 'depth', 'segmentation'], 145 | folders_to_load=['aachen', 'zurich', 'tubingen']) 146 | check_dataset('kitti', split='eigen_split', keys_to_load=['color', 'depth']) 147 | check_dataset('synthia', keys_to_load=['color', 'depth', 'segmentation']) 148 | check_dataset('kitti_2015', keys_to_load=['color', 'depth', 'flow']) 149 | 150 | check_scaled_dataset('gta5', 'gta5_mross_scaled', trainvaltest_split='train', 151 | keys_to_load=['color', 'segmentation'], scaled_size=(526, 957)) 152 | check_scaled_dataset('make3d', 'make3d_mross_scaled_2', trainvaltest_split='train', 153 | keys_to_load=['color', 'depth'], scaled_size=(1136, 852)) 154 | check_scaled_dataset('virtual_kitti', 'virtual_kitti_mross_scaled', trainvaltest_split='train', 155 | keys_to_load=['color', 'depth', 'segmentation'], scaled_size=(187, 621), split="full_split") 156 | check_scaled_dataset('synthia', 'synthia_mross_scaled', trainvaltest_split='train', 157 | keys_to_load=['color', 'depth', 'segmentation'], scaled_size=(380, 640)) 158 | check_scaled_dataset('bdd100k', 'bdd100k_mross_scaled', trainvaltest_split='train', 159 | keys_to_load=['color', 'segmentation'], scaled_size=(360, 640)) -------------------------------------------------------------------------------- /dataloader/data_preprocessing/Mapillary/labels_file.py: -------------------------------------------------------------------------------- 1 | labels_mapillary_seg = ClassDefinitions([ 2 | # name id trainId category catId hasInstances ignoreInEval color 3 | Label( 'car mount' , 0 , 255 , 'void' , 0 , False , True , ( 32, 32, 32) ), 4 | Label( 'ego vehicle' , 1 , 255 , 'void' , 0 , False , True , (120, 10, 10) ), 5 | Label( 'unlabeled' , 2 , 255 , 'void' , 0 , False , True , ( 0, 0, 0) ), 6 | Label( 'barrier' , 3 , 0 , 'construction' , 2 , False , False , ( 90,120,150) ), 7 | Label( 'bike lane' , 4 , 1 , 'construction' , 2 , False , False , (128, 64,255) ), 8 | Label( 'bridge' , 5 , 2 , 'construction' , 2 , False , False , (150,100,100) ), 9 | Label( 'building' , 6 , 3 , 'construction' , 2 , False , False , ( 70, 70, 70) ), 10 | Label( 'crosswalk - plain' , 7 , 4 , 'construction' , 2 , True , False , (140,140,200) ), 11 | Label( 'curb' , 8 , 5 , 'construction' , 2 , False , False , (196,196,196) ), 12 | Label( 'curb cut' , 9 , 6 , 'construction' , 2 , False , False , (170,170,170) ), 13 | Label( 'fence' , 10 , 7 , 'construction' , 2 , False , False , (190,153,153) ), 14 | Label( 'guard rail' , 11 , 8 , 'construction' , 2 , False , False , (180,165,180) ), 15 | Label( 'parking' , 12 , 9 , 'construction' , 2 , False , False , (250,170,160) ), 16 | Label( 'pedestrian area' , 13 , 10 , 'construction' , 2 , False , False , ( 96, 96, 96) ), 17 | Label( 'rail track' , 14 , 11 , 'construction' , 2 , False , False , (230,150,140) ), 18 | Label( 'road' , 15 , 12 , 'construction' , 2 , False , False , (128, 64,128) ), 19 | Label( 'service lane' , 16 , 13 , 'construction' , 2 , False , False , (110,110,110) ), 20 | Label( 'sidewalk' , 17 , 14 , 'construction' , 2 , False , False , (244, 35,232) ), 21 | Label( 'tunnel' , 18 , 15 , 'construction' , 2 , False , False , (150,120, 90) ), 22 | Label( 'wall' , 19 , 16 , 'construction' , 2 , False , False , (102,102,156) ), 23 | Label( 'banner' , 20 , 17 , 'object' , 3 , True , False , (255,255,128) ), 24 | Label( 'bench' , 21 , 18 , 'object' , 3 , True , False , (250, 0, 30) ), 25 | Label( 'bicycle' , 22 , 19 , 'object' , 3 , True , False , (119, 11, 32) ), 26 | Label( 'bike rack' , 23 , 20 , 'object' , 3 , True , False , (100,140,180) ), 27 | Label( 'billboard' , 24 , 21 , 'object' , 3 , True , False , (220,220,220) ), 28 | Label( 'boat' , 25 , 22 , 'object' , 3 , True , False , (150, 0,255) ), 29 | Label( 'bus' , 26 , 23 , 'object' , 3 , True , False , ( 0, 60,100) ), 30 | Label( 'cctv camera' , 27 , 24 , 'object' , 3 , True , False , (222, 40, 40) ), 31 | Label( 'car' , 28 , 25 , 'object' , 3 , True , False , ( 0, 0,142) ), 32 | Label( 'caravan' , 29 , 26 , 'object' , 3 , True , False , ( 0, 0, 90) ), 33 | Label( 'catch basin' , 30 , 27 , 'object' , 3 , True , False , (220,128,128) ), 34 | Label( 'fire hydrant' , 31 , 28 , 'object' , 3 , True , False , (100,170, 30) ), 35 | Label( 'junction box' , 32 , 29 , 'object' , 3 , True , False , ( 40, 40, 40) ), 36 | Label( 'mailbox' , 33 , 30 , 'object' , 3 , True , False , ( 33, 33, 33) ), 37 | Label( 'manhole' , 34 , 31 , 'object' , 3 , True , False , (100,128,160) ), 38 | Label( 'motorcycle' , 35 , 32 , 'object' , 3 , True , False , ( 0, 0,230) ), 39 | Label( 'on rails' , 36 , 33 , 'object' , 3 , False , False , ( 0, 80,100) ), 40 | Label( 'other vehicle' , 37 , 34 , 'object' , 3 , True , False , (128, 64, 64) ), 41 | Label( 'phone booth' , 38 , 35 , 'object' , 3 , True , False , (142, 0, 0) ), 42 | Label( 'pole' , 39 , 36 , 'object' , 3 , True , False , (153,153,153) ), 43 | Label( 'pothole' , 40 , 37 , 'object' , 3 , False , False , ( 70,100,150) ), 44 | Label( 'street light' , 41 , 38 , 'object' , 3 , True , False , (210,170,100) ), 45 | Label( 'traffic light' , 42 , 39 , 'object' , 3 , True , False , (250,170, 30) ), 46 | Label( 'traffic sign (back)' , 43 , 40 , 'object' , 3 , True , False , (192,192,192) ), 47 | Label( 'traffic sign (front)' , 44 , 41 , 'object' , 3 , True , False , (220,220, 0) ), 48 | Label( 'traffic sign frame' , 45 , 42 , 'object' , 3 , True , False , (128,128,128) ), 49 | Label( 'trailer' , 46 , 43 , 'object' , 3 , True , False , ( 0, 0,110) ), 50 | Label( 'trash can' , 47 , 44 , 'object' , 3 , True , False , (140,140, 20) ), 51 | Label( 'truck' , 48 , 45 , 'object' , 3 , True , False , ( 0, 0, 70) ), 52 | Label( 'utility pole' , 49 , 46 , 'object' , 3 , True , False , ( 0, 0, 80) ), 53 | Label( 'wheeled slow' , 50 , 47 , 'object' , 3 , True , False , ( 0, 0,192) ), 54 | Label( 'mountain' , 51 , 48 , 'nature' , 4 , False , False , ( 64,170, 64) ), 55 | Label( 'sand' , 52 , 49 , 'nature' , 4 , False , False , (230,160, 50) ), 56 | Label( 'sky' , 53 , 50 , 'nature' , 4 , False , False , ( 70,130,180) ), 57 | Label( 'snow' , 54 , 51 , 'nature' , 4 , False , False , (190,255,255) ), 58 | Label( 'terrain' , 55 , 52 , 'nature' , 4 , False , False , (152,251,152) ), 59 | Label( 'vegetation' , 56 , 53 , 'nature' , 4 , False , False , (107,142, 35) ), 60 | Label( 'water' , 57 , 54 , 'nature' , 4 , False , False , ( 0,170, 30) ), 61 | Label( 'bicyclist' , 58 , 55 , 'human' , 6 , True , False , (255, 0, 0) ), 62 | Label( 'motorcyclist' , 59 , 56 , 'human' , 6 , True , False , (255, 0,100) ), 63 | Label( 'other rider' , 60 , 57 , 'human' , 6 , True , False , (255, 0,200) ), 64 | Label( 'person' , 61 , 58 , 'human' , 6 , True , False , (220, 20, 60) ), 65 | Label( 'lane marking - crosswalk' , 62 , 59 , 'marking' , 8 , True , False , (200,128,128) ), 66 | Label( 'lane marking - general' , 63 , 60 , 'marking' , 8 , False , False , (255,255,255) ), 67 | Label( 'bird' , 64 , 61 , 'animal' , 9 , True , False , (165, 42, 42) ), 68 | Label( 'ground animal' , 65 , 62 , 'animal' , 9 , True , False , ( 0,192, 0) ), 69 | ]) 70 | -------------------------------------------------------------------------------- /dataloader/pt_data_loader/README.md: -------------------------------------------------------------------------------- 1 | The code was created with Python 3.7 and is possibly not compatible to previous 2 | versions of Python at the moment. 3 | 4 | Datasets 5 | ======== 6 | 7 | To create a Dataset, use one of the classes in `specialdatasets.py`. 8 | There are classes for KITTI datasets and Cityscape datasets, both of 9 | which require that the dataset has been preprocessed by creating a 10 | JSON file containing the desired structure. At the core of the dataset 11 | is a dictionary of the form 12 | 13 | {(name1, frame_index1, resolution1): [filepath11, filepath12, ...] 14 | (name2, frame_index2, resolution2): [filepath21, filepath22, ...] 15 | ... } 16 | 17 | where `name` denotes the category like `color`, `depth` or `segmentation`. 18 | If the dataset deals only with images and not with videos, `frame_index` is 19 | always `0`. `resolution` has a standard value of `-1` that can be modified e.g. 20 | via data transforms. The values in the dictionary contain lists of image file names 21 | corresponding to the key. The actual image file is only loaded when the 22 | `__getitem()__` method of the dataset is called. 23 | 24 | There is also a `SimpleDataset` class which simply reads the data 25 | directly from the folders. However, it should be noted that as of now, a 26 | source for the labels is still needed and there is no way to 27 | specify how the depth information is stored in the dataset. To enable 28 | this simple reading mode, the parameter `simple_mode` must be set to True. 29 | 30 | In all cases, the file `get_path.py` in the `file_io` folder has to be adjusted 31 | in order to return the path to the datasets folder on the user's PC. 32 | 33 | For testing purposes, the file `specialdatasets.py` ist executable. There are 34 | several functions implemented in order to test the datasets and transforms. 35 | 36 | Necessary parameters 37 | -------------------- 38 | The following parameters must be specified when creating a new Dataset instance: 39 | 40 | `dataset:` Name of the folder in which the dataset is saved 41 | 42 | `trainvaltest_split:` Can be `'train'`, `'validation'` or `'test'` to specify what 43 | type of dataset it is 44 | 45 | 46 | Optional Parameters 47 | ------------------- 48 | `data_transforms:` Takes a list of data transforms that are to be executed each 49 | time a sample from the dataset is loaded (see next section). Default: `[mytransforms.CreateScaledImage(), 50 | mytransforms.CreateColoraug(), mytransforms.ToTensor()]`. These transforms are required 51 | in every case. If e.g. you include depth or segmentation images, you also need to add 52 | `mytransforms.ConvertDepth()` or `mytransforms.ConvertSegmentation()`, respectively. 53 | 54 | `video_mode:` can be `'mono'` or `'video'` and defines if only the images or image 55 | sequences are to be loaded 56 | 57 | `stereo_mode:` can be `'mono'` or `'stereo'` and defines if the stereo images are 58 | to be loaded 59 | 60 | `width:` defines the width at which the images are loaded 61 | 62 | `height:` defines the height at which the images are loaded 63 | 64 | `labels:` can be used if the different labels than specified in the 65 | `dataloader/file_io/dataset_index.py` are desired. It takes the labels as defined in the named tuples style in Cityscapes. 66 | Get the labels from defintions folder. 67 | 68 | `labels_mode:` can be used if the a different labels mode than specified in the 69 | `dataloader/file_io/dataset_index.py` is desired. It can be `'fromid'`, `'fromrgb'` or 70 | `'fromtrainid'` and defines if the segmentation masks are given as ID, RGB color code or trainID. If the 71 | segmentation is given color coded, it is strongly 72 | recommended to convert the labels into IDs first (do it on a GPU!) and use `'fromid'` instead. 73 | 74 | The following parameters have a standard value and therefore it is not necessary 75 | to assign them a value 76 | 77 | `simple_mode:` if `True`, the Data is read directly from a folder without using 78 | a .json file. Default: `False` 79 | 80 | `scales:` list of all scales at which the images should be loaded (list of exponents 81 | for powers of 2) 82 | 83 | `keys_to_load:` a list of all key names that are to be loaded, e.g. 84 | `['color', 'segmentation', ...]`. Default: `None` (meaning all keys). In simple mode, 85 | this parameter has to be specified! 86 | 87 | `keys_to_video:` defines for which keys the sequences are to be loaded 88 | 89 | `keys_to_stereo:` defines for which keys the stereo images are supposed to be loaded 90 | 91 | `split:` dataset split that is supposed to be loaded. Default is the complete 92 | dataset itself (`None`) 93 | 94 | `video_frames:` all frames of the sequence that are supposed to be loaded. Default: `[0, -1, 1]` 95 | 96 | `folders_to_load`: list of folders from which data should be loaded; folders not mentioned are skipped in 97 | the respective set. Filter is case insensitive. 98 | 99 | `files_to_load`: list of files that should be loaded; files not mentioned are skipped in the respective 100 | set. File names need not be complete; filter is case insensitive. 101 | 102 | `n_files`: How many files shall be loaded. Files are selected randomly if there are more files than n_files. 103 | Seeded by numpy.random.seed() 104 | 105 | Transforms 106 | ========== 107 | There are several transforms available. There are LoadTransforms 108 | 109 | LoadRGB() 110 | LoadSegmentation() 111 | LoadDepth() 112 | LoadNumerics() 113 | 114 | that are automatically performed every time a dataset is initialized. 115 | 116 | When initializing a dataset, a list of other transforms can be given in the 117 | parameter `data_transforms` e.g. like this: 118 | 119 | example_dataset = KITTIDataset(..., data_transforms = 120 | [RandomHorizontalFlip(), 121 | RandomRotate(rotation=90, fraction = 1.0), 122 | ...], ...) 123 | 124 | These are transforms that are performed every time 125 | a sample from the dataset is loaded. 126 | 127 | Converting the segmentation to trainIDs 128 | ------------------------------------- 129 | Passing ``ConvertSegmentation()`` is mandatory if segmentation images are used. However, 130 | if the standard labels for a dataset are to be used, the arguments don't have to be specified. 131 | They wil be included automatically in the constructor of the `BaseDataset` class. 132 | 133 | For the sake of computational efficiency, the conversion of the segmentation ground truths into 134 | trainIDs was moved from ``LoadSegmentation()`` to a new transform called 135 | 136 | ConvertSegmentation(labels, lables_mode, original=False) 137 | 138 | This new transform is passed via ``data_transforms`` and should be passed after the data 139 | transforms that scale the image. That way, the segmentation is only converted on the scaled image 140 | which is noticeably faster if the image size was reduced. Optionally, by passing the argument 141 | ``original=True``, the segmentation can be converted on the original image. It should also appear in the 142 | `data_transforms` list before `CreateColorAug`. 143 | 144 | Converting the depth image 145 | -------------------------- 146 | Passing ``ConvertDepth()`` is mandatory if depth images are used. You don't have to specify the `depth_mode` 147 | since it will be loaded automatically in the constructor of the `BaseDataset` class from the `parameters.json` 148 | generated by the script `dataloader/file_io/dataset_index`. 149 | 150 | Data Transforms without pre-processing 151 | -------------------------------------- 152 | All of these transforms include randomness and do therefore yield different results every time 153 | they are performed. They are 154 | 155 | RandomExchangeStereo() 156 | RandomHorizontalFlip() 157 | RandomVerticalFlip() 158 | 159 | These exchange the left and right side of a stereo image or flip the image horizontally 160 | or vertically with a probability of 0.5. 161 | 162 | Data Transforms that scale the image 163 | ------------------------------------ 164 | For all of the following transforms, the transform `CreateScaledImage()` must be performed first. 165 | 166 | RandomRotate(rotation, fraction) 167 | RandomTranslate(translation, fraction) 168 | RandomRescale(scale, fraction) 169 | 170 | These transforms again include randomness. The probability that these tranforms are performed 171 | can be adjusted with the parameter `fraction`. 172 | 173 | Resize(output_size) 174 | MultiResize(scales) 175 | 176 | These transforms resize the whole image. 177 | 178 | RandomCrop(output_size) 179 | CenterCrop(output_size) 180 | SidesCrop(hw, tl) 181 | 182 | These transforms crop the image either randomly, centered or with a given offset to the side. 183 | 184 | Data Transforms that augment the image 185 | -------------------------------------- 186 | For all of the following transforms, the transform `CreateColorAug()` must be performed first. 187 | 188 | ColorJitter(brightness, contrast, saturation, hue, gamma, fraction) 189 | GaussianBlurr(fraction, max_rad) 190 | 191 | -------------------------------------------------------------------------------- /dataloader/file_io/dir_lister.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | import dataloader.file_io.get_path as gp 5 | 6 | 7 | class DirLister: 8 | """ This class will provide methods that enable the creation 9 | file lists and may return them in desired formats""" 10 | def __init__(self): 11 | pass 12 | 13 | @staticmethod 14 | def check_formats(cur_dir=None, file_ending=None): 15 | """ method to check if specified parameters have the right format 16 | 17 | :param cur_dir: directory which is checked for existance 18 | :param file_ending: file ending that is checked for the right format 19 | """ 20 | check = True 21 | if cur_dir is not None: 22 | if os.path.isdir(cur_dir) is False: 23 | print("the specified directory does not exist") 24 | check = False 25 | if file_ending is not None: 26 | if file_ending[0] != '.': 27 | print("the file ending has no '.' at the beginning") 28 | check = False 29 | return check 30 | 31 | @staticmethod 32 | def list_subdirectories(top_dir): 33 | """ method that lists all subdirectories of a given directory 34 | 35 | :param top_dir: directory in which the subdirectories are searched in 36 | """ 37 | top_dir = os.path.abspath(top_dir) 38 | sub_dirs = [os.path.join(top_dir, x) for x in os.listdir(top_dir) 39 | if os.path.isdir(os.path.join(top_dir, x))] 40 | return sub_dirs 41 | 42 | @staticmethod 43 | def list_files_in_directory(top_dir): 44 | """ method that lists all files of a given directory 45 | 46 | :param top_dir: directory in which the files are searched in 47 | """ 48 | top_dir = os.path.abspath(top_dir) 49 | files = [os.path.join(top_dir, x) for x in os.listdir(top_dir) 50 | if os.path.isfile(os.path.join(top_dir, x))] 51 | return files 52 | 53 | @staticmethod 54 | def get_directories(parent_dir): 55 | """ method that lists all directories of a given directory recursively 56 | 57 | :param parent_dir: directory in which the subdirectories are searched in 58 | """ 59 | if DirLister.check_formats(cur_dir=parent_dir) is False: 60 | sys.exit("Inputparameter überprüfen") 61 | parent_dir = os.path.abspath(parent_dir) 62 | sub_dirs = [] 63 | still_to_search = DirLister.list_subdirectories(parent_dir) 64 | 65 | while len(still_to_search) > 0: 66 | curr_sub_dirs = DirLister.list_subdirectories(still_to_search[0]) 67 | if len(curr_sub_dirs) == 0: 68 | sub_dirs.append(still_to_search[0]) 69 | else: 70 | still_to_search.extend(curr_sub_dirs) 71 | still_to_search.remove(still_to_search[0]) 72 | 73 | return sub_dirs 74 | 75 | @staticmethod 76 | def include_files_by_name(file_list, names, positions): 77 | """ takes a list of filepaths and keeps only the files which have all strings 78 | inside of their path specified by the list names 79 | 80 | :param file_list: list of filepaths 81 | :param names: strings which have to be inside the directory name 82 | :param positions: positions inside the dataset which are also only kept if the element is kept 83 | """ 84 | if type(names) == list: 85 | for name in names: 86 | positions = [positions[i] for i in range(len(file_list)) if name in file_list[i]] 87 | file_list = [x for x in file_list if name in x] 88 | elif type(names) == str: 89 | name = names 90 | positions = [positions[i] for i in range(len(file_list)) if name in file_list[i]] 91 | file_list = [x for x in file_list if name in x] 92 | return file_list, positions 93 | 94 | @staticmethod 95 | def include_files_by_folder(file_list, names, positions): 96 | """ takes a list of filepaths and keeps only the files which have all strings 97 | inside of their path specified by the list names 98 | 99 | :param file_list: list of filepaths 100 | :param names: folders which have to be inside the directory path 101 | :param positions: positions inside the dataset which are also only kept if the element is kept 102 | """ 103 | if type(names) == list: 104 | for name in names: 105 | positions = [positions[i] for i in range(len(file_list)) if name in file_list[i]] 106 | file_list = [x for x in file_list if name + os.sep in x or name == os.path.split(x)[1]] 107 | elif type(names) == str: 108 | name = names 109 | positions = [positions[i] for i in range(len(file_list)) if name in file_list[i]] 110 | file_list = [x for x in file_list if name + os.sep in x or name == os.path.split(x)[1]] 111 | return file_list, positions 112 | 113 | @staticmethod 114 | def include_dirs_by_name(dir_list, names, ignore=(), ambiguous_names_to_ignore=()): 115 | """ takes a list of directories and includes the directories which have all strings 116 | of the ones specified by the list names 117 | 118 | :param dir_list: list of directories 119 | :param names: strings which have to be inside the directory name 120 | :param ignore: string that must not be inside the directory name 121 | :param ambiguous_names_to_ignore: A list containing all strings that should not be taken into account when 122 | comparing to names. For example, if an upper folder is called 'dataset_images' and one filter name 123 | is also 'images' (e.g. for the color image), then this parameter will prevent all folder from being 124 | returned 125 | :return: a list of all folders containing all names, excluding those containing a string in ignore 126 | """ 127 | shortened_dir_list = dir_list.copy() 128 | if type(ambiguous_names_to_ignore) == str: 129 | ambiguous_names_to_ignore = (ambiguous_names_to_ignore, ) 130 | for ambiguous_name in ambiguous_names_to_ignore: 131 | shortened_dir_list = [x.replace(ambiguous_name, '') for x in shortened_dir_list] 132 | if type(names) == list: 133 | for name in names: 134 | dir_list = [x for x, xs in zip(dir_list, shortened_dir_list) if name in xs] 135 | shortened_dir_list = [xs for xs in shortened_dir_list if name in xs] 136 | elif type(names) == str: 137 | name = names 138 | dir_list = [x for x, xs in zip(dir_list, shortened_dir_list) if name in xs] 139 | for ignore_string in ignore: 140 | dir_list = [x for x in dir_list if ignore_string not in x] 141 | return dir_list 142 | 143 | @staticmethod 144 | def include_dirs_by_folder(dir_list, names): 145 | """ takes a list of directories and includes the directories which have all strings 146 | of the ones specified by the list names 147 | 148 | :param dir_list: list of directories 149 | :param names: folders which have to be inside the directory path 150 | """ 151 | if type(names) == list: 152 | for name in names: 153 | dir_list = [x for x in dir_list if name + os.sep in x or name == os.path.split(x)[1]] 154 | elif type(names) == str: 155 | name = names 156 | dir_list = [x for x in dir_list if name + os.sep in x or name == os.path.split(x)[1]] 157 | return dir_list 158 | 159 | @staticmethod 160 | def remove_dirs_by_name(dir_list, names): 161 | """ takes a list of directories and removes the directories which have at least one string 162 | of the ones specified by the list names 163 | 164 | :param dir_list: list of directories 165 | :param names: strings which are not allowed inside the directory name 166 | """ 167 | if type(names) == list: 168 | for name in names: 169 | dir_list = [x for x in dir_list if name not in x] 170 | elif type(names) == str: 171 | name = names 172 | dir_list = [x for x in dir_list if name not in x] 173 | return dir_list 174 | 175 | @staticmethod 176 | def get_files_by_ending(cur_dir, file_ending, ignore = []): 177 | """ returns all files inside a directory which have a certain ending 178 | 179 | :param cur_dir: list of directories 180 | :param file_ending: all files with the specified file_ending are returned 181 | :param ignore: list of strings. Filenames containing one of these strings will be ignored. 182 | :return: all files inside cur_dir which have the ending file_ending 183 | """ 184 | if DirLister.check_formats(cur_dir=cur_dir, 185 | file_ending=file_ending) is False: 186 | sys.exit("Inputparameter überprüfen") 187 | files = DirLister.list_files_in_directory(cur_dir) 188 | len_ending = len(file_ending) 189 | files = [x for x in files if x[-len_ending:] == file_ending] 190 | for ignore_string in ignore: 191 | files = [x for x in files if ignore_string not in x] 192 | return files 193 | 194 | 195 | if __name__ == '__main__': 196 | """can be used for testing purposes""" 197 | path_getter = gp.GetPath() 198 | path = path_getter.get_data_path() 199 | path = os.path.join(path, 'Cityscapes') 200 | a = DirLister() 201 | test = a.get_directories(path) 202 | print(a.include_dirs_by_name(test, 'test')) 203 | -------------------------------------------------------------------------------- /dataloader/eval/metrics.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import warnings 3 | 4 | 5 | # from https://github.com/tinghuiz/SfMLearner 6 | def dump_xyz(source_to_target_transformations): 7 | xyzs = [] 8 | cam_to_world = np.eye(4) 9 | xyzs.append(cam_to_world[:3, 3]) 10 | for source_to_target_transformation in source_to_target_transformations: 11 | cam_to_world = np.dot(cam_to_world, source_to_target_transformation) 12 | xyzs.append(cam_to_world[:3, 3]) 13 | return xyzs 14 | 15 | 16 | # from https://github.com/tinghuiz/SfMLearner 17 | def compute_ate(gtruth_xyz, pred_xyz_o): 18 | 19 | # Make sure that the first matched frames align (no need for rotational alignment as 20 | # all the predicted/ground-truth snippets have been converted to use the same coordinate 21 | # system with the first frame of the snippet being the origin). 22 | offset = gtruth_xyz[0] - pred_xyz_o[0] 23 | pred_xyz = pred_xyz_o + offset[None, :] 24 | 25 | # Optimize the scaling factor 26 | scale = np.sum(gtruth_xyz * pred_xyz) / np.sum(pred_xyz ** 2) 27 | alignment_error = pred_xyz * scale - gtruth_xyz 28 | rmse = np.sqrt(np.sum(alignment_error ** 2)) / gtruth_xyz.shape[0] 29 | return rmse 30 | 31 | 32 | class Evaluator(object): 33 | # CONF MATRIX 34 | # 0 1 2 (PRED) 35 | # 0 |TP FN FN| 36 | # 1 |FP TP FN| 37 | # 2 |FP FP TP| 38 | # (GT) 39 | # -> rows (axis=1) are FN 40 | # -> columns (axis=0) are FP 41 | @staticmethod 42 | def iou(conf): # TP / (TP + FN + FP) 43 | with warnings.catch_warnings(): 44 | warnings.filterwarnings('ignore') 45 | iu = np.diag(conf) / (conf.sum(axis=1) + conf.sum(axis=0) - np.diag(conf)) 46 | meaniu = np.nanmean(iu) 47 | result = {'iou': dict(zip(range(len(iu)), iu)), 'meaniou': meaniu} 48 | return result 49 | 50 | @staticmethod 51 | def accuracy(conf): # TP / (TP + FN) aka 'Recall' 52 | # Add 'add' in order to avoid division by zero and consequently NaNs in iu 53 | with warnings.catch_warnings(): 54 | warnings.filterwarnings('ignore') 55 | totalacc = np.diag(conf).sum() / (conf.sum()) 56 | acc = np.diag(conf) / (conf.sum(axis=1)) 57 | meanacc = np.nanmean(acc) 58 | result = {'totalacc': totalacc, 'meanacc': meanacc, 'acc': acc} 59 | return result 60 | 61 | @staticmethod 62 | def precision(conf): # TP / (TP + FP) 63 | # Add 'add' in order to avoid division by zero and consequently NaNs in iu 64 | with warnings.catch_warnings(): 65 | warnings.filterwarnings('ignore') 66 | prec = np.diag(conf) / (conf.sum(axis=0)) 67 | meanprec = np.nanmean(prec) 68 | result = {'meanprec': meanprec, 'prec': prec} 69 | return result 70 | 71 | @staticmethod 72 | def freqwacc(conf): 73 | # Add 'add' in order to avoid division by zero and consequently NaNs in iu 74 | with warnings.catch_warnings(): 75 | warnings.filterwarnings('ignore') 76 | iu = np.diag(conf) / (conf.sum(axis=1) + conf.sum(axis=0) - np.diag(conf)) 77 | freq = conf.sum(axis=1) / (conf.sum()) 78 | fwavacc = (freq[freq > 0] * iu[freq > 0]).sum() 79 | result = {'freqwacc': fwavacc} 80 | return result 81 | 82 | @staticmethod 83 | def depththresh(gt, pred): 84 | thresh = np.maximum((gt / pred), (pred / gt)) 85 | a1 = (thresh < 1.25).mean() 86 | a2 = (thresh < 1.25 ** 2).mean() 87 | a3 = (thresh < 1.25 ** 3).mean() 88 | 89 | result = {'delta1': a1, 'delta2': a2, 'delta3': a3} 90 | return result 91 | 92 | @staticmethod 93 | def deptherror(gt, pred): 94 | rmse = (gt - pred) ** 2 95 | rmse = np.sqrt(rmse.mean()) 96 | rmse_log = (np.log(gt) - np.log(pred)) ** 2 97 | rmse_log = np.sqrt(rmse_log.mean()) 98 | abs_rel = np.mean(np.abs(gt - pred) / gt) 99 | sq_rel = np.mean(((gt - pred) ** 2) / gt) 100 | 101 | result = {'abs_rel': abs_rel, 'sq_rel': sq_rel, 'rmse': rmse, 'rmse_log': rmse_log} 102 | return result 103 | 104 | 105 | class SegmentationRunningScore(object): 106 | def __init__(self, n_classes): 107 | self.n_classes = n_classes 108 | self.confusion_matrix = np.zeros((n_classes, n_classes)) 109 | 110 | def _fast_hist(self, label_true, label_pred, n_class): 111 | mask_true = (label_true >= 0) & (label_true < n_class) 112 | mask_pred = (label_pred >= 0) & (label_pred < n_class) 113 | mask = mask_pred & mask_true 114 | label_true = label_true[mask].astype(np.int) 115 | label_pred = label_pred[mask].astype(np.int) 116 | hist = np.bincount(n_class * label_true + label_pred, 117 | minlength=n_class*n_class).reshape(n_class, n_class).astype(np.float) 118 | return hist 119 | 120 | def update(self, label_trues, label_preds): 121 | for lt, lp in zip(label_trues, label_preds): 122 | self.confusion_matrix += self._fast_hist(lt.flatten(), lp.flatten(), self.n_classes) 123 | 124 | def get_scores(self, listofparams=None): 125 | """Returns the evaluation params specified in the list""" 126 | possibleparams = { 127 | 'iou': Evaluator.iou, 128 | 'acc': Evaluator.accuracy, 129 | 'freqwacc': Evaluator.freqwacc, 130 | 'prec': Evaluator.precision 131 | } 132 | if listofparams is None: 133 | listofparams = possibleparams 134 | 135 | result = {} 136 | for param in listofparams: 137 | if param in possibleparams.keys(): 138 | result.update(possibleparams[param](self.confusion_matrix)) 139 | return result 140 | 141 | def reset(self): 142 | self.confusion_matrix = np.zeros((self.n_classes, self.n_classes)) 143 | 144 | 145 | class DepthRunningScore(object): 146 | def __init__(self): 147 | self.num_samples = 0 148 | self.depth_thresh = {'delta1': 0, 'delta2': 0, 'delta3': 0} 149 | self.depth_errors = {'abs_rel': 0, 'sq_rel': 0, 'rmse': 0, 'rmse_log': 0} 150 | 151 | def update(self, ground_truth, prediction): 152 | if isinstance(ground_truth, list): 153 | self.num_samples += len(ground_truth) 154 | else: 155 | ground_truth = [ground_truth] 156 | prediction = [prediction] 157 | self.num_samples += 1 158 | 159 | for k in range(len(ground_truth)): 160 | gt = ground_truth[k].astype(np.float) 161 | pred = prediction[k].astype(np.float) 162 | thresh = Evaluator.depththresh(gt, pred) 163 | error = Evaluator.deptherror(gt, pred) 164 | for i, j in zip(thresh.keys(), self.depth_thresh.keys()): 165 | self.depth_thresh[i] += thresh[j] 166 | for i, j in zip(error.keys(), self.depth_errors.keys()): 167 | self.depth_errors[i] += error[j] 168 | 169 | def get_scores(self, listofparams=None): 170 | """Returns the evaluation params specified in the list""" 171 | possibleparams = { 172 | 'thresh': self.depth_thresh, 173 | 'error': self.depth_errors, 174 | } 175 | if listofparams is None: 176 | listofparams = possibleparams 177 | 178 | result = {} 179 | for param in listofparams: 180 | if param in possibleparams.keys(): 181 | result.update(possibleparams[param]) 182 | for i in result.keys(): 183 | result[i] = result[i]/self.num_samples 184 | 185 | return result 186 | 187 | def reset(self): 188 | self.num_samples = 0 189 | self.depth_thresh = {'delta1': 0, 'delta2': 0, 'delta3': 0} 190 | self.depth_errors = {'abs_rel': 0, 'sq_rel': 0, 'rmse': 0, 'rmse_log': 0} 191 | 192 | 193 | class PoseRunningScore(object): 194 | def __init__(self): 195 | self.preds = list() 196 | self.gts = list() 197 | 198 | def update(self, ground_truth, prediction): 199 | if isinstance(ground_truth, list): 200 | self.gts += ground_truth 201 | else: 202 | self.gts += [ground_truth] 203 | 204 | if isinstance(prediction, list): 205 | self.preds += prediction 206 | else: 207 | self.preds += [prediction] 208 | 209 | def get_scores(self): 210 | """Returns the evaluation params specified in the list""" 211 | 212 | gt_global_poses = np.concatenate(self.gts) 213 | pred_poses = np.concatenate(self.preds) 214 | 215 | gt_global_poses = np.concatenate( 216 | (gt_global_poses, np.zeros((gt_global_poses.shape[0], 1, 4))), 1) 217 | gt_global_poses[:, 3, 3] = 1 218 | gt_xyzs = gt_global_poses[:, :3, 3] 219 | gt_local_poses = [] 220 | for i in range(1, len(gt_global_poses)): 221 | gt_local_poses.append( 222 | np.linalg.inv(np.dot(np.linalg.inv(gt_global_poses[i - 1]), gt_global_poses[i]))) 223 | ates = [] 224 | num_frames = gt_xyzs.shape[0] 225 | track_length = 5 226 | for i in range(0, num_frames - track_length + 1): 227 | local_xyzs = np.array(dump_xyz(pred_poses[i:i + track_length - 1])) 228 | gt_local_xyzs = np.array(dump_xyz(gt_local_poses[i:i + track_length - 1])) 229 | ates.append(compute_ate(gt_local_xyzs, local_xyzs)) 230 | 231 | pose_error = {'mean': np.mean(ates), 'std': np.std(ates)} 232 | return pose_error 233 | 234 | def reset(self): 235 | self.preds = list() 236 | self.gts = list() 237 | 238 | 239 | class AverageMeter(object): 240 | """Computes and stores the average and current value""" 241 | 242 | def __init__(self): 243 | self.reset() 244 | 245 | def reset(self): 246 | self.val = 0 247 | self.avg = 0 248 | self.sum = 0 249 | self.count = 0 250 | 251 | def update(self, val, n=1): 252 | self.val = val 253 | self.sum += val * n 254 | self.count += n 255 | self.avg = self.sum / self.count 256 | -------------------------------------------------------------------------------- /dataloader/file_io/README.md: -------------------------------------------------------------------------------- 1 | The code was created with Python 3.7 and is possibly not compatible to previous 2 | versions of Python at the moment. 3 | 4 | This readme file is still under construction. 5 | 6 | Basic Workflow 7 | ============== 8 | If you want to create the needed json-files for a dataset, do the following: 9 | 1. Execute `filelist_creator.py` with the desired dataset sepcified in the code's `main` part. 10 | This creates a `basic_files.json` file in the dataset folder. 11 | 2. Execute `split_creator.py` with the desired dataset sepcified in the code's `main` part. 12 | This creates a `train.json`, `validation.json` and `test.json` file in the dataset folder. 13 | 3. Execute the `dataset_index.py` with the dataset name as an input parameter. 14 | 15 | For some datasets, some preprocessing scripts have to be performed first. These can be found in 16 | the `dataloader/data_preprocessing` folder. 17 | 18 | If you want to create a scaled version of an existing dataset, execute `dataset_scaler.py` 19 | with the desired dataset and output size/scale factor in the code's main part. 20 | 21 | If you want to convert the segmentation images of a dataset to image consisting of the train_ids, 22 | use the `trainid_converter.py`. Note that an execution of the `filelist_creator.py` afterwards will 23 | remove the new entries from the json files. 24 | 25 | Filelist Creator 26 | ================ 27 | There are specific FilelistCreator classes for every supported Dataset. They are designed 28 | s.t. they understand the specific structure of the dataset 29 | 30 | Dataset Creator 31 | --------------- 32 | data_creator = DatasetCreator(dataset, rewrite) 33 | The `DatasetCreator` class is the class that is used in the main method to include the dataset. 34 | It contains a list of all supported datasets and checks whether the `dataset` that was given 35 | is supported. If yes, and when the method 36 | 37 | data_creator.create_dataset() 38 | is called, the DatasetCretor calls its method corresponding to the given dataset, e.g. `self.create_kittilists()` 39 | for the KITTI dataset. 40 | 41 | The `create_dataset()` method should only be used if the `check_state()` method returns `True`, which is the case 42 | if there is no valid `basic_files.json` in the dataset folder or if the parameter `rewrite` is 43 | set to `True`, in which case a valid `basic_files.json` will be overwritten. 44 | 45 | Structure of the `basic_files.json` 46 | ----------------------------------- 47 | The `basic_files.json` contains a dictionary with 7 entries. The following example shows what this dictionary might 48 | look like for a specific dataset. 49 | 50 | { "names": ["color", "color_right", "depth", "depth_right", "segmentation", ...], 51 | "types": [".png", ".png", ".png", ...], 52 | "filters:" ["image", "image_right", "depth", "depth_right", "semantics", ...], 53 | "folders": [["folder_path_1a", "folder_path_1b, ...], ["folder_path_2a", ...]], 54 | "files": [["file_path_1a", "file_path_1b, ...], ["file_path_2a", ...]], 55 | "positions": [(0,0,499,0), (1,1,498,1), ...], [(0,0,499,0), (1,1,498,1), ...], ...], 56 | "numerical_values": None, None, None, None, None, [0.0, 0.058810459, ...], ...]} 57 | 58 | The keys are always the same. The value of each key is a list as shown above. The number of list 59 | entries can vary depending on the dataset. However, it is important that each list contains the same 60 | number of entries. The first entry in "names" corresponds the first entry in the other lists, and so on. 61 | For example, if the first entry in `"names"` is `"color"`, then the first entry in `"types"` will define 62 | in which format the color images are stored, the first entry in `"folders"` lists all folders containing 63 | color images, and so on. The meaning of the seven keys are further explained in the following subsections. 64 | 65 | ### `names` 66 | Names of the data category. This can be 67 | 68 | * `"color"`: A color image from the left/right camera 69 | * `"depth"`: A depth image from the left/right camera, usually stored as a `uint_16` 70 | * `"segmenation"`: Segmentation images. Can have different formats, e.g. stored by IDs or 71 | by a color mapping 72 | * `"camera_intrinsics"`: The matrix with the intrinsic camera parameters 73 | * Other possible names, depending on the dataset, are `"timestamp"`, `"velocity"`, `"poses"`, ... 74 | 75 | The above list is not exhaustive and the parameters can e.g. be modified by a succeeding 76 | `_right` to indicate the same data category but belonging to the right camera in a stereo dataset. 77 | 78 | ### `types` 79 | File type of the corresponding name, e.g. `".jpg"`, `".png"` for images or `".txt"` for 80 | textual data 81 | 82 | ### `filters` 83 | List of strings by which the folders are filtered in order to map them to the corresponding name 84 | 85 | ### `folders`, `files` 86 | List containing all folders/files in the dataset that belong to the corresponding 87 | `name`. The mapping of folders to the `name` is done by the `FilelistCreator` using the 88 | `filters`. 89 | 90 | ### `positions` 91 | `positions` contains 4-tuples, where the single entries have the following meaning: 92 | 1. global position inside the dataset (sorted by frame number and sequence) 93 | 2. number of preceding frames in the sequence 94 | 3. number of frames in the sequence after the current frame 95 | 4. local position inside the list of the elements (e.g. depth has 20000 elements but color has 40000 96 | then the first entry will contain the mapping from depth to color and the fourth entry will contain 97 | numbers from 0 to 20000) 98 | 99 | ### `numerical_values` 100 | If the datatype of `name` is not an image but numerical, e.g. `"velocity"` or 101 | `"camera_intrinsics"`, these numerical values are stored here as a list, each entry corresponding 102 | to one file. If the data type is not numerical, the respecive entry in `numerical_values` is `None`. 103 | 104 | Dataset Index 105 | ============= 106 | There are several parameters that vary from dataset to dataset. This includes the format in which depth 107 | and segmentation images are stored or camera matrices. It is necessary to create an entry in the dictionary 108 | for each new dataset. 109 | 110 | Dataset Scaler 111 | ============== 112 | The `DatasetScaler` creates a scaled version of an existing dataset. The dataset must have a `basic_files.json`. 113 | To scale a dataset, create a `DatasetScaler` object with the unscaled dataset as a parameter 114 | 115 | scaler = DatasetScaler('unscaled_dataset') 116 | 117 | To create the scaled dataset, call 118 | 119 | scaler.process(self, new_dataset_name, output_size, scale_factor, keys_to_convert, splits_to_adapt) 120 | 121 | The `new_dataset_name` must be specified and the desired `output_size` as a tuple (height, width) or a 122 | `scale_factor` by which both dimensions will be scaled uniformly. You must not use both parameters. 123 | 124 | The optional parameter `keys_to_convert` specifies which images types will be converted. If e.g. 125 | only color and segmentation images are needed but no depth images, use `keys_to_convert = 126 | ('color', 'segmentation')`. Note that you always need to include the 'color' key as this key is used to 127 | determine the native shape of the image. Note using this key will most definitely result in an error 128 | message. If this parameter is not used, all keys will be converted. 129 | 130 | The optional parameter `splits_to_adapt` can contain a tuple of split folders. The split files in these 131 | folders will be copied into a new folder with a name that corresponds to the name of the scaled dataset folder. 132 | 133 | All json-files from the old dataset folder will be copied into the folder of the new dataset s.t. the 134 | new dataset can be used exactly like the old dataset. If camera intrinsics are stored in the json files, 135 | they will be adapted to match the new image size. If the parameter `splits_to_adapt` is used, the same 136 | holds for the json-files in the new split folders. 137 | 138 | It is also possible to adapt a split folder to a scaled dataset after the scaled dataset was created. 139 | Create a new `DatasetScaler` object with the unscaled dataset name as a parameter. Then, use 140 | 141 | scaler.adapt_splits(self, scaled_dataset_name, split_names) 142 | 143 | The parameter `scaled_dataset_name` must refer to an already existing scaled dataset folder. `split names` 144 | is a tuple of split names like in the `process()` method. 145 | 146 | Note that it is not possible in general to execute the filelist_creator on a scaled dataset, since 147 | only image files are copied into the new folder, but no textual data like txt-files. 148 | 149 | Train-ID Converter 150 | ================== 151 | The `TrainIDConverter` takes all segmentation images from a dataset and performs the 152 | `ConvertSegmentation` transform which results in a segmentation image consisting of train_ids. 153 | These images are saved into a new folder called `segmentation_trainid` and added to all existing 154 | json files as a new entry. It is a requirement that the `basic_files.json` has already been 155 | generated. 156 | 157 | converter = TrainIDConverter('dataset') 158 | 159 | To create the new folder called `segmentation_trainid` with the trainid-images and 160 | add these images as new entries to the json files, call 161 | 162 | converter.process(splits_to_adapt) 163 | 164 | where `splits_to_adapt` is an optional parameter that can specify split names from splits 165 | in sperate folders. The new entries will also be added to their json files. 166 | 167 | The json files are adapted in the following way: Since for each segmentation image a new 168 | `trainid` image is created, each segmentation entry will be duplicated and the 169 | new entry name gets the suffix `_trainid`. The other lists in the 170 | dictionaries are simply copied ot modified accordingly. If for example the `basic_files.json` 171 | originally contains (`files`, `positions` and `numerical values` are omitted for reasons of clarity) 172 | 173 | { "names": [..., "segmentation", "segmentation_right" ...], 174 | "types": [..., ".png", ".png", ...], 175 | "filters:" [..., ["semantics"], ["semantics_right"], ...], 176 | "folders": [..., ["seg_path_l_1", "seg_path_l_2"], ["seg_path_r_1", "seg_path_r_2"], ...], 177 | ...} 178 | 179 | then the new `basic_files.json` will contain 180 | 181 | { "names": [..., "segmentation", "segmentation_right", 182 | "segmentation_trainid", "segmentation_right_trainid", ...], 183 | "types": [..., ".png", ".png", ".png", ".png", ...], 184 | "filters:" [..., ["semantics"], ["semantics_right"], 185 | ["segmentation_trainid", "semantics"], ["segmentation_trainid", "semantics_right"],...], 186 | "folders": [..., ["seg_path_l_1", "seg_path_l_2"], ["seg_path_r_1", "seg_path_r_2"], 187 | ["segmentation_trainid/seg_path_l_1", "segmentation_trainid/seg_path_l_2"], 188 | ["segmentation_trainid/seg_path_r_1", "segmentation_trainid/seg_path_r_2"], ...], 189 | ...} 190 | 191 | IMPORTANT NOTE: If the `filelist_creator.py` is executed on this dataset after the trainid_converter 192 | has been executed, it will create a new `basic_files.json` without the `_trainid` entries. This can occur e.g. 193 | if the `basic_files.json` is created at first without depth information, then the `trainid_converter`ist 194 | executed and at some point, someone decides to include the depth information and executes the `filelist_creator.py` 195 | again. In this case, it is not necessary to convert all segmentation images to train_ids again. The method 196 | 197 | converter.adapt_json_files(splits_to_adapt) 198 | 199 | will add the `_trainid` entries to the json files if a `segmentation_trainid` folder exists. 200 | It will, however, not check if there is still a one-to-one mapping from the `segmentation` data to 201 | the data in the `segmentation_trainid` folder. Thus, if the `filelist_creator` has modified the `segmentation` 202 | entries in some form, you must not use this function. The parameter `splits_to_adapt` has the same meaning 203 | as in `converter.process(splits_to_adapt)` -------------------------------------------------------------------------------- /dataloader/data_preprocessing/KITTI/download_kitti.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import wget 4 | import zipfile 5 | import pandas as pd 6 | import shutil 7 | import numpy as np 8 | import glob 9 | import cv2 10 | 11 | sys.path.append("../../../") 12 | import dataloader.file_io.get_path as gp 13 | import dataloader.file_io.dir_lister as dl 14 | from dataloader.data_preprocessing.KITTI.kitti_utils import pcl_to_depth_map 15 | 16 | 17 | def download_kitti_all(kitti_folder='kitti_download'): 18 | """ This pathon-script downloads all KITTI folders and aranges them in a 19 | coherent data structure which can respectively be used by the other data 20 | scripts. It is recommended to keep the standard name KITTI. Note that the 21 | path is determined automatically inside of file_io/get_path.py 22 | 23 | parameters: 24 | - kitti_folder: Name of the folder in which the dataset should be downloaded 25 | This is no path but just a name. the path ios determined by 26 | get_path.py 27 | 28 | """ 29 | 30 | # Download the standard KITTI Raw data 31 | 32 | path_getter = gp.GetPath() 33 | dataset_folder_path = path_getter.get_data_path() 34 | assert os.path.isdir(dataset_folder_path), 'Path to dataset folder does not exist' 35 | 36 | kitti_path = os.path.join(dataset_folder_path, kitti_folder) 37 | kitti_raw_data = pd.read_csv('kitti_archives_to_download.txt', 38 | header=None, delimiter=' ')[0].values 39 | kitti_path_raw = os.path.join(kitti_path, 'Raw_data') 40 | if not os.path.isdir(kitti_path_raw): 41 | os.makedirs(kitti_path_raw) 42 | for url in kitti_raw_data: 43 | folder = os.path.split(url)[1] 44 | folder = os.path.join(kitti_path_raw, folder) 45 | folder = folder[:-4] 46 | wget.download(url, out=kitti_path_raw) 47 | unzipper = zipfile.ZipFile(folder + '.zip', 'r') 48 | unzipper.extractall(kitti_path_raw) 49 | unzipper.close() 50 | os.remove(folder + '.zip') 51 | 52 | kitti_dirs_days = os.listdir(kitti_path_raw) 53 | 54 | # Get ground truth depths 55 | 56 | kitti_path_depth_annotated = os.path.join(kitti_path, 'Depth_improved') 57 | if not os.path.isdir(kitti_path_depth_annotated): 58 | os.makedirs(kitti_path_depth_annotated) 59 | url_depth_annotated = 'https://s3.eu-central-1.amazonaws.com/avg-kitti/data_depth_annotated.zip' 60 | wget.download(url_depth_annotated, out=kitti_path_depth_annotated) 61 | depth_zipped = os.path.join(kitti_path_depth_annotated, os.path.split(url_depth_annotated)[1]) 62 | unzipper = zipfile.ZipFile(depth_zipped, 'r') 63 | unzipper.extractall(kitti_path_depth_annotated) 64 | unzipper.close() 65 | os.remove(depth_zipped) 66 | 67 | trainval_folder = os.listdir(kitti_path_depth_annotated) 68 | kitti_drives_list = [] 69 | for sub_folder in trainval_folder: 70 | sub_folder = os.path.join(kitti_path_depth_annotated, sub_folder) 71 | kitti_drives_list.extend([os.path.join(sub_folder, i) for i in os.listdir(sub_folder)]) 72 | 73 | for sub_folder in kitti_dirs_days: 74 | sub_folder = os.path.join(kitti_path_depth_annotated, sub_folder) 75 | if not os.path.isdir(sub_folder): 76 | os.makedirs(sub_folder) 77 | for drive in kitti_drives_list: 78 | if os.path.split(sub_folder)[1] in drive: 79 | shutil.move(drive, sub_folder) 80 | 81 | for sub_folder in trainval_folder: 82 | sub_folder = os.path.join(kitti_path_depth_annotated, sub_folder) 83 | shutil.rmtree(sub_folder) 84 | 85 | # Get sparse depths 86 | 87 | kitti_path_depth_sparse = os.path.join(kitti_path, 'Depth_projected') 88 | if not os.path.isdir(kitti_path_depth_sparse): 89 | os.makedirs(kitti_path_depth_sparse) 90 | url_depth_sparse = 'https://s3.eu-central-1.amazonaws.com/avg-kitti/data_depth_velodyne.zip' 91 | wget.download(url_depth_sparse, out=kitti_path_depth_sparse) 92 | depth_zipped = os.path.join(kitti_path_depth_sparse, os.path.split(url_depth_sparse)[1]) 93 | unzipper = zipfile.ZipFile(depth_zipped, 'r') 94 | unzipper.extractall(kitti_path_depth_sparse) 95 | unzipper.close() 96 | os.remove(depth_zipped) 97 | 98 | trainval_folder = os.listdir(kitti_path_depth_sparse) 99 | kitti_drives_list = [] 100 | for sub_folder in trainval_folder: 101 | sub_folder = os.path.join(kitti_path_depth_sparse, sub_folder) 102 | kitti_drives_list.extend([os.path.join(sub_folder, i) for i in os.listdir(sub_folder)]) 103 | 104 | for sub_folder in kitti_dirs_days: 105 | sub_folder = os.path.join(kitti_path_depth_sparse, sub_folder) 106 | if not os.path.isdir(sub_folder): 107 | os.makedirs(sub_folder) 108 | for drive in kitti_drives_list: 109 | if os.path.split(sub_folder)[1] in drive: 110 | shutil.move(drive, sub_folder) 111 | 112 | for sub_folder in trainval_folder: 113 | sub_folder = os.path.join(kitti_path_depth_sparse, sub_folder) 114 | shutil.rmtree(sub_folder) 115 | 116 | # download test_files and integrate them into the folder structure 117 | 118 | url_depth_testset = 'https://s3.eu-central-1.amazonaws.com/avg-kitti/data_depth_selection.zip' 119 | wget.download(url_depth_testset, out=kitti_path) 120 | depth_zipped = os.path.join(kitti_path, os.path.split(url_depth_testset)[1]) 121 | unzipper = zipfile.ZipFile(depth_zipped, 'r') 122 | unzipper.extractall(kitti_path) 123 | unzipper.close() 124 | os.remove(depth_zipped) 125 | 126 | init_depth_completion_folder = os.path.join(kitti_path, 'depth_selection', 127 | 'test_depth_completion_anonymous', 'image') 128 | target_depth_completion_folder = os.path.join(kitti_path_raw, 'test_depth_completion', 'image_02') 129 | if not os.path.isdir(target_depth_completion_folder): 130 | os.makedirs(target_depth_completion_folder) 131 | shutil.move(init_depth_completion_folder, target_depth_completion_folder) 132 | os.rename(os.path.join(target_depth_completion_folder, os.path.split(init_depth_completion_folder)[1]), 133 | os.path.join(target_depth_completion_folder, 'data')) 134 | 135 | init_depth_completion_folder = os.path.join(kitti_path, 'depth_selection', 136 | 'test_depth_completion_anonymous', 'intrinsics') 137 | target_depth_completion_folder = os.path.join(kitti_path_raw, 'test_depth_completion') 138 | shutil.move(init_depth_completion_folder, target_depth_completion_folder) 139 | 140 | init_depth_completion_folder = os.path.join(kitti_path, 'depth_selection', 141 | 'test_depth_completion_anonymous', 'velodyne_raw') 142 | target_depth_completion_folder = os.path.join(kitti_path_depth_sparse, 'test_depth_completion', 'image_02') 143 | if not os.path.isdir(target_depth_completion_folder): 144 | os.makedirs(target_depth_completion_folder) 145 | shutil.move(init_depth_completion_folder, target_depth_completion_folder) 146 | os.rename(os.path.join(target_depth_completion_folder, os.path.split(init_depth_completion_folder)[1]), 147 | os.path.join(target_depth_completion_folder, 'data')) 148 | 149 | init_depth_prediction_folder = os.path.join(kitti_path, 'depth_selection', 150 | 'test_depth_prediction_anonymous', 'image') 151 | target_depth_prediction_folder = os.path.join(kitti_path_raw, 'test_depth_prediction', 'image_02') 152 | if not os.path.isdir(target_depth_prediction_folder): 153 | os.makedirs(target_depth_prediction_folder) 154 | shutil.move(init_depth_prediction_folder, target_depth_prediction_folder) 155 | os.rename(os.path.join(target_depth_prediction_folder, os.path.split(init_depth_prediction_folder)[1]), 156 | os.path.join(target_depth_prediction_folder, 'data')) 157 | 158 | init_depth_prediction_folder = os.path.join(kitti_path, 'depth_selection', 159 | 'test_depth_prediction_anonymous', 'intrinsics') 160 | target_depth_prediction_folder = os.path.join(kitti_path_raw, 'test_depth_prediction') 161 | shutil.move(init_depth_prediction_folder, target_depth_prediction_folder) 162 | 163 | shutil.rmtree(os.path.join(kitti_path, 'depth_selection')) 164 | 165 | 166 | def adjust_improvedgt_folders(kitti_folder = 'kitti_download'): 167 | """ This function adjust the format of the improved ground truth folder structure 168 | to the structure of the KITTI raw data and afterward removes the old directories. 169 | It is taken care that only the directories from the Download are worked on so that 170 | the procedure does not work on directories which it is not supposed to""" 171 | 172 | path_getter = gp.GetPath() 173 | dataset_folder_path = path_getter.get_data_path() 174 | gt_path = os.path.join(dataset_folder_path, kitti_folder) 175 | gt_path = os.path.join(gt_path, 'Depth_improved') 176 | assert os.path.isdir(gt_path), 'Path to data does not exist' 177 | folders = dl.DirLister.get_directories(gt_path) 178 | folders = dl.DirLister.include_dirs_by_name(folders, 'proj_depth') 179 | for f in folders: 180 | ground_path, camera = os.path.split(f) 181 | ground_path = os.path.split(ground_path)[0] 182 | ground_path = os.path.split(ground_path)[0] 183 | target_path = os.path.join(ground_path, camera, 'data') 184 | if not os.path.isdir(target_path): 185 | os.makedirs(target_path) 186 | else: 187 | continue 188 | for filepath in glob.glob(os.path.join(f, '*')): 189 | # Move each file to destination Directory 190 | shutil.move(filepath, target_path) 191 | print(target_path) 192 | 193 | for f in folders: 194 | remove_path = os.path.split(f)[0] 195 | remove_path = os.path.split(remove_path)[0] 196 | print(remove_path) 197 | shutil.rmtree(remove_path, ignore_errors=True) 198 | 199 | 200 | def adjust_projectedvelodyne_folders(kitti_folder='kitti_download'): 201 | """ This function adjust the format of the sparse ground truth folder structure 202 | to the structure of the KITTI raw data and afterward removes the old directories. 203 | It is taken care that only the directories from the Download are worked on so that 204 | the procedure does not work on directories which it is not supposed to""" 205 | 206 | path_getter = gp.GetPath() 207 | dataset_folder_path = path_getter.get_data_path() 208 | gt_path = os.path.join(dataset_folder_path, kitti_folder) 209 | gt_path = os.path.join(gt_path, 'Depth_projected') 210 | assert os.path.isdir(gt_path), 'Path to data does not exist' 211 | folders = dl.DirLister.get_directories(gt_path) 212 | folders = dl.DirLister.include_dirs_by_name(folders, 'proj_depth') 213 | for f in folders: 214 | ground_path, camera = os.path.split(f) 215 | ground_path = os.path.split(ground_path)[0] 216 | ground_path = os.path.split(ground_path)[0] 217 | target_path = os.path.join(ground_path, camera, 'data') 218 | if not os.path.isdir(target_path): 219 | os.makedirs(target_path) 220 | else: 221 | continue 222 | for filepath in glob.glob(os.path.join(f, '*')): 223 | # Move each file to destination Directory 224 | shutil.move(filepath, target_path) 225 | print(target_path) 226 | 227 | for f in folders: 228 | remove_path = os.path.split(f)[0] 229 | remove_path = os.path.split(remove_path)[0] 230 | print(remove_path) 231 | shutil.rmtree(remove_path, ignore_errors=True) 232 | 233 | 234 | def generate_depth_from_velo(kitti_folder='kitti_download'): 235 | """ This function generates the depth maps that correspond to the 236 | single point clouds of the raw LiDAR scans""" 237 | 238 | path_getter = gp.GetPath() 239 | dataset_folder_path = path_getter.get_data_path() 240 | gt_path = os.path.join(dataset_folder_path, kitti_folder) 241 | depth_path = os.path.join(gt_path, 'Depth') 242 | gt_path = os.path.join(gt_path, 'Raw_data') 243 | assert os.path.isdir(gt_path), 'Path to data does not exist' 244 | folders = dl.DirLister.get_directories(gt_path) 245 | folders = dl.DirLister.include_dirs_by_name(folders, 'velodyne_points') 246 | for f in folders: 247 | base_dir = os.path.split(f)[0] 248 | base_dir = os.path.split(base_dir)[0] 249 | calib_dir = os.path.split(base_dir)[0] 250 | image_dir_2 = os.path.join(base_dir, 'image_02', 'data') 251 | image_dir_3 = os.path.join(base_dir, 'image_03', 'data') 252 | day, drive = os.path.split(base_dir) 253 | day = os.path.split(day)[1] 254 | depth_dir_2 = os.path.join(depth_path, day, drive, 'image_02', 'data') 255 | depth_dir_3 = os.path.join(depth_path, day, drive, 'image_03', 'data') 256 | if not os.path.isdir(depth_dir_2): 257 | os.makedirs(depth_dir_2) 258 | if not os.path.isdir(depth_dir_3): 259 | os.makedirs(depth_dir_3) 260 | 261 | for file in glob.glob(os.path.join(f, '*')): 262 | filename = os.path.split(file)[1] 263 | filename_img = filename[:-3] + 'png' 264 | im_size_2 = cv2.imread(os.path.join(image_dir_2, filename_img)).shape[:2] 265 | im_size_3 = cv2.imread(os.path.join(image_dir_3, filename_img)).shape[:2] 266 | depth_2 = pcl_to_depth_map(calib_dir, file, im_size_2, 2) 267 | depth_3 = pcl_to_depth_map(calib_dir, file, im_size_3, 3) 268 | depth_2 = (depth_2 * 256).astype(np.uint16) 269 | depth_3 = (depth_3 * 256).astype(np.uint16) 270 | 271 | cv2.imwrite(os.path.join(depth_dir_2, filename_img), depth_2) 272 | cv2.imwrite(os.path.join(depth_dir_3, filename_img), depth_3) 273 | print(f) 274 | 275 | 276 | if __name__ == '__main__': 277 | kitti_folder = 'kitti_download' 278 | download_kitti_all(kitti_folder) 279 | adjust_improvedgt_folders(kitti_folder) 280 | adjust_projectedvelodyne_folders(kitti_folder) 281 | generate_depth_from_velo(kitti_folder) -------------------------------------------------------------------------------- /dataloader/data_preprocessing/Mapillary/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "labels": [ 3 | { 4 | "color": [ 5 | 165, 6 | 42, 7 | 42 8 | ], 9 | "instances": true, 10 | "readable": "Bird", 11 | "name": "animal--bird", 12 | "evaluate": true 13 | }, 14 | { 15 | "color": [ 16 | 0, 17 | 192, 18 | 0 19 | ], 20 | "instances": true, 21 | "readable": "Ground Animal", 22 | "name": "animal--ground-animal", 23 | "evaluate": true 24 | }, 25 | { 26 | "color": [ 27 | 196, 28 | 196, 29 | 196 30 | ], 31 | "instances": false, 32 | "readable": "Curb", 33 | "name": "construction--barrier--curb", 34 | "evaluate": true 35 | }, 36 | { 37 | "color": [ 38 | 190, 39 | 153, 40 | 153 41 | ], 42 | "instances": false, 43 | "readable": "Fence", 44 | "name": "construction--barrier--fence", 45 | "evaluate": true 46 | }, 47 | { 48 | "color": [ 49 | 180, 50 | 165, 51 | 180 52 | ], 53 | "instances": false, 54 | "readable": "Guard Rail", 55 | "name": "construction--barrier--guard-rail", 56 | "evaluate": true 57 | }, 58 | { 59 | "color": [ 60 | 90, 61 | 120, 62 | 150 63 | ], 64 | "instances": false, 65 | "readable": "Barrier", 66 | "name": "construction--barrier--other-barrier", 67 | "evaluate": true 68 | }, 69 | { 70 | "color": [ 71 | 102, 72 | 102, 73 | 156 74 | ], 75 | "instances": false, 76 | "readable": "Wall", 77 | "name": "construction--barrier--wall", 78 | "evaluate": true 79 | }, 80 | { 81 | "color": [ 82 | 128, 83 | 64, 84 | 255 85 | ], 86 | "instances": false, 87 | "readable": "Bike Lane", 88 | "name": "construction--flat--bike-lane", 89 | "evaluate": true 90 | }, 91 | { 92 | "color": [ 93 | 140, 94 | 140, 95 | 200 96 | ], 97 | "instances": true, 98 | "readable": "Crosswalk - Plain", 99 | "name": "construction--flat--crosswalk-plain", 100 | "evaluate": true 101 | }, 102 | { 103 | "color": [ 104 | 170, 105 | 170, 106 | 170 107 | ], 108 | "instances": false, 109 | "readable": "Curb Cut", 110 | "name": "construction--flat--curb-cut", 111 | "evaluate": true 112 | }, 113 | { 114 | "color": [ 115 | 250, 116 | 170, 117 | 160 118 | ], 119 | "instances": false, 120 | "readable": "Parking", 121 | "name": "construction--flat--parking", 122 | "evaluate": true 123 | }, 124 | { 125 | "color": [ 126 | 96, 127 | 96, 128 | 96 129 | ], 130 | "instances": false, 131 | "readable": "Pedestrian Area", 132 | "name": "construction--flat--pedestrian-area", 133 | "evaluate": true 134 | }, 135 | { 136 | "color": [ 137 | 230, 138 | 150, 139 | 140 140 | ], 141 | "instances": false, 142 | "readable": "Rail Track", 143 | "name": "construction--flat--rail-track", 144 | "evaluate": true 145 | }, 146 | { 147 | "color": [ 148 | 128, 149 | 64, 150 | 128 151 | ], 152 | "instances": false, 153 | "readable": "Road", 154 | "name": "construction--flat--road", 155 | "evaluate": true 156 | }, 157 | { 158 | "color": [ 159 | 110, 160 | 110, 161 | 110 162 | ], 163 | "instances": false, 164 | "readable": "Service Lane", 165 | "name": "construction--flat--service-lane", 166 | "evaluate": true 167 | }, 168 | { 169 | "color": [ 170 | 244, 171 | 35, 172 | 232 173 | ], 174 | "instances": false, 175 | "readable": "Sidewalk", 176 | "name": "construction--flat--sidewalk", 177 | "evaluate": true 178 | }, 179 | { 180 | "color": [ 181 | 150, 182 | 100, 183 | 100 184 | ], 185 | "instances": false, 186 | "readable": "Bridge", 187 | "name": "construction--structure--bridge", 188 | "evaluate": true 189 | }, 190 | { 191 | "color": [ 192 | 70, 193 | 70, 194 | 70 195 | ], 196 | "instances": false, 197 | "readable": "Building", 198 | "name": "construction--structure--building", 199 | "evaluate": true 200 | }, 201 | { 202 | "color": [ 203 | 150, 204 | 120, 205 | 90 206 | ], 207 | "instances": false, 208 | "readable": "Tunnel", 209 | "name": "construction--structure--tunnel", 210 | "evaluate": true 211 | }, 212 | { 213 | "color": [ 214 | 220, 215 | 20, 216 | 60 217 | ], 218 | "instances": true, 219 | "readable": "Person", 220 | "name": "human--person", 221 | "evaluate": true 222 | }, 223 | { 224 | "color": [ 225 | 255, 226 | 0, 227 | 0 228 | ], 229 | "instances": true, 230 | "readable": "Bicyclist", 231 | "name": "human--rider--bicyclist", 232 | "evaluate": true 233 | }, 234 | { 235 | "color": [ 236 | 255, 237 | 0, 238 | 100 239 | ], 240 | "instances": true, 241 | "readable": "Motorcyclist", 242 | "name": "human--rider--motorcyclist", 243 | "evaluate": true 244 | }, 245 | { 246 | "color": [ 247 | 255, 248 | 0, 249 | 200 250 | ], 251 | "instances": true, 252 | "readable": "Other Rider", 253 | "name": "human--rider--other-rider", 254 | "evaluate": true 255 | }, 256 | { 257 | "color": [ 258 | 200, 259 | 128, 260 | 128 261 | ], 262 | "instances": true, 263 | "readable": "Lane Marking - Crosswalk", 264 | "name": "marking--crosswalk-zebra", 265 | "evaluate": true 266 | }, 267 | { 268 | "color": [ 269 | 255, 270 | 255, 271 | 255 272 | ], 273 | "instances": false, 274 | "readable": "Lane Marking - General", 275 | "name": "marking--general", 276 | "evaluate": true 277 | }, 278 | { 279 | "color": [ 280 | 64, 281 | 170, 282 | 64 283 | ], 284 | "instances": false, 285 | "readable": "Mountain", 286 | "name": "nature--mountain", 287 | "evaluate": true 288 | }, 289 | { 290 | "color": [ 291 | 230, 292 | 160, 293 | 50 294 | ], 295 | "instances": false, 296 | "readable": "Sand", 297 | "name": "nature--sand", 298 | "evaluate": true 299 | }, 300 | { 301 | "color": [ 302 | 70, 303 | 130, 304 | 180 305 | ], 306 | "instances": false, 307 | "readable": "Sky", 308 | "name": "nature--sky", 309 | "evaluate": true 310 | }, 311 | { 312 | "color": [ 313 | 190, 314 | 255, 315 | 255 316 | ], 317 | "instances": false, 318 | "readable": "Snow", 319 | "name": "nature--snow", 320 | "evaluate": true 321 | }, 322 | { 323 | "color": [ 324 | 152, 325 | 251, 326 | 152 327 | ], 328 | "instances": false, 329 | "readable": "Terrain", 330 | "name": "nature--terrain", 331 | "evaluate": true 332 | }, 333 | { 334 | "color": [ 335 | 107, 336 | 142, 337 | 35 338 | ], 339 | "instances": false, 340 | "readable": "Vegetation", 341 | "name": "nature--vegetation", 342 | "evaluate": true 343 | }, 344 | { 345 | "color": [ 346 | 0, 347 | 170, 348 | 30 349 | ], 350 | "instances": false, 351 | "readable": "Water", 352 | "name": "nature--water", 353 | "evaluate": true 354 | }, 355 | { 356 | "color": [ 357 | 255, 358 | 255, 359 | 128 360 | ], 361 | "instances": true, 362 | "readable": "Banner", 363 | "name": "object--banner", 364 | "evaluate": true 365 | }, 366 | { 367 | "color": [ 368 | 250, 369 | 0, 370 | 30 371 | ], 372 | "instances": true, 373 | "readable": "Bench", 374 | "name": "object--bench", 375 | "evaluate": true 376 | }, 377 | { 378 | "color": [ 379 | 100, 380 | 140, 381 | 180 382 | ], 383 | "instances": true, 384 | "readable": "Bike Rack", 385 | "name": "object--bike-rack", 386 | "evaluate": true 387 | }, 388 | { 389 | "color": [ 390 | 220, 391 | 220, 392 | 220 393 | ], 394 | "instances": true, 395 | "readable": "Billboard", 396 | "name": "object--billboard", 397 | "evaluate": true 398 | }, 399 | { 400 | "color": [ 401 | 220, 402 | 128, 403 | 128 404 | ], 405 | "instances": true, 406 | "readable": "Catch Basin", 407 | "name": "object--catch-basin", 408 | "evaluate": true 409 | }, 410 | { 411 | "color": [ 412 | 222, 413 | 40, 414 | 40 415 | ], 416 | "instances": true, 417 | "readable": "CCTV Camera", 418 | "name": "object--cctv-camera", 419 | "evaluate": true 420 | }, 421 | { 422 | "color": [ 423 | 100, 424 | 170, 425 | 30 426 | ], 427 | "instances": true, 428 | "readable": "Fire Hydrant", 429 | "name": "object--fire-hydrant", 430 | "evaluate": true 431 | }, 432 | { 433 | "color": [ 434 | 40, 435 | 40, 436 | 40 437 | ], 438 | "instances": true, 439 | "readable": "Junction Box", 440 | "name": "object--junction-box", 441 | "evaluate": true 442 | }, 443 | { 444 | "color": [ 445 | 33, 446 | 33, 447 | 33 448 | ], 449 | "instances": true, 450 | "readable": "Mailbox", 451 | "name": "object--mailbox", 452 | "evaluate": true 453 | }, 454 | { 455 | "color": [ 456 | 100, 457 | 128, 458 | 160 459 | ], 460 | "instances": true, 461 | "readable": "Manhole", 462 | "name": "object--manhole", 463 | "evaluate": true 464 | }, 465 | { 466 | "color": [ 467 | 142, 468 | 0, 469 | 0 470 | ], 471 | "instances": true, 472 | "readable": "Phone Booth", 473 | "name": "object--phone-booth", 474 | "evaluate": true 475 | }, 476 | { 477 | "color": [ 478 | 70, 479 | 100, 480 | 150 481 | ], 482 | "instances": false, 483 | "readable": "Pothole", 484 | "name": "object--pothole", 485 | "evaluate": true 486 | }, 487 | { 488 | "color": [ 489 | 210, 490 | 170, 491 | 100 492 | ], 493 | "instances": true, 494 | "readable": "Street Light", 495 | "name": "object--street-light", 496 | "evaluate": true 497 | }, 498 | { 499 | "color": [ 500 | 153, 501 | 153, 502 | 153 503 | ], 504 | "instances": true, 505 | "readable": "Pole", 506 | "name": "object--support--pole", 507 | "evaluate": true 508 | }, 509 | { 510 | "color": [ 511 | 128, 512 | 128, 513 | 128 514 | ], 515 | "instances": true, 516 | "readable": "Traffic Sign Frame", 517 | "name": "object--support--traffic-sign-frame", 518 | "evaluate": true 519 | }, 520 | { 521 | "color": [ 522 | 0, 523 | 0, 524 | 80 525 | ], 526 | "instances": true, 527 | "readable": "Utility Pole", 528 | "name": "object--support--utility-pole", 529 | "evaluate": true 530 | }, 531 | { 532 | "color": [ 533 | 250, 534 | 170, 535 | 30 536 | ], 537 | "instances": true, 538 | "readable": "Traffic Light", 539 | "name": "object--traffic-light", 540 | "evaluate": true 541 | }, 542 | { 543 | "color": [ 544 | 192, 545 | 192, 546 | 192 547 | ], 548 | "instances": true, 549 | "readable": "Traffic Sign (Back)", 550 | "name": "object--traffic-sign--back", 551 | "evaluate": true 552 | }, 553 | { 554 | "color": [ 555 | 220, 556 | 220, 557 | 0 558 | ], 559 | "instances": true, 560 | "readable": "Traffic Sign (Front)", 561 | "name": "object--traffic-sign--front", 562 | "evaluate": true 563 | }, 564 | { 565 | "color": [ 566 | 140, 567 | 140, 568 | 20 569 | ], 570 | "instances": true, 571 | "readable": "Trash Can", 572 | "name": "object--trash-can", 573 | "evaluate": true 574 | }, 575 | { 576 | "color": [ 577 | 119, 578 | 11, 579 | 32 580 | ], 581 | "instances": true, 582 | "readable": "Bicycle", 583 | "name": "object--vehicle--bicycle", 584 | "evaluate": true 585 | }, 586 | { 587 | "color": [ 588 | 150, 589 | 0, 590 | 255 591 | ], 592 | "instances": true, 593 | "readable": "Boat", 594 | "name": "object--vehicle--boat", 595 | "evaluate": true 596 | }, 597 | { 598 | "color": [ 599 | 0, 600 | 60, 601 | 100 602 | ], 603 | "instances": true, 604 | "readable": "Bus", 605 | "name": "object--vehicle--bus", 606 | "evaluate": true 607 | }, 608 | { 609 | "color": [ 610 | 0, 611 | 0, 612 | 142 613 | ], 614 | "instances": true, 615 | "readable": "Car", 616 | "name": "object--vehicle--car", 617 | "evaluate": true 618 | }, 619 | { 620 | "color": [ 621 | 0, 622 | 0, 623 | 90 624 | ], 625 | "instances": true, 626 | "readable": "Caravan", 627 | "name": "object--vehicle--caravan", 628 | "evaluate": true 629 | }, 630 | { 631 | "color": [ 632 | 0, 633 | 0, 634 | 230 635 | ], 636 | "instances": true, 637 | "readable": "Motorcycle", 638 | "name": "object--vehicle--motorcycle", 639 | "evaluate": true 640 | }, 641 | { 642 | "color": [ 643 | 0, 644 | 80, 645 | 100 646 | ], 647 | "instances": false, 648 | "readable": "On Rails", 649 | "name": "object--vehicle--on-rails", 650 | "evaluate": true 651 | }, 652 | { 653 | "color": [ 654 | 128, 655 | 64, 656 | 64 657 | ], 658 | "instances": true, 659 | "readable": "Other Vehicle", 660 | "name": "object--vehicle--other-vehicle", 661 | "evaluate": true 662 | }, 663 | { 664 | "color": [ 665 | 0, 666 | 0, 667 | 110 668 | ], 669 | "instances": true, 670 | "readable": "Trailer", 671 | "name": "object--vehicle--trailer", 672 | "evaluate": true 673 | }, 674 | { 675 | "color": [ 676 | 0, 677 | 0, 678 | 70 679 | ], 680 | "instances": true, 681 | "readable": "Truck", 682 | "name": "object--vehicle--truck", 683 | "evaluate": true 684 | }, 685 | { 686 | "color": [ 687 | 0, 688 | 0, 689 | 192 690 | ], 691 | "instances": true, 692 | "readable": "Wheeled Slow", 693 | "name": "object--vehicle--wheeled-slow", 694 | "evaluate": true 695 | }, 696 | { 697 | "color": [ 698 | 32, 699 | 32, 700 | 32 701 | ], 702 | "instances": false, 703 | "readable": "Car Mount", 704 | "name": "void--car-mount", 705 | "evaluate": true 706 | }, 707 | { 708 | "color": [ 709 | 120, 710 | 10, 711 | 10 712 | ], 713 | "instances": false, 714 | "readable": "Ego Vehicle", 715 | "name": "void--ego-vehicle", 716 | "evaluate": true 717 | }, 718 | { 719 | "color": [ 720 | 0, 721 | 0, 722 | 0 723 | ], 724 | "instances": false, 725 | "readable": "Unlabeled", 726 | "name": "void--unlabeled", 727 | "evaluate": false 728 | } 729 | ], 730 | "version": 1.1, 731 | "mapping": "public", 732 | "folder_structure": "{split}/{content}/{key:.{22}}.{ext}" 733 | } 734 | -------------------------------------------------------------------------------- /dataloader/data_preprocessing/KITTI/kitti_archives_to_download.txt: -------------------------------------------------------------------------------- 1 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_calib.zip 2 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0001/2011_09_26_drive_0001_sync.zip 3 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0002/2011_09_26_drive_0002_sync.zip 4 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0005/2011_09_26_drive_0005_sync.zip 5 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0009/2011_09_26_drive_0009_sync.zip 6 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0011/2011_09_26_drive_0011_sync.zip 7 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0013/2011_09_26_drive_0013_sync.zip 8 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0014/2011_09_26_drive_0014_sync.zip 9 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0015/2011_09_26_drive_0015_sync.zip 10 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0017/2011_09_26_drive_0017_sync.zip 11 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0018/2011_09_26_drive_0018_sync.zip 12 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0019/2011_09_26_drive_0019_sync.zip 13 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0020/2011_09_26_drive_0020_sync.zip 14 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0022/2011_09_26_drive_0022_sync.zip 15 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0023/2011_09_26_drive_0023_sync.zip 16 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0027/2011_09_26_drive_0027_sync.zip 17 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0028/2011_09_26_drive_0028_sync.zip 18 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0029/2011_09_26_drive_0029_sync.zip 19 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0032/2011_09_26_drive_0032_sync.zip 20 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0035/2011_09_26_drive_0035_sync.zip 21 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0036/2011_09_26_drive_0036_sync.zip 22 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0039/2011_09_26_drive_0039_sync.zip 23 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0046/2011_09_26_drive_0046_sync.zip 24 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0048/2011_09_26_drive_0048_sync.zip 25 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0051/2011_09_26_drive_0051_sync.zip 26 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0052/2011_09_26_drive_0052_sync.zip 27 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0056/2011_09_26_drive_0056_sync.zip 28 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0057/2011_09_26_drive_0057_sync.zip 29 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0059/2011_09_26_drive_0059_sync.zip 30 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0060/2011_09_26_drive_0060_sync.zip 31 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0061/2011_09_26_drive_0061_sync.zip 32 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0064/2011_09_26_drive_0064_sync.zip 33 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0070/2011_09_26_drive_0070_sync.zip 34 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0079/2011_09_26_drive_0079_sync.zip 35 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0084/2011_09_26_drive_0084_sync.zip 36 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0086/2011_09_26_drive_0086_sync.zip 37 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0087/2011_09_26_drive_0087_sync.zip 38 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0091/2011_09_26_drive_0091_sync.zip 39 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0093/2011_09_26_drive_0093_sync.zip 40 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0095/2011_09_26_drive_0095_sync.zip 41 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0096/2011_09_26_drive_0096_sync.zip 42 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0101/2011_09_26_drive_0101_sync.zip 43 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0104/2011_09_26_drive_0104_sync.zip 44 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0106/2011_09_26_drive_0106_sync.zip 45 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0113/2011_09_26_drive_0113_sync.zip 46 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_26_drive_0117/2011_09_26_drive_0117_sync.zip 47 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_calib.zip 48 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0001/2011_09_28_drive_0001_sync.zip 49 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0002/2011_09_28_drive_0002_sync.zip 50 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0016/2011_09_28_drive_0016_sync.zip 51 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0021/2011_09_28_drive_0021_sync.zip 52 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0034/2011_09_28_drive_0034_sync.zip 53 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0035/2011_09_28_drive_0035_sync.zip 54 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0037/2011_09_28_drive_0037_sync.zip 55 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0038/2011_09_28_drive_0038_sync.zip 56 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0039/2011_09_28_drive_0039_sync.zip 57 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0043/2011_09_28_drive_0043_sync.zip 58 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0045/2011_09_28_drive_0045_sync.zip 59 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0047/2011_09_28_drive_0047_sync.zip 60 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0053/2011_09_28_drive_0053_sync.zip 61 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0054/2011_09_28_drive_0054_sync.zip 62 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0057/2011_09_28_drive_0057_sync.zip 63 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0065/2011_09_28_drive_0065_sync.zip 64 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0066/2011_09_28_drive_0066_sync.zip 65 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0068/2011_09_28_drive_0068_sync.zip 66 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0070/2011_09_28_drive_0070_sync.zip 67 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0071/2011_09_28_drive_0071_sync.zip 68 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0075/2011_09_28_drive_0075_sync.zip 69 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0077/2011_09_28_drive_0077_sync.zip 70 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0078/2011_09_28_drive_0078_sync.zip 71 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0080/2011_09_28_drive_0080_sync.zip 72 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0082/2011_09_28_drive_0082_sync.zip 73 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0086/2011_09_28_drive_0086_sync.zip 74 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0087/2011_09_28_drive_0087_sync.zip 75 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0089/2011_09_28_drive_0089_sync.zip 76 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0090/2011_09_28_drive_0090_sync.zip 77 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0094/2011_09_28_drive_0094_sync.zip 78 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0095/2011_09_28_drive_0095_sync.zip 79 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0096/2011_09_28_drive_0096_sync.zip 80 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0098/2011_09_28_drive_0098_sync.zip 81 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0100/2011_09_28_drive_0100_sync.zip 82 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0102/2011_09_28_drive_0102_sync.zip 83 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0103/2011_09_28_drive_0103_sync.zip 84 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0104/2011_09_28_drive_0104_sync.zip 85 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0106/2011_09_28_drive_0106_sync.zip 86 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0108/2011_09_28_drive_0108_sync.zip 87 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0110/2011_09_28_drive_0110_sync.zip 88 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0113/2011_09_28_drive_0113_sync.zip 89 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0117/2011_09_28_drive_0117_sync.zip 90 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0119/2011_09_28_drive_0119_sync.zip 91 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0121/2011_09_28_drive_0121_sync.zip 92 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0122/2011_09_28_drive_0122_sync.zip 93 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0125/2011_09_28_drive_0125_sync.zip 94 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0126/2011_09_28_drive_0126_sync.zip 95 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0128/2011_09_28_drive_0128_sync.zip 96 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0132/2011_09_28_drive_0132_sync.zip 97 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0134/2011_09_28_drive_0134_sync.zip 98 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0135/2011_09_28_drive_0135_sync.zip 99 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0136/2011_09_28_drive_0136_sync.zip 100 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0138/2011_09_28_drive_0138_sync.zip 101 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0141/2011_09_28_drive_0141_sync.zip 102 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0143/2011_09_28_drive_0143_sync.zip 103 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0145/2011_09_28_drive_0145_sync.zip 104 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0146/2011_09_28_drive_0146_sync.zip 105 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0149/2011_09_28_drive_0149_sync.zip 106 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0153/2011_09_28_drive_0153_sync.zip 107 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0154/2011_09_28_drive_0154_sync.zip 108 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0155/2011_09_28_drive_0155_sync.zip 109 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0156/2011_09_28_drive_0156_sync.zip 110 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0160/2011_09_28_drive_0160_sync.zip 111 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0161/2011_09_28_drive_0161_sync.zip 112 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0162/2011_09_28_drive_0162_sync.zip 113 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0165/2011_09_28_drive_0165_sync.zip 114 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0166/2011_09_28_drive_0166_sync.zip 115 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0167/2011_09_28_drive_0167_sync.zip 116 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0168/2011_09_28_drive_0168_sync.zip 117 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0171/2011_09_28_drive_0171_sync.zip 118 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0174/2011_09_28_drive_0174_sync.zip 119 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0177/2011_09_28_drive_0177_sync.zip 120 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0179/2011_09_28_drive_0179_sync.zip 121 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0183/2011_09_28_drive_0183_sync.zip 122 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0184/2011_09_28_drive_0184_sync.zip 123 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0185/2011_09_28_drive_0185_sync.zip 124 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0186/2011_09_28_drive_0186_sync.zip 125 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0187/2011_09_28_drive_0187_sync.zip 126 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0191/2011_09_28_drive_0191_sync.zip 127 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0192/2011_09_28_drive_0192_sync.zip 128 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0195/2011_09_28_drive_0195_sync.zip 129 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0198/2011_09_28_drive_0198_sync.zip 130 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0199/2011_09_28_drive_0199_sync.zip 131 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0201/2011_09_28_drive_0201_sync.zip 132 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0204/2011_09_28_drive_0204_sync.zip 133 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0205/2011_09_28_drive_0205_sync.zip 134 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0208/2011_09_28_drive_0208_sync.zip 135 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0209/2011_09_28_drive_0209_sync.zip 136 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0214/2011_09_28_drive_0214_sync.zip 137 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0216/2011_09_28_drive_0216_sync.zip 138 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0220/2011_09_28_drive_0220_sync.zip 139 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_28_drive_0222/2011_09_28_drive_0222_sync.zip 140 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_29_calib.zip 141 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_29_drive_0004/2011_09_29_drive_0004_sync.zip 142 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_29_drive_0026/2011_09_29_drive_0026_sync.zip 143 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_29_drive_0071/2011_09_29_drive_0071_sync.zip 144 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_30_calib.zip 145 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_30_drive_0016/2011_09_30_drive_0016_sync.zip 146 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_30_drive_0018/2011_09_30_drive_0018_sync.zip 147 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_30_drive_0020/2011_09_30_drive_0020_sync.zip 148 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_30_drive_0027/2011_09_30_drive_0027_sync.zip 149 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_30_drive_0028/2011_09_30_drive_0028_sync.zip 150 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_30_drive_0033/2011_09_30_drive_0033_sync.zip 151 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_09_30_drive_0034/2011_09_30_drive_0034_sync.zip 152 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_10_03_calib.zip 153 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_10_03_drive_0027/2011_10_03_drive_0027_sync.zip 154 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_10_03_drive_0034/2011_10_03_drive_0034_sync.zip 155 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_10_03_drive_0042/2011_10_03_drive_0042_sync.zip 156 | https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_10_03_drive_0047/2011_10_03_drive_0047_sync.zip -------------------------------------------------------------------------------- /dataloader/file_io/dataset_scaler.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import warnings 4 | import json 5 | from multiprocessing import Pool 6 | 7 | import cv2 8 | import torchvision.transforms as transforms 9 | import PIL.Image as pil 10 | import numpy as np 11 | 12 | cv2.setNumThreads(0) 13 | 14 | import dataloader.file_io.get_path as gp 15 | import dataloader.pt_data_loader.mytransforms as mytransforms 16 | import dataloader.pt_data_loader.dataset_parameterset as dps 17 | 18 | NUM_WORKERS = 4 19 | IMAGE_KEYS = ('color', 'depth', 'segmentation') 20 | SPLIT_NAMES = ('train', 'validation', 'test') 21 | CAMERA_KEYS = ('camera_intrinsics', 'camera_intrinsics_right') 22 | 23 | # IMPORTANT NOTE: This dataset scaler works only reliably when the original input files are PNG files, not JPEG 24 | def load_sample(si): 25 | """ 26 | Loads the specified sample. 27 | 28 | :param si: dict of the form {global_index: {"color": path, "depth": path, ...}} 29 | :return: - the global index of the sample in the dataset 30 | - the sample in the standard dataloader format 31 | - a path dictionary that is built similar to the sample dictionary and contains the image path instead of 32 | an image object 33 | """ 34 | set_idx, sample = si 35 | 36 | dataset = sample.pop('dataset') 37 | dataset_path = sample.pop('dataset_path') 38 | depth_mode = sample.pop('depth_mode') 39 | 40 | new_sample = dict() 41 | paths = dict() 42 | 43 | for key, content in sample.items(): 44 | if any(key.startswith(s) for s in IMAGE_KEYS): 45 | filepath = os.path.join(dataset_path, content) 46 | filepath = filepath.replace('/', os.sep) 47 | filepath = filepath.replace('\\', os.sep) 48 | 49 | image = cv2.imread(filepath, -1) 50 | 51 | new_sample[key, 0, 0] = image 52 | paths[key, 0, 0] = content 53 | 54 | else: 55 | new_sample[key, 0, 0] = content 56 | 57 | load_transforms = transforms.Compose([ 58 | mytransforms.LoadRGB(), 59 | mytransforms.LoadSegmentation(), 60 | mytransforms.LoadDepth(), 61 | mytransforms.LoadNumerics() 62 | ]) 63 | 64 | new_sample = load_transforms(new_sample) 65 | 66 | return set_idx, new_sample, paths 67 | 68 | 69 | def save_image_file(image, img_type, dataset, path): 70 | """ 71 | Brings the image in the right format to be saved. Therefore, it reverses all load transforms 72 | 73 | :param image: PIL image 74 | :param img_type: color, depth, segmentation 75 | :param dataset: e.g. 'cityscapes' 76 | :param path: path of the destination path 77 | """ 78 | 79 | depth_transformer = mytransforms.LoadDepth() 80 | segmentation_transformer = mytransforms.LoadSegmentation() 81 | 82 | image = np.array(image) # convert from PIL to openCV 83 | 84 | if 'color' in img_type: 85 | image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR) 86 | 87 | elif 'depth' in img_type: 88 | image = depth_transformer.inverse({img_type: image})[img_type] 89 | # image = image.astype(np.uint16) 90 | 91 | elif 'segmentation' in img_type: 92 | image = segmentation_transformer.inverse({img_type: image})[img_type] 93 | # image = np.array(image, dtype=np.uint8) 94 | 95 | cv2.imwrite(path, image) 96 | 97 | 98 | def get_index_from_position(position_array, pos_0): 99 | """ 100 | Returns the index of the position tuple with first entry pos_0 in the pos_array 101 | 102 | :param position_array: An array built out of 4-tuples in the standard positions format. 103 | :param pos_0: Global position index (first entry of the 4-tuple) to search for 104 | :return: index of the tuple in pos_array that begins with pos_0. If none is found, -1 is returned. 105 | """ 106 | for i, position in zip(range(len(position_array)), position_array): 107 | if position[0] == pos_0: 108 | return i 109 | return -1 110 | 111 | 112 | class DatasetScaler(object): 113 | def __init__(self, dataset, split=None): 114 | self.dataset = dataset 115 | self.dataset_path = self._gen_dataset_path(dataset) 116 | if split is not None: 117 | self.split_path = self.dataset_path + '_' + split 118 | else: 119 | self.split_path = self.dataset_path 120 | parameters = dps.DatasetParameterset(dataset) 121 | self.depth_mode = parameters.depth_mode 122 | 123 | def _gen_dataset_path(self, dataset): 124 | path_getter = gp.GetPath() 125 | dataset_folder = path_getter.get_data_path() 126 | 127 | return os.path.join(dataset_folder, dataset) 128 | 129 | def _parse_json_file(self, path, keys_to_convert): 130 | with open(path) as fd: 131 | json_data = json.load(fd) 132 | 133 | names = json_data['names'] 134 | files = json_data['files'] 135 | positions = json_data['positions'] 136 | numerical_values = json_data['numerical_values'] 137 | # Intuition would say that this should be a list, 138 | # but a user could for example decide to convert only 139 | # the depth ground truth from KITTI which is not 140 | # available for every point in time, while for example 141 | # color is. 142 | # A dict indexed by integers can represent 143 | # this sparseness nicely. 144 | samples = dict() 145 | 146 | for name, filenames, position, numerics in zip(names, files, positions, numerical_values): 147 | if name not in keys_to_convert and keys_to_convert != (): 148 | continue 149 | 150 | if os.path.splitext(filenames[0])[1].upper() in ('.JPG', '.JPEG'): 151 | warnings.warn( 152 | 'This dataset uses JPEG images. Due to the lossy JPEG compression, the scaled version of the ' 153 | 'dataset will not be exactly equal to the unscaled dataset where the images are loaded in the ' 154 | 'normal size and scaled afterwards.') 155 | 156 | for (set_idx, _, _, _), filename, i in zip(position, filenames, range(len(filenames))): 157 | if set_idx not in samples: 158 | samples[set_idx] = dict() 159 | samples[set_idx]['dataset'] = self.dataset 160 | samples[set_idx]['dataset_path'] = self.dataset_path 161 | samples[set_idx]['depth_mode'] = self.depth_mode 162 | if type(numerics) != list: 163 | samples[set_idx][name] = filename 164 | else: 165 | samples[set_idx][name] = numerics[i] 166 | 167 | # >>> samples[30004]['depth'] # layout example 168 | # 'Depth/2011_09_30/2011_09_30_drive_0028_sync/image_02/data/0000001324.png' 169 | 170 | return samples 171 | 172 | def _get_samples(self, pool, keys_to_convert): 173 | json_path = os.path.join(self.dataset_path, 'basic_files.json') 174 | samples = self._parse_json_file(json_path, keys_to_convert) 175 | 176 | return pool.imap_unordered(load_sample, samples.items()) 177 | 178 | def _print_progress(self, iterator): 179 | for i, elem in enumerate(iterator): 180 | if i % 100 == 0: 181 | print('', flush=True) 182 | 183 | print('.', end='') 184 | 185 | yield elem 186 | 187 | def _load_split_data(self, split_path=None): 188 | """ 189 | 190 | :param split_path: Path where the split is stored. Standard is self.split_path which is the same as 191 | self.dataset_path if not specified otherwise when calling the __init__() 192 | :return: A dictionary containing all split dictionaries 193 | """ 194 | if split_path is None: 195 | split_path = self.split_path 196 | split_data = {} 197 | for split in SPLIT_NAMES: 198 | path = os.path.join(split_path, split+'.json') 199 | try: 200 | with open(path) as fd: 201 | split_json_data = json.load(fd) 202 | split_data[split] = split_json_data 203 | except: 204 | print(split_path) 205 | print('No {} data accessible'.format(split)) 206 | return split_data 207 | 208 | def _get_index_from_position(self, position_array, pos_0): 209 | """ 210 | Returns the index of the position tuple with first entry pos_0 in the pos_array 211 | 212 | :param position_array: An array built out of 4-tuples in the standard positions format. 213 | :param pos_0: Global position index (first entry of the 4-tuple) to search for 214 | :return: index of the tuple in pos_array that begins with pos_0. If none is found, None is returned. 215 | """ 216 | for i, position in zip(range(len(position_array)), position_array): 217 | if position[0] == pos_0: 218 | return i 219 | return None 220 | 221 | def _adapt_camera_intrinsics_in_split_file(self, split_data_dict, camera_intrinsics): 222 | """ 223 | Writes the given camera intrinsics into the given split data dictionary. Automatically searches for the right 224 | place in the dictionary and saves tha camera intrinsics in the 'files' entry. 225 | 226 | :param split_data_dict: dictionary with the split data (as saved in the json files) 227 | :param camera_intrinsics: dictionary with the camera intrinsics 228 | :return: split_data_dict with modified camera_intrinsics 229 | """ 230 | for split, split_data in split_data_dict.items(): 231 | names = split_data['names'] 232 | positions = split_data['positions'] 233 | 234 | for set_idx in camera_intrinsics: 235 | for camera_key in camera_intrinsics[set_idx]: 236 | camera_index = names.index(camera_key) 237 | split_index = self._get_index_from_position(positions[camera_index], set_idx) 238 | if split_index is None: 239 | continue 240 | split_data['files'][camera_index][split_index] = camera_intrinsics[set_idx][camera_key] 241 | 242 | return split_data_dict 243 | 244 | def _adapt_splits(self, split_names, scaled_path, camera_intrinsics): 245 | """ 246 | Writes new camera intrinsics into every given split data, creates a new folder for the new version based 247 | on the path of the scaled dataset and saves the new json files. 248 | 249 | :param split_names: List containing all split names that are supposed to be adapted 250 | :param scaled_path: Path of the scaled dataset 251 | :param camera_intrinsics: dict of camera intrinsics that will be written into the split json files. It has the 252 | form {1: 'camera': camera_matrix, 'camera_right': camera_matrix, 253 | 2: ...} 254 | :return: 255 | """ 256 | for split_name in split_names: 257 | split_path = self.dataset_path + '_' + split_name 258 | scaled_split_path = scaled_path + '_' + split_name 259 | split_data_dict = self._load_split_data(split_path) 260 | if not os.path.isdir(scaled_split_path): 261 | os.mkdir(scaled_split_path) 262 | else: 263 | print('{} will not be overwritten!', format(scaled_split_path)) 264 | 265 | split_data_dict = self._adapt_camera_intrinsics_in_split_file(split_data_dict, camera_intrinsics) 266 | for split, split_data in split_data_dict.items(): 267 | with open(os.path.join(scaled_split_path, split + '.json'), 'w') as fd: 268 | json.dump(split_data, fd) 269 | 270 | def process(self, new_dataset_name, output_size=None, scale_factor=None, keys_to_convert=(), splits_to_adapt=None): 271 | """ 272 | Scales every image in the dataset and saves them in the specified output folder. Also creates new json files 273 | with adapted camera intrinsics. One can define either a scale factor or a desired output size. 274 | 275 | :param new_dataset_name: name of the desired output folder. It is forbidden to use an existing folder 276 | :param output_size: target output size as a 2-tuple (h, w) 277 | :param scale_factor: Both dimensions gets scaled by this factor 278 | :param keys_to_convert: A tuple of keys, only the images behind these keys will be converted (optional) 279 | :param splits_to_adapt: Splits in seperate folders that will also be copied into a new folder and have their 280 | camera parameters adapted (optional) 281 | """ 282 | 283 | assert self.dataset != new_dataset_name 284 | 285 | new_path = self._gen_dataset_path(new_dataset_name) 286 | assert not os.path.isdir(new_path), 'You are not allowed to write into an existing dataset folder!' 287 | if scale_factor is not None: 288 | assert output_size is None 289 | assert isinstance(scale_factor, int) 290 | 291 | scale_mode = 'relative' 292 | 293 | elif output_size is not None: 294 | assert scale_factor is None 295 | assert isinstance(output_size, tuple) 296 | 297 | scale_mode = 'absolute' 298 | 299 | resizer = mytransforms.Resize(output_size=output_size) 300 | if type(splits_to_adapt) == str: 301 | splits_to_adapt = (splits_to_adapt,) 302 | 303 | camera_intrinsics = {} 304 | pending_writes = [] 305 | 306 | # Scale and save the images 307 | with Pool(processes=NUM_WORKERS) as pool_rd, Pool(processes=NUM_WORKERS) as pool_wr: 308 | for set_idx, sample, paths in self._print_progress(self._get_samples(pool_rd, keys_to_convert)): 309 | if scale_mode == 'relative': 310 | width, height = sample[('color', 0, 0)].size 311 | new_size = ( 312 | int(height / scale_factor), 313 | int(width / scale_factor) 314 | ) 315 | resizer = mytransforms.Resize(output_size=new_size) 316 | sample = resizer(sample) 317 | 318 | for key in sample: 319 | if key in paths: 320 | image = sample[key] 321 | new_filepath = os.path.join(new_path, paths[key]) 322 | os.makedirs(os.path.split(new_filepath)[0], exist_ok=True) 323 | args = (image, key[0], self.dataset, new_filepath) 324 | job = pool_wr.apply_async(save_image_file, args) 325 | pending_writes.append(job) 326 | elif key[0] in CAMERA_KEYS: 327 | if set_idx not in camera_intrinsics: 328 | camera_intrinsics[set_idx] = {} 329 | camera_intrinsics[set_idx][key[0]] = sample[key].tolist() 330 | # Limit the number of pending write operations 331 | # by waiting for old ones to complete 332 | while len(pending_writes) > NUM_WORKERS: 333 | pending_writes.pop(0).get() 334 | 335 | while pending_writes: 336 | pending_writes.pop(0).get() 337 | 338 | # Modify the json data and safe the new json files 339 | with open(os.path.join(self.dataset_path, 'basic_files.json')) as fd: 340 | basic_json_data = json.load(fd) 341 | names = basic_json_data['names'] 342 | positions = basic_json_data['positions'] 343 | for set_idx in camera_intrinsics: 344 | for camera_key in camera_intrinsics[set_idx]: 345 | camera_index = names.index(camera_key) 346 | basic_index = self._get_index_from_position(positions[camera_index], set_idx) 347 | basic_json_data['numerical_values'][camera_index][basic_index] = camera_intrinsics[set_idx][camera_key] 348 | 349 | with open(os.path.join(new_path, 'basic_files.json'), 'w') as fd: 350 | json.dump(basic_json_data, fd) 351 | 352 | # Modify the train, val, test.json, if present 353 | split_data_dict = self._load_split_data() 354 | split_data_dict = self._adapt_camera_intrinsics_in_split_file(split_data_dict, camera_intrinsics) 355 | for split, split_data in split_data_dict.items(): 356 | with open(os.path.join(new_path, split + '.json'), 'w') as fd: 357 | json.dump(split_data, fd) 358 | 359 | # Copy the parameters.json into the new path, adapt the split list 360 | with open(os.path.join(self.dataset_path, 'parameters.json')) as fd: 361 | parameters = json.load(fd) 362 | parameters['splits'] = splits_to_adapt 363 | with open(os.path.join(new_path, 'parameters.json'), 'w') as fd: 364 | json.dump(parameters, fd) 365 | 366 | # If there are any separate split folders given, adapt them too. 367 | if splits_to_adapt is not None: 368 | self._adapt_splits(splits_to_adapt, new_path, camera_intrinsics) 369 | 370 | def adapt_splits(self, scaled_dataset_name, split_names): 371 | """ 372 | Adapts the json files from the given splits to an already scaled dataset. This means that a new split folder 373 | is created and the camera parameters in the json files are adapted. 374 | 375 | :param scaled_dataset_name: Name of an existing scaled dataset 376 | :param split_names: List containing all split names that are supposed to be adapted 377 | """ 378 | if type(split_names) == str: 379 | split_names = [split_names] 380 | scaled_path = self._gen_dataset_path(scaled_dataset_name) 381 | with open(os.path.join(scaled_path, 'basic_files.json')) as fd: 382 | basic_json_data_scaled = json.load(fd) 383 | names = basic_json_data_scaled['names'] 384 | positions = basic_json_data_scaled['positions'] 385 | numerical_values = basic_json_data_scaled['numerical_values'] 386 | camera_intrinsics = {} 387 | for name, position, numerics in zip(names, positions, numerical_values): 388 | if name not in CAMERA_KEYS: 389 | continue 390 | for (set_idx, _, _, _), camera_intrinsic in zip(position, numerics): 391 | if set_idx not in camera_intrinsics: 392 | camera_intrinsics[set_idx] = {} 393 | camera_intrinsics[set_idx][name] = camera_intrinsic 394 | 395 | with open(os.path.join(scaled_path, 'parameters.json')) as fd: 396 | parameters = json.load(fd) 397 | if parameters['splits'] is None: 398 | parameters['splits'] = [] 399 | parameters['splits'].extend(split_names) 400 | with open(os.path.join(scaled_path, 'parameters.json'), 'w') as fd: 401 | json.dump(parameters, fd) 402 | 403 | self._adapt_splits(split_names, scaled_path, camera_intrinsics) 404 | 405 | 406 | if __name__ == '__main__': 407 | # To create a scaled, version of an existing dataset, execute something like 408 | # scaler = DatasetScaler('unscaled_dataset') 409 | # scaler.process('scaled_dataset', scale_factor=2) 410 | # To adapt split folders to this dataset, use the paramter splits_to adapt. If splits shall be adapted to an 411 | # existing scaled dataset, execute something like 412 | # scaler = DatasetScaler('unscaled_dataset') 413 | # scaler.adapt_splits('scaled_dataset', split_names=('split_1', 'split_2')) 414 | scaler = DatasetScaler('bdd100k') 415 | scaler.process('bdd100k_mross_scaled', scale_factor=2) 416 | -------------------------------------------------------------------------------- /dataloader/file_io/trainid_converter.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | import cv2 5 | import torchvision.transforms as transforms 6 | import PIL.Image as pil 7 | import numpy as np 8 | from multiprocessing import Pool 9 | 10 | import dataloader.file_io.get_path as gp 11 | import dataloader.pt_data_loader.mytransforms as mytransforms 12 | import dataloader.definitions.labels_file as lf 13 | 14 | SEGMENTATION_KEYS = ('segmentation', 'segmentation_right') 15 | SPLIT_NAMES = ('train', 'validation', 'test') 16 | NEW_FOLDER_NAME = 'segmentation_trainid' 17 | BASIC_FILES_KEYS = ('names', 'types', 'filters', 'folders', 'files', 'positions', 'numerical_values') 18 | TVT_KEYS = ('names', 'types', 'folders', 'files', 'positions') 19 | JSON_NAMES = {'basic_files': 'basic_files.json', 'train': 'train.json', 'validation': 'validation.json', 20 | 'test': 'test.json'} 21 | NUM_WORKERS = 4 22 | 23 | 24 | def insert_into_json_dict(json_dict, items, pos=None): 25 | """ 26 | Inserts items into a dictionary that has the standard format for dataset json files 27 | 28 | :param json_dict: dict into which an item is to be inserted 29 | :param pos: position where the new item is to be placed 30 | :param items: dictionary containing an item for every key in dict 31 | :return: dict with the additional item in position pos in every list in the dict 32 | """ 33 | keys = tuple(json_dict.keys()) 34 | assert sorted(keys) in (sorted(BASIC_FILES_KEYS), sorted(TVT_KEYS)), \ 35 | 'Not a valid dictionary: Keys {} given, but {} or {} expected'.format(keys, BASIC_FILES_KEYS, TVT_KEYS) 36 | assert sorted(tuple(items.keys())) == sorted(keys), 'Given items dictionary does not have fitting keys' 37 | for key in keys: 38 | json_dict[key].insert(pos, items[key]) 39 | return json_dict 40 | 41 | 42 | def remove_from_json_dict(json_dict, pos=None, name=None): 43 | """ 44 | Removes items from a dictionary that has the standard format for dataset json files 45 | 46 | :param json_dict: dict from which an item will be removed 47 | :param pos: position of the item in every list 48 | :param name: alternative for position. Gets the position from the name entry. 49 | :return: dict with the specified entry removed in every list in the dict 50 | """ 51 | keys = tuple(json_dict.keys()) 52 | assert sorted(keys) in (sorted(BASIC_FILES_KEYS), sorted(TVT_KEYS)), 'Not a valid dictionary' 53 | assert (pos == None or name == None) and not (pos == None and name == None) 54 | if name is not None: 55 | pos = json_dict['names'].index(name) 56 | for key in keys: 57 | json_dict[key].pop(pos) 58 | return json_dict 59 | 60 | 61 | class TrainIDConverter(object): 62 | def __init__(self, dataset, labels, labels_mode, split=None): 63 | self.dataset = dataset 64 | self.dataset_path = self._gen_dataset_path(dataset) 65 | self.labels = labels 66 | self.labels_mode = labels_mode 67 | if split is not None: 68 | self.split_path = self.dataset_path + '_' + split 69 | else: 70 | self.split_path = self.dataset_path 71 | 72 | def _gen_dataset_path(self, dataset): 73 | path_getter = gp.GetPath() 74 | dataset_folder = path_getter.get_data_path() 75 | 76 | return os.path.join(dataset_folder, dataset) 77 | 78 | def _parse_json_file(self, path): 79 | """ 80 | Generates a dictionary of samples from the json file in the given path 81 | 82 | :param path: Path to a json file 83 | :return: A dictionary of the form {set_index: {'segmentation': path}, set_index_2: {'segmentation': path}, ...} 84 | """ 85 | with open(path) as fd: 86 | json_data = json.load(fd) 87 | 88 | names = json_data['names'] 89 | files = json_data['files'] 90 | positions = json_data['positions'] 91 | numerical_values = json_data['numerical_values'] 92 | samples = dict() 93 | 94 | for name, filenames, position, in zip(names, files, positions): 95 | if name not in SEGMENTATION_KEYS: 96 | continue 97 | 98 | for (set_idx, _, _, _), filename, i in zip(position, filenames, range(len(filenames))): 99 | if set_idx not in samples: 100 | samples[set_idx] = dict() 101 | samples[set_idx]['dataset'] = self.dataset 102 | samples[set_idx]['dataset_path'] = self.dataset_path 103 | samples[set_idx][name] = filename 104 | 105 | # >>> samples[30004]['segmentation'] # layout example 106 | # 'Segmentation/2011_09_30/2011_09_30_drive_0028_sync/image_02/data/0000001324.png' 107 | 108 | return samples 109 | 110 | def _load_sample(self, si): 111 | """ 112 | Loads the specified sample. 113 | 114 | :param si: dict of the form {global_index: {"color": path, "depth": path, ...}} 115 | :return: - the global index of the sample in the dataset 116 | - the sample in the standard dataloader format 117 | - a path dictionary that is built similar to the sample dictionary and contains the image path instead of 118 | an image object 119 | """ 120 | set_idx, sample = si 121 | 122 | dataset = sample.pop('dataset') 123 | dataset_path = sample.pop('dataset_path') 124 | 125 | new_sample = dict() 126 | paths = dict() 127 | 128 | for key, content in sample.items(): 129 | assert any(key.startswith(s) for s in SEGMENTATION_KEYS) 130 | filepath = os.path.join(dataset_path, content) 131 | filepath = filepath.replace('/', os.sep) 132 | filepath = filepath.replace('\\', os.sep) 133 | image = cv2.imread(filepath, -1) 134 | new_sample[key, 0, 0] = image 135 | paths[key, 0, 0] = content 136 | 137 | # By using the transform ConvertSegmentation, the segmentation images will be converted to the train_id format 138 | load_transforms = transforms.Compose([ 139 | mytransforms.LoadSegmentation(), 140 | mytransforms.ConvertSegmentation(labels=self.labels, labels_mode=self.labels_mode) 141 | ]) 142 | new_sample = load_transforms(new_sample) 143 | 144 | return set_idx, new_sample, paths 145 | 146 | def _save_image_file(self, image, path): 147 | """ 148 | Brings the image in the right format to be saved. 149 | 150 | :param image: PIL image 151 | :param path: path of the destination directory 152 | """ 153 | 154 | image = np.array(image, dtype=np.uint8) 155 | cv2.imwrite(path, image) 156 | 157 | def _get_samples(self, pool): 158 | json_path = os.path.join(self.dataset_path, JSON_NAMES['basic_files']) 159 | samples = self._parse_json_file(json_path) 160 | 161 | return pool.imap_unordered(self._load_sample, samples.items()) 162 | 163 | def _print_progress(self, iterator): 164 | for i, elem in enumerate(iterator): 165 | if i % 100 == 0: 166 | print('', flush=True) 167 | 168 | print('.', end='') 169 | 170 | yield elem 171 | 172 | def _load_split_data(self, split_path=None): 173 | """ 174 | 175 | :param split_path: Path where the split is stored. Standard is self.split_path which is the same as 176 | self.dataset_path if not specified otherwise when calling the __init__() 177 | :return: A dictionary containing all split dictionaries 178 | """ 179 | if split_path is None: 180 | split_path = self.split_path 181 | split_data = {} 182 | for split in SPLIT_NAMES: 183 | path = os.path.join(split_path, JSON_NAMES[split]) 184 | try: 185 | with open(path) as fd: 186 | split_json_data = json.load(fd) 187 | split_data[split] = split_json_data 188 | except: 189 | print(split_path) 190 | print('No {} data accessible'.format(split)) 191 | return split_data 192 | 193 | def _get_index_from_position(self, position_array, pos_0): 194 | """ 195 | Returns the index of the position tuple with first entry pos_0 in the pos_array 196 | 197 | :param position_array: An array built out of 4-tuples in the standard positions format. 198 | :param pos_0: Global position index (first entry of the 4-tuple) to search for 199 | :return: index of the tuple in pos_array that begins with pos_0. If none is found, None is returned. 200 | """ 201 | for i, position in zip(range(len(position_array)), position_array): 202 | if position[0] == pos_0: 203 | return i 204 | return None 205 | 206 | def _add_new_segmentation_to_split_file(self, split_data_dict, segmentation_keys): 207 | """ 208 | Duplicates every segmentation entry to a segmentation_trainid entry with the modified file paths 209 | 210 | :param split_data_dict: split dictionary as saved in the json file 211 | :param segmentation_keys: list of segmentation keys that will be dupilicated 212 | :return: The split_data_dict with the additional entries 213 | """ 214 | for split, split_data in split_data_dict.items(): 215 | # If there are already trainid entries in the basic_files.json, remove them 216 | for seg_name in segmentation_keys: 217 | new_seg_name = seg_name + '_trainid' 218 | if new_seg_name in split_data['names']: 219 | basic_json_data = remove_from_json_dict(split_data, name=new_seg_name) 220 | 221 | # Find the index after the last segmentation entry. This is the index where the new entries will be inserted 222 | first_seg_key_found = False 223 | for i, name in zip(range(len(split_data['names'])), split_data['names']): 224 | if 'segmentation' in name: 225 | first_seg_key_found = True 226 | new_seg_index = i + 1 227 | elif first_seg_key_found and 'segmentation' not in name: 228 | new_seg_index = i 229 | break 230 | assert first_seg_key_found, "No segmentation keys have been found in the basic_files.json" 231 | 232 | # Insert the trainid entries into the json list 233 | segmentation_keys.reverse() 234 | for seg_name in segmentation_keys: 235 | new_entry = {} 236 | original_seg_index = split_data['names'].index(seg_name) 237 | new_seg_name = seg_name + '_trainid' 238 | new_entry['names'] = new_seg_name 239 | for key in ('types', 'positions', 'files', 'folders'): 240 | old_entry = split_data[key][original_seg_index] 241 | if type(old_entry) == list: 242 | new_entry[key] = old_entry.copy() 243 | else: 244 | new_entry[key] = old_entry 245 | for i in range(len(new_entry['files'])): 246 | old_path = new_entry['files'][i] 247 | new_entry['files'][i] = os.path.join(NEW_FOLDER_NAME, old_path) 248 | for i in range(len(new_entry['folders'])): 249 | old_path = new_entry['folders'][i] 250 | new_entry['folders'][i] = os.path.join(NEW_FOLDER_NAME, old_path) 251 | basic_json_data = insert_into_json_dict(split_data, new_entry, new_seg_index) 252 | return split_data_dict 253 | 254 | def _adapt_splits(self, split_names, segmentation_keys): 255 | """ 256 | Writes the new segmentation data into every given split. 257 | 258 | :param split_names: List containing all split names that are supposed to be adapted 259 | :param basic_data: 260 | :return: 261 | """ 262 | for split_name in split_names: 263 | split_path = self.dataset_path + '_' + split_name 264 | split_data_dict = self._load_split_data(split_path) 265 | split_data_dict = self._add_new_segmentation_to_split_file(split_data_dict, segmentation_keys) 266 | for split, split_data in split_data_dict.items(): 267 | with open(os.path.join(split_path, JSON_NAMES[split]), 'w') as fd: 268 | json.dump(split_data, fd) 269 | 270 | def _adapt_json_files(self, splits_to_adapt=None, segmentation_keys=None): 271 | """ 272 | Adds the new trainid segmentation images to the json files as new entries with the suffix _trainid 273 | 274 | :param splits_to_adapt: Splits in seperate folders that will have the segmentation_trainid entry copied into 275 | their json files 276 | :param segmentation_keys: Segmentation names which will be copied to a new trainid entry. Default are all 277 | segmentation names in the basic_files 278 | """ 279 | # Load the basic files json data 280 | with open(os.path.join(self.dataset_path, JSON_NAMES['basic_files'])) as fd: 281 | basic_json_data = json.load(fd) 282 | names = basic_json_data['names'] 283 | if segmentation_keys == None: 284 | segmentation_keys = [] 285 | for name in names: 286 | if 'segmentation' in name: 287 | segmentation_keys.append(name) 288 | 289 | # If there are already trainid entries in the basic_files.json, remove them 290 | for seg_name in segmentation_keys: 291 | new_seg_name = seg_name + '_trainid' 292 | if new_seg_name in basic_json_data['names']: 293 | basic_json_data = remove_from_json_dict(basic_json_data, name=new_seg_name) 294 | segmentation_keys.remove(new_seg_name) 295 | 296 | # Find the index where the trainid entries will be inserted 297 | first_seg_key_found = False 298 | for i, name in zip(range(len(names)), names): 299 | if 'segmentation' in name: 300 | first_seg_key_found = True 301 | new_seg_index = i+1 302 | elif first_seg_key_found and 'segmentation' not in name: 303 | new_seg_index = i 304 | break 305 | assert first_seg_key_found, "No segmentation keys have been found in the basic_files.json" 306 | 307 | # Insert the trainid entries into the json list 308 | segmentation_keys.reverse() 309 | for seg_name in segmentation_keys: 310 | new_entry = {} 311 | original_seg_index = basic_json_data['names'].index(seg_name) 312 | new_seg_name = seg_name + '_trainid' 313 | new_entry['names'] = new_seg_name 314 | for key in ('types', 'positions', 'numerical_values', 'filters', 'files', 'folders'): 315 | old_entry = basic_json_data[key][original_seg_index] 316 | if type(old_entry) == list: 317 | new_entry[key] = old_entry.copy() 318 | elif key == 'filters': 319 | new_entry[key] = [old_entry] 320 | else: 321 | new_entry[key] = old_entry 322 | 323 | new_entry['filters'].append(NEW_FOLDER_NAME) 324 | for i in range(len(new_entry['files'])): 325 | old_path = new_entry['files'][i] 326 | new_entry['files'][i] = os.path.join(NEW_FOLDER_NAME, old_path) 327 | for i in range(len(new_entry['folders'])): 328 | old_path = new_entry['folders'][i] 329 | new_entry['folders'][i] = os.path.join(NEW_FOLDER_NAME, old_path) 330 | basic_json_data = insert_into_json_dict(basic_json_data, new_entry, new_seg_index) 331 | 332 | with open(os.path.join(self.dataset_path, JSON_NAMES['basic_files']), 'w') as fd: 333 | json.dump(basic_json_data, fd) 334 | 335 | # Modify the train, val, test.json, if present 336 | split_data_dict = self._load_split_data() 337 | split_data_dict = self._add_new_segmentation_to_split_file(split_data_dict, segmentation_keys) 338 | for split, split_data in split_data_dict.items(): 339 | with open(os.path.join(self.dataset_path, JSON_NAMES[split]), 'w') as fd: 340 | json.dump(split_data, fd) 341 | 342 | # If there are any separate split folders given, adapt them too. 343 | if splits_to_adapt is not None: 344 | self._adapt_splits(splits_to_adapt, segmentation_keys) 345 | 346 | def adapt_json_files(self, splits_to_adapt=None): 347 | """ 348 | This function is meant for the case that the scaled segmentation files have already been created but the json 349 | files have been altered by the filelist creator, which does remove the segmentation_trainid entries. Using this 350 | function, the segmentation_trainid entries will be restored. If there is no segmentation_trainid Folder, this 351 | function will have no effect 352 | 353 | :param splits_to_adapt: Splits in seperate folders that will have the segmentation_trainid entry copied into 354 | their json files 355 | """ 356 | if not os.path.isdir(os.path.join(self.dataset_path, NEW_FOLDER_NAME)): 357 | print('No segmentation_trainid folder found in the dataset directory') 358 | return 359 | self._adapt_json_files(splits_to_adapt) 360 | 361 | def process(self, splits_to_adapt=None): 362 | """ 363 | Converts all segmentation images in the dataset to train_ids and saves them into a new folder in the dataset 364 | root directory. The new segmentation images will be added to the json_files as a new entry 365 | "segmentation_trainid" 366 | 367 | :param splits_to_adapt: Splits in seperate folders that will have the segmentation_trainid entry copied into 368 | their json files 369 | """ 370 | 371 | new_path = os.path.join(self.dataset_path, NEW_FOLDER_NAME) 372 | 373 | if type(splits_to_adapt) == str: 374 | splits_to_adapt = (splits_to_adapt,) 375 | 376 | camera_intrinsics = {} 377 | pending_writes = [] 378 | segmentation_keys = [] 379 | 380 | # Save the images 381 | with Pool(processes=NUM_WORKERS) as pool_rd, Pool(processes=NUM_WORKERS) as pool_wr: 382 | for set_idx, sample, paths in self._print_progress(self._get_samples(pool_rd)): 383 | for key in sample: 384 | if key[0] not in segmentation_keys: 385 | segmentation_keys.append(key[0]) 386 | image = sample[key] 387 | new_filepath = os.path.join(new_path, paths[key]) 388 | os.makedirs(os.path.split(new_filepath)[0], exist_ok=True) 389 | args = (image, new_filepath) 390 | job = pool_wr.apply_async(self._save_image_file, args) 391 | pending_writes.append(job) 392 | # Limit the number of pending write operations 393 | # by waiting for old ones to complete 394 | while len(pending_writes) > NUM_WORKERS: 395 | pending_writes.pop(0).get() 396 | 397 | while pending_writes: 398 | pending_writes.pop(0).get() 399 | self._adapt_json_files(splits_to_adapt, segmentation_keys) 400 | 401 | 402 | if __name__ == '__main__': 403 | pass 404 | # Example workflow: 405 | # To create a new folder containing the segmentation images encoded as train_ids, perform 406 | # converter = TrainIDConverter('cityscapes', labels=lf.labels_cityscape_seg.getlabels(), labels_mode='fromid') 407 | # converter.process() 408 | # 409 | # If you have already created the train_id images but have to update the json files, use 410 | # converter.adapt_json_files() 411 | 412 | # converter = TrainIDConverter('cityscapes', labels=lf.labels_cityscape_seg.getlabels(), labels_mode='fromid') 413 | # converter.process() 414 | # converter.adapt_json_files() --------------------------------------------------------------------------------