From 783a3bba1832a5c84c2734b54ccccdd687c17fae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Su=C5=82ek?= Date: Sun, 6 Aug 2023 21:08:57 +0200 Subject: [PATCH 1/7] Add Pandaset class and config --- ml3d/configs/randlanet_pandaset.yml | 47 +++++ ml3d/datasets/pandaset.py | 272 ++++++++++++++++++++++++++++ 2 files changed, 319 insertions(+) create mode 100644 ml3d/configs/randlanet_pandaset.yml create mode 100644 ml3d/datasets/pandaset.py diff --git a/ml3d/configs/randlanet_pandaset.yml b/ml3d/configs/randlanet_pandaset.yml new file mode 100644 index 000000000..46927aba4 --- /dev/null +++ b/ml3d/configs/randlanet_pandaset.yml @@ -0,0 +1,47 @@ +dataset: + name: Pandaset + dataset_path: path_to_dataset + cache_dir: ./logs/cache + test_result_folder: './logs/test' + training_split: [ '001', '002', '003', '005', '011', '013', '015', '016', + '017', '019', '021', '023', '024', '027', '028', '029', + '030', '032', '033', '034', '035', '037', '038', '039', + '040', '041', '042', '043', '044', '046', '052', '053', + '054', '056', '057', '058', '064', '065', '066', '067', + '070', '071', '072', '073', '077', '078', '080', '084', + '088', '089', '090', '094', '095', '097', '098', '101', + '102', '103', '105', '106', '109', '110', '112', '113' + ] + test_split: ['115', '116', '117', '119', '120', '124', '139', '149', '158'] + validation_split: ['122', '123'] + use_cache: true + sampler: + name: 'SemSegRandomSampler' +model: + name: RandLANet + batcher: DefaultBatcher + num_classes: 39 + num_points: 81920 + num_neighbors: 16 + framework: torch + num_layers: 4 + ignored_label_inds: [0] + sub_sampling_ratio: [4, 4, 4, 4] + in_channels: 3 + dim_features: 8 + dim_output: [16, 64, 128, 256] + grid_size: 0.06 +pipeline: + name: SemanticSegmentation + max_epoch: 50 + save_ckpt_freq: 5 + device: gpu + optimizer: + lr: 0.001 + batch_size: 4 + main_log_dir: './logs' + logs_dir: './logs' + scheduler_gamma: 0.9886 + test_batch_size: 2 + train_sum_dir: './logs/training_log' + val_batch_size: 2 diff --git a/ml3d/datasets/pandaset.py b/ml3d/datasets/pandaset.py new file mode 100644 index 000000000..6037fc816 --- /dev/null +++ b/ml3d/datasets/pandaset.py @@ -0,0 +1,272 @@ +import os +from os.path import join +import numpy as np +import pandas as pd +from pathlib import Path +import logging + +from .base_dataset import BaseDataset, BaseDatasetSplit +from ..utils import make_dir, DATASET + +log = logging.getLogger(__name__) + +class Pandaset(BaseDataset): + """ This class is used to create a dataset based on the Pandaset autonomous + driving dataset. + + https://pandaset.org/ + + The dataset includes 42 semantic classes and covers more than 100 scenes, + each of which is 8 seconds long. + + """ + def __init__(self, + dataset_path, + name="Pandaset", + cache_dir="./logs/cache", + use_cache=False, + ignored_label_inds=[], + test_result_folder='./logs/test_log', + test_split=['115', '116', '117', '119', '120', '124', '139', '149', '158'], + training_split=[ + '001', '002', '003', '005', '011', '013', '015', '016', + '017', '019', '021', '023', '024', '027', '028', '029', + '030', '032', '033', '034', '035', '037', '038', '039', + '040', '041', '042', '043', '044', '046', '052', '053', + '054', '056', '057', '058', '064', '065', '066', '067', + '070', '071', '072', '073', '077', '078', '080', '084', + '088', '089', '090', '094', '095', '097', '098', '101', + '102', '103', '105', '106', '109', '110', '112', '113' + ], + validation_split=['122', '123'], + all_split=['001', '002', '003', '005', '011', '013', '015', '016', + '017', '019', '021', '023', '024', '027', '028', '029', + '030', '032', '033', '034', '035', '037', '038', '039', + '040', '041', '042', '043', '044', '046', '052', '053', + '054', '056', '057', '058', '064', '065', '066', '067', + '069', '070', '071', '072', '073', '077', '078', '080', + '084', '088', '089', '090', '094', '095', '097', '098', + '101', '102', '103', '105', '106', '109', '110', '112', + '113', '115', '116', '117', '119', '120', '122', '123', + '124', '139', '149', '158'], + **kwargs): + + """Initialize the function by passing the dataset and other details. + + Args: + dataset_path: The path to the dataset to use. + name: The name of the dataset. + cache_dir: The directory where the cache is stored. + use_cache: Indicates if the dataset should be cached. + ignored_label_inds: A list of labels that should be ignored in the dataset. + Returns: + class: The corresponding class. + """ + super().__init__(dataset_path=dataset_path, + name=name, + cache_dir=cache_dir, + use_cache=use_cache, + ignored_label_inds=ignored_label_inds, + test_result_folder=test_result_folder, + test_split=test_split, + training_split=training_split, + validation_split=validation_split, + all_split=all_split, + **kwargs) + + cfg = self.cfg + + self.label_to_names = self.get_label_to_names() + self.num_classes = len(self.label_to_names) + self.label_values = np.sort([k for k, v in self.label_to_names.items()]) + + @staticmethod + def get_label_to_names(): + """Returns a label to names dictionary object. + + Returns: + A dict where keys are label numbers and + values are the corresponding names. + """ + label_to_names = { + 1: "Reflection", + 2: "Vegetation", + 3: "Ground", + 4: "Road", + 5: "Lane Line Marking", + 6: "Stop Line Marking", + 7: "Other Road Marking", + 8: "Sidewalk", + 9: "Driveway", + 10: "Car", + 11: "Pickup Truck", + 12: "Medium-sized Truck", + 13: "Semi-truck", + 14: "Towed Object", + 15: "Motorcycle", + 16: "Other Vehicle - Construction Vehicle", + 17: "Other Vehicle - Uncommon", + 18: "Other Vehicle - Pedicab", + 19: "Emergency Vehicle", + 20: "Bus", + 21: "Personal Mobility Device", + 22: "Motorized Scooter", + 23: "Bicycle", + 24: "Train", + 25: "Trolley", + 26: "Tram / Subway", + 27: "Pedestrian", + 28: "Pedestrian with Object", + 29: "Animals - Bird", + 30: "Animals - Other", + 31: "Pylons", + 32: "Road Barriers", + 33: "Signs", + 34: "Cones", + 35: "Construction Signs", + 36: "Temporary Construction Barriers", + 37: "Rolling Containers", + 38: "Building", + 39: "Other Static Object" + } + return label_to_names + + def get_split(self, split): + """Returns a dataset split. + + Args: + split: A string identifying the dataset split that is usually one of + 'training', 'test', or 'all'. + + Returns: + A dataset split object providing the requested subset of the data. + """ + return PandasetSplit(self, split=split) + + def get_split_list(self, split): + """Returns the list of data splits available. + + Args: + split: A string identifying the dataset split that is usually one of + 'training', 'test', 'validation', or 'all'. + + Returns: + A dataset split object providing the requested subset of the data. + + Raises: + ValueError: Indicates that the split name passed is incorrect. The split name should be one of + 'training', 'test', 'validation', or 'all'. + """ + cfg = self.cfg + dataset_path = cfg.dataset_path + file_list = [] + + if split in ['train', 'training']: + seq_list = cfg.training_split + elif split in ['test', 'testing']: + seq_list = cfg.test_split + elif split in ['val', 'validation']: + seq_list = cfg.validation_split + elif split in ['all']: + seq_list = cfg.all_split + else: + raise ValueError("Invalid split {}".format(split)) + + for seq_id in seq_list: + pc_path = join(dataset_path, seq_id, 'lidar') + for f in np.sort(os.listdir(pc_path)): + if f.split('.')[-1] == 'gz': + file_list.append(join(pc_path, f)) + + return file_list + + def is_tested(self, attr): + """Checks if a datum in the dataset has been tested. + + Args: + dataset: The current dataset to which the datum belongs to. + attr: The attribute that needs to be checked. + + Returns: + If the dataum attribute is tested, then return the path where the + attribute is stored; else, returns false. + """ + pass + + def save_test_result(self, results, attr): + """Saves the output of a model. + + Args: + results: The output of a model for the datum associated with the + attribute passed. + attrs: The attributes that correspond to the outputs passed in + results. + """ + cfg = self.cfg + pred = results['predict_labels'] + name = attr['name'] + + test_path = join(cfg.test_result_folder, 'sequences') + make_dir(test_path) + save_path = join(test_path, name, 'predictions') + make_dir(save_path) + pred = results['predict_labels'] + + for ign in cfg.ignored_label_inds: + pred[pred >= ign] += 1 + + store_path = join(save_path, name + '.label') + + pred = pred.astype(np.uint32) + pred.tofile(store_path) + + +class PandasetSplit(BaseDatasetSplit): + """This class is used to create a split for Pandaset dataset. + + Args: + dataset: The dataset to split. + split: A string identifying the dataset split that is usually one of + 'training', 'test', 'validation', or 'all'. + **kwargs: The configuration of the model as keyword arguments. + + Returns: + A dataset split object providing the requested subset of the data. + """ + def __init__(self, dataset, split='train'): + super().__init__(dataset, split=split) + log.info("Found {} pointclouds for {}".format(len(self.path_list), + split)) + + def __len__(self): + return len(self.path_list) + + def get_data(self, idx): + pc_path = self.path_list[idx] + label_path = pc_path.replace('lidar', 'annotations/semseg') + + points = pd.read_pickle(pc_path) + labels = pd.read_pickle(label_path) + + intensity = points['i'].to_numpy().astype(np.float32) + points = points.drop(columns=['i', 't', 'd']).to_numpy().astype(np.float32) + labels = labels.to_numpy().astype(np.int32) + + data = { + 'point': points, + 'intensity': intensity, + 'label': labels + } + + return data + + def get_attr(self, idx): + pc_path = self.path_list[idx] + value = (pc_path).split('/')[9] + name = Path(pc_path).name.split('.')[0] + name = value + '_' + name + + attr = {'name': name, 'path': pc_path, 'split': self.split} + return attr + +DATASET._register_module(Pandaset) From 200085ee4414823872423ad24f26dd8d1e19e27d Mon Sep 17 00:00:00 2001 From: Manon Cortial <118278101+ManonCortial@users.noreply.github.com> Date: Sat, 7 Oct 2023 06:41:52 +0200 Subject: [PATCH 2/7] Avoid renaming unwanted part of the path in dataset loading scripts (#597) In kitti.py, in get_data method of KITTISplit class, paths are created by renaming some parts of other paths (like replacing 'velodyne' by 'label_2'. This can create problems if you work on full path (i.e. if you have a velodyne directory in the path). Same issues fixed in matterport_objects.py and waymo.py Co-authored-by: Marius U <44391510+mariusud@users.noreply.github.com> Co-authored-by: Benjamin Ummenhofer Co-authored-by: Sameer Sheorey --- ml3d/datasets/kitti.py | 8 +++++--- ml3d/datasets/matterport_objects.py | 3 ++- ml3d/datasets/waymo.py | 6 +++--- ml3d/torch/models/kpconv.py | 6 ++---- ml3d/torch/models/point_transformer.py | 5 ++--- ml3d/torch/models/pvcnn.py | 5 ++--- ml3d/torch/models/randlanet.py | 9 +++------ ml3d/torch/pipelines/semantic_segmentation.py | 19 +++++++++---------- requirements-tensorflow.txt | 3 ++- 9 files changed, 30 insertions(+), 34 deletions(-) diff --git a/ml3d/datasets/kitti.py b/ml3d/datasets/kitti.py index feb8af81f..63e90ad5b 100644 --- a/ml3d/datasets/kitti.py +++ b/ml3d/datasets/kitti.py @@ -266,9 +266,11 @@ def __len__(self): def get_data(self, idx): pc_path = self.path_list[idx] - label_path = pc_path.replace('velodyne', - 'label_2').replace('.bin', '.txt') - calib_path = label_path.replace('label_2', 'calib') + + # Replace the last instance of "velodyne" in the path by label_2, and the '.bin' by '.txt' + label_path = ("label_2".join(pc_path.rsplit("velodyne", 1))).replace( + '.bin', '.txt') + calib_path = "calib".join(label_path.rsplit("label_2", 1)) pc = self.dataset.read_lidar(pc_path) calib = self.dataset.read_calib(calib_path) diff --git a/ml3d/datasets/matterport_objects.py b/ml3d/datasets/matterport_objects.py index 025e8432a..1f8981de2 100644 --- a/ml3d/datasets/matterport_objects.py +++ b/ml3d/datasets/matterport_objects.py @@ -262,7 +262,8 @@ def __len__(self): def get_data(self, idx): pc_path = self.path_list[idx] - label_path = pc_path.replace('pc', 'boxes').replace('.bin', '.txt') + label_path = ("boxes".join(pc_path.rsplit("pc", + 1))).replace('.bin', '.txt') pc = self.dataset.read_lidar(pc_path) label = self.dataset.read_label(label_path) diff --git a/ml3d/datasets/waymo.py b/ml3d/datasets/waymo.py index 1dd2036ef..29ee47466 100644 --- a/ml3d/datasets/waymo.py +++ b/ml3d/datasets/waymo.py @@ -250,9 +250,9 @@ def __len__(self): def get_data(self, idx): pc_path = self.path_list[idx] - label_path = pc_path.replace('velodyne', - 'label_all').replace('.bin', '.txt') - calib_path = label_path.replace('label_all', 'calib') + label_path = ("label_all".join(pc_path.rsplit("velodyne", 1))).replace( + '.bin', '.txt') + calib_path = "calib".join(label_path.rsplit("label_all", 1)) pc = self.dataset.read_lidar(pc_path) calib = self.dataset.read_calib(calib_path) diff --git a/ml3d/torch/models/kpconv.py b/ml3d/torch/models/kpconv.py index a437312f3..c6a239618 100644 --- a/ml3d/torch/models/kpconv.py +++ b/ml3d/torch/models/kpconv.py @@ -557,7 +557,7 @@ def inference_preprocess(self): return inputs - def update_probs(self, inputs, results, test_probs, test_labels): + def update_probs(self, inputs, results, test_probs): self.test_smooth = 0.95 stk_probs = torch.nn.functional.softmax(results, dim=-1) stk_probs = stk_probs.cpu().data.numpy() @@ -577,16 +577,14 @@ def update_probs(self, inputs, results, test_probs, test_labels): for b_i, length in enumerate(lengths): # Get prediction probs = stk_probs[i0:i0 + length] - labels = np.argmax(probs, 1) proj_inds = r_inds_list[b_i] proj_mask = r_mask_list[b_i] test_probs[proj_mask] = self.test_smooth * test_probs[proj_mask] + ( 1 - self.test_smooth) * probs - test_labels[proj_mask] = labels i0 += length - return test_probs, test_labels + return test_probs def inference_end(self, inputs, results): m_softmax = torch.nn.Softmax(dim=-1) diff --git a/ml3d/torch/models/point_transformer.py b/ml3d/torch/models/point_transformer.py index 804387628..a28293e19 100644 --- a/ml3d/torch/models/point_transformer.py +++ b/ml3d/torch/models/point_transformer.py @@ -304,14 +304,13 @@ def transform(self, data, attr): return data - def update_probs(self, inputs, results, test_probs, test_labels): + def update_probs(self, inputs, results, test_probs): result = results.reshape(-1, self.cfg.num_classes) probs = torch.nn.functional.softmax(result, dim=-1).cpu().data.numpy() - labels = np.argmax(probs, 1) self.trans_point_sampler(patchwise=False) - return probs, labels + return probs def inference_begin(self): data = self.preprocess(data, {'split': 'test'}) diff --git a/ml3d/torch/models/pvcnn.py b/ml3d/torch/models/pvcnn.py index fc157488f..13908bddd 100644 --- a/ml3d/torch/models/pvcnn.py +++ b/ml3d/torch/models/pvcnn.py @@ -250,14 +250,13 @@ def transform(self, data, attr): return data - def update_probs(self, inputs, results, test_probs, test_labels): + def update_probs(self, inputs, results, test_probs): result = results.reshape(-1, self.cfg.num_classes) probs = torch.nn.functional.softmax(result, dim=-1).cpu().data.numpy() - labels = np.argmax(probs, 1) self.trans_point_sampler(patchwise=False) - return probs, labels + return probs def inference_begin(self, data): data = self.preprocess(data, {'split': 'test'}) diff --git a/ml3d/torch/models/randlanet.py b/ml3d/torch/models/randlanet.py index 06c86b87e..390c53cbe 100644 --- a/ml3d/torch/models/randlanet.py +++ b/ml3d/torch/models/randlanet.py @@ -438,17 +438,16 @@ def inference_end(self, inputs, results): else: return False - def update_probs(self, inputs, results, test_probs, test_labels): + def update_probs(self, inputs, results, test_probs): """Update test probabilities with probs from current tested patch. Args: inputs: input to the model. results: output of the model. test_probs: probabilities for whole pointcloud - test_labels: ground truth for whole pointcloud. Returns: - updated probabilities and labels + updated probabilities """ self.test_smooth = 0.95 @@ -458,14 +457,12 @@ def update_probs(self, inputs, results, test_probs, test_labels): result = torch.reshape(results[b], (-1, self.cfg.num_classes)) probs = torch.nn.functional.softmax(result, dim=-1) probs = probs.cpu().data.numpy() - labels = np.argmax(probs, 1) inds = inputs['data']['point_inds'][b] test_probs[inds] = self.test_smooth * test_probs[inds] + ( 1 - self.test_smooth) * probs - test_labels[inds] = labels - return test_probs, test_labels + return test_probs MODEL._register_module(RandLANet, 'torch') diff --git a/ml3d/torch/pipelines/semantic_segmentation.py b/ml3d/torch/pipelines/semantic_segmentation.py index 232ce5a30..6f6767ecf 100644 --- a/ml3d/torch/pipelines/semantic_segmentation.py +++ b/ml3d/torch/pipelines/semantic_segmentation.py @@ -153,7 +153,6 @@ def run_inference(self, data): model.trans_point_sampler = infer_sampler.get_point_sampler() self.curr_cloud_id = -1 self.test_probs = [] - self.test_labels = [] self.ori_test_probs = [] self.ori_test_labels = [] @@ -219,7 +218,6 @@ def run_test(self): model.trans_point_sampler = test_sampler.get_point_sampler() self.curr_cloud_id = -1 self.test_probs = [] - self.test_labels = [] self.ori_test_probs = [] self.ori_test_labels = [] @@ -277,8 +275,6 @@ def update_tests(self, sampler, inputs, results): self.test_probs.append( np.zeros(shape=[num_points, self.model.cfg.num_classes], dtype=np.float16)) - self.test_labels.append(np.zeros(shape=[num_points], - dtype=np.int16)) self.complete_infer = False this_possiblility = sampler.possibilities[sampler.cloud_id] @@ -287,10 +283,11 @@ def update_tests(self, sampler, inputs, results): self.pbar_update) self.pbar_update = this_possiblility[ this_possiblility > end_threshold].shape[0] - self.test_probs[self.curr_cloud_id], self.test_labels[ - self.curr_cloud_id] = self.model.update_probs( - inputs, results, self.test_probs[self.curr_cloud_id], - self.test_labels[self.curr_cloud_id]) + self.test_probs[self.curr_cloud_id] = self.model.update_probs( + inputs, + results, + self.test_probs[self.curr_cloud_id], + ) if (split in ['test'] and this_possiblility[this_possiblility > end_threshold].shape[0] @@ -303,10 +300,12 @@ def update_tests(self, sampler, inputs, results): if proj_inds is None: proj_inds = np.arange( self.test_probs[self.curr_cloud_id].shape[0]) + test_labels = np.argmax( + self.test_probs[self.curr_cloud_id][proj_inds], 1) + self.ori_test_probs.append( self.test_probs[self.curr_cloud_id][proj_inds]) - self.ori_test_labels.append( - self.test_labels[self.curr_cloud_id][proj_inds]) + self.ori_test_labels.append(test_labels) self.complete_infer = True def run_train(self): diff --git a/requirements-tensorflow.txt b/requirements-tensorflow.txt index 442d0699a..2c8fb571e 100644 --- a/requirements-tensorflow.txt +++ b/requirements-tensorflow.txt @@ -1 +1,2 @@ -tensorflow~=2.8.4 +tensorflow~=2.8.4 ; sys_platform != 'darwin' or platform_machine != 'arm64' +tensorflow-macos==2.8.0 ; sys_platform == 'darwin' and platform_machine == 'arm64' From fb87da270c3c2186c25e8aa9af2f05a4ef25202b Mon Sep 17 00:00:00 2001 From: Zhongyi Du <116033603+zhongyidu@users.noreply.github.com> Date: Mon, 9 Oct 2023 11:19:21 +0200 Subject: [PATCH 3/7] corrected pairing in SSD head of pointpillars (#602) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accordiing to the PointPillars paper, "A positive match is either the highest with a ground truth box, or above the positive match threshold". While the latter point is implemented correctly, there's a minor mistake in the implementation of the first point. The original code only updates the bool array _pos_idx_ without changing the matched target to the correct ground truth index. This could lead to ground truth box without a valid match in the end. When I corrected the SSD matching, I also referred to this implementation of SSD: https://github.com/amdegroot/ssd.pytorch/blob/5b0b77faa955c1917b0c710d770739ba8fbff9b7/layers/box_utils.py#L106 - - - This change is [Reviewable](https://reviewable.io/reviews/isl-org/Open3D-ML/602) Co-authored-by: Benjamin Ummenhofer --- ml3d/torch/models/point_pillars.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ml3d/torch/models/point_pillars.py b/ml3d/torch/models/point_pillars.py index a519acc75..c6453454b 100644 --- a/ml3d/torch/models/point_pillars.py +++ b/ml3d/torch/models/point_pillars.py @@ -907,7 +907,7 @@ def flatten_idx(idx, j): # for each anchor the gt with max IoU max_overlaps, argmax_overlaps = overlaps.max(dim=0) # for each gt the anchor with max IoU - gt_max_overlaps, _ = overlaps.max(dim=1) + gt_max_overlaps, gt_argmax_overlaps = overlaps.max(dim=1) pos_idx = max_overlaps >= pos_th neg_idx = (max_overlaps >= 0) & (max_overlaps < neg_th) @@ -916,6 +916,7 @@ def flatten_idx(idx, j): for k in range(len(target_bboxes[i])): if gt_max_overlaps[k] >= neg_th: pos_idx[overlaps[k, :] == gt_max_overlaps[k]] = True + argmax_overlaps[gt_argmax_overlaps[k]] = k # encode bbox for positive matches assigned_bboxes.append( From c62c4f5f05fdbe6f254774a4a27fa7eab3d270cd Mon Sep 17 00:00:00 2001 From: He Zhizhou <2670226747@qq.com> Date: Tue, 24 Oct 2023 02:52:38 +0800 Subject: [PATCH 4/7] Replace colon with dash in timestamp variables (#620) Prevent OS errors when running the Semantic Segmentation program on exFAT SSD. --- ml3d/tf/pipelines/object_detection.py | 6 +++--- ml3d/tf/pipelines/semantic_segmentation.py | 4 ++-- ml3d/torch/pipelines/object_detection.py | 6 +++--- ml3d/torch/pipelines/semantic_segmentation.py | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ml3d/tf/pipelines/object_detection.py b/ml3d/tf/pipelines/object_detection.py index cf0688e17..2cefc07a7 100644 --- a/ml3d/tf/pipelines/object_detection.py +++ b/ml3d/tf/pipelines/object_detection.py @@ -62,7 +62,7 @@ def run_test(self): dataset = self.dataset cfg = self.cfg - timestamp = datetime.now().strftime('%Y-%m-%d_%H:%M:%S') + timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S') log_file_path = join(cfg.logs_dir, 'log_test_' + timestamp + '.txt') log.info("Logging in file : {}".format(log_file_path)) @@ -111,7 +111,7 @@ def run_valid(self, epoch=0): dataset = self.dataset cfg = self.cfg - timestamp = datetime.now().strftime('%Y-%m-%d_%H:%M:%S') + timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S') log_file_path = join(cfg.logs_dir, 'log_valid_' + timestamp + '.txt') log.info("Logging in file : {}".format(log_file_path)) @@ -212,7 +212,7 @@ def run_train(self): cfg = self.cfg - timestamp = datetime.now().strftime('%Y-%m-%d_%H:%M:%S') + timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S') log_file_path = join(cfg.logs_dir, 'log_train_' + timestamp + '.txt') log.info("Logging in file : {}".format(log_file_path)) log.addHandler(logging.FileHandler(log_file_path)) diff --git a/ml3d/tf/pipelines/semantic_segmentation.py b/ml3d/tf/pipelines/semantic_segmentation.py index c1baab7cf..7a5b1e6bb 100644 --- a/ml3d/tf/pipelines/semantic_segmentation.py +++ b/ml3d/tf/pipelines/semantic_segmentation.py @@ -147,7 +147,7 @@ def run_test(self): cfg = self.cfg self.load_ckpt(model.cfg.ckpt_path) - timestamp = datetime.now().strftime('%Y-%m-%d_%H:%M:%S') + timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S') log_file_path = join(cfg.logs_dir, 'log_test_' + timestamp + '.txt') log.info("Logging in file : {}".format(log_file_path)) @@ -192,7 +192,7 @@ def run_train(self): cfg = self.cfg log.info(model) - timestamp = datetime.now().strftime('%Y-%m-%d_%H:%M:%S') + timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S') log_file_path = join(cfg.logs_dir, 'log_train_' + timestamp + '.txt') log.info("Logging in file : {}".format(log_file_path)) log.addHandler(logging.FileHandler(log_file_path)) diff --git a/ml3d/torch/pipelines/object_detection.py b/ml3d/torch/pipelines/object_detection.py index 593494fea..892d86154 100644 --- a/ml3d/torch/pipelines/object_detection.py +++ b/ml3d/torch/pipelines/object_detection.py @@ -85,7 +85,7 @@ def run_test(self): model.eval() - timestamp = datetime.now().strftime('%Y-%m-%d_%H:%M:%S') + timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S') log.info("DEVICE : {}".format(device)) log_file_path = join(cfg.logs_dir, 'log_test_' + timestamp + '.txt') @@ -145,7 +145,7 @@ def run_valid(self, epoch=0): model.eval() - timestamp = datetime.now().strftime('%Y-%m-%d_%H:%M:%S') + timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S') log.info("DEVICE : {}".format(device)) log_file_path = join(cfg.logs_dir, 'log_valid_' + timestamp + '.txt') @@ -282,7 +282,7 @@ def run_train(self): if rank == 0: log.info("DEVICE : {}".format(device)) - timestamp = datetime.now().strftime('%Y-%m-%d_%H:%M:%S') + timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S') log_file_path = join(cfg.logs_dir, 'log_train_' + timestamp + '.txt') diff --git a/ml3d/torch/pipelines/semantic_segmentation.py b/ml3d/torch/pipelines/semantic_segmentation.py index 6f6767ecf..aa918cc7d 100644 --- a/ml3d/torch/pipelines/semantic_segmentation.py +++ b/ml3d/torch/pipelines/semantic_segmentation.py @@ -190,7 +190,7 @@ def run_test(self): model.eval() self.metric_test = SemSegMetric() - timestamp = datetime.now().strftime('%Y-%m-%d_%H:%M:%S') + timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S') log.info("DEVICE : {}".format(device)) log_file_path = join(cfg.logs_dir, 'log_test_' + timestamp + '.txt') @@ -320,7 +320,7 @@ def run_train(self): model.to(device) log.info("DEVICE : {}".format(device)) - timestamp = datetime.now().strftime('%Y-%m-%d_%H:%M:%S') + timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S') log_file_path = join(cfg.logs_dir, 'log_train_' + timestamp + '.txt') log.info("Logging in file : {}".format(log_file_path)) From 25a27d1722571d36fac5c8d977227487e7a57344 Mon Sep 17 00:00:00 2001 From: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> Date: Tue, 24 Oct 2023 21:27:52 +0530 Subject: [PATCH 5/7] Support python 311 (#619) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Companion Open3D PR: https://github.com/isl-org/Open3D/pull/6288 - Add support for python3.11. -️ Upgrade pytorch to 2.0.1 with cuda 11.7 - Upgrade tensorflow / tensorboard to 2.13. - Disable building tensorflow ops on Linux. Keep old cxx11_abi. - Update CI to use requirements files instead of manually specified versions. - Add instructions to build with new cxx11_abi with tensorflow ops support. --- .github/workflows/ubuntu.yml | 4 +- README.md | 200 +++++++++++++++++++---------------- ci/run_ci.sh | 53 ++++------ requirements-tensorflow.txt | 3 +- requirements-torch-cuda.txt | 4 +- requirements-torch.txt | 8 +- requirements.txt | 2 +- tests/test_integration.py | 18 ++-- tests/test_models.py | 30 ++++-- 9 files changed, 167 insertions(+), 155 deletions(-) diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index b7aa7f3ae..0362236f0 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -16,7 +16,7 @@ jobs: NPROC: 2 steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup cache uses: actions/cache@v3 with: @@ -33,7 +33,7 @@ jobs: - name: Set up Python version uses: actions/setup-python@v4 with: - python-version: "3.10" + python-version: "3.11" # Pre-installed packages: https://github.com/actions/runner-images/tree/main/images - name: Install ccache run: | diff --git a/README.md b/README.md index 489173630..f565eb5bb 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ -

ML @@ -9,7 +8,7 @@ ![PyTorch badge](https://img.shields.io/badge/PyTorch-supported-brightgreen?style=flat&logo=pytorch) ![TensorFlow badge](https://img.shields.io/badge/TensorFlow-supported-brightgreen?style=flat&logo=tensorflow) -[**Installation**](#installation) | [**Get started**](#getting-started) | [**Structure**](#repository-structure) | [**Tasks & Algorithms**](#tasks-and-algorithms) | [**Model Zoo**](model_zoo.md) | [**Datasets**](#datasets) | [**How-tos**](#how-tos) | [**Contribute**](#contribute) +[**Installation**](#installation) \| [**Get started**](#getting-started) \| [**Structure**](#repository-structure) \| [**Tasks & Algorithms**](#tasks-and-algorithms) \| [**Model Zoo**](model_zoo.md) \| [**Datasets**](#datasets) \| [**How-tos**](#how-tos) \| [**Contribute**](#contribute) Open3D-ML is an extension of Open3D for 3D machine learning tasks. It builds on top of the Open3D core library and extends it with machine learning @@ -21,7 +20,6 @@ Open3D-ML works with **TensorFlow** and **PyTorch** to integrate easily into existing projects and also provides general functionality independent of ML frameworks such as data visualization. - ## Installation ### Users @@ -29,11 +27,12 @@ ML frameworks such as data visualization. Open3D-ML is integrated in the Open3D v0.11+ python distribution and is compatible with the following versions of ML frameworks. - * PyTorch 1.8.2 - * TensorFlow 2.5.2 - * CUDA 10.1, 11.* (On `GNU/Linux x86_64`, optional) +- PyTorch 2.0.\* +- TensorFlow 2.13.\* (macOS, see below for Linux) +- CUDA 10.1, 11.\* (On `GNU/Linux x86_64`, optional) You can install Open3D with + ```bash # make sure you have the latest pip version pip install --upgrade pip @@ -43,6 +42,7 @@ pip install open3d To install a compatible version of PyTorch or TensorFlow you can use the respective requirements files: + ```bash # To install a compatible version of TensorFlow pip install -r requirements-tensorflow.txt @@ -63,7 +63,22 @@ $ python -c "import open3d.ml.tf as ml3d" If you need to use different versions of the ML frameworks or CUDA we recommend to -[build Open3D from source](http://www.open3d.org/docs/release/compilation.html). +[build Open3D from source](http://www.open3d.org/docs/release/compilation.html) + or [build Open3D in docker](http://www.open3d.org/docs/release/docker.html#building-open3d-in-docker). + +From v0.18 onwards on Linux, the PyPI Open3D wheel does not have native support +for Tensorflow due to build incompatibilities between PyTorch and Tensorflow +\[See [Python 3.11 support PR](https://github.com/isl-org/Open3D/pull/6288)] for +details. If you'd like to use Open3D with Tensorflow on Linux, you can +[build Open3D wheel from source in docker](http://www.open3d.org/docs/release/docker.html#building-open3d-in-docker) +with support for Tensorflow (but not PyTorch) as: + +```bash +cd docker +# Build open3d and open3d-cpu wheels for Python 3.10 with Tensorflow support +export BUILD_PYTORCH_OPS=OFF BUILD_TENSORFLOW_OPS=ON +./docker_build.sh cuda_wheel_py310 +``` ## Getting started @@ -91,10 +106,13 @@ print(all_split.get_data(0)['point'].shape) vis = ml3d.vis.Visualizer() vis.visualize_dataset(dataset, 'all', indices=range(100)) ``` + ![Visualizer GIF](docs/images/getting_started_ml_visualizer.gif) ### Loading a config file + Configs of models, datasets, and pipelines are stored in `ml3d/configs`. Users can also construct their own yaml files to keep record of their customized configurations. Here is an example of reading a config file and constructing modules from it. + ```python import open3d.ml as _ml3d import open3d.ml.torch as ml3d # or open3d.ml.tf as ml3d @@ -123,6 +141,7 @@ Building on the previous example we can instantiate a pipeline with a pretrained model for semantic segmentation and run it on a point cloud of our dataset. See the [model zoo](#model-zoo) for obtaining the weights of the pretrained model. + ```python import os import open3d.ml as _ml3d @@ -158,8 +177,8 @@ result = pipeline.run_inference(data) # evaluate performance on the test set; this will write logs to './logs'. pipeline.run_test() ``` -Users can also [use predefined scripts](README.md#using-predefined-scripts) to load pretrained weights and run testing. +Users can also [use predefined scripts](README.md#using-predefined-scripts) to load pretrained weights and run testing. #### Training a model for semantic segmentation @@ -191,7 +210,9 @@ for details. ### 3D Object Detection #### Running a pretrained model for 3D object detection + The 3D object detection model is similar to a semantic segmentation model. We can instantiate a pipeline with a pretrained model for Object Detection and run it on a point cloud of our dataset. See the [model zoo](#model-zoo) for obtaining the weights of the pretrained model. + ```python import os import open3d.ml as _ml3d @@ -227,10 +248,11 @@ result = pipeline.run_inference(data) # evaluate performance on the test set; this will write logs to './logs'. pipeline.run_test() ``` -Users can also [use predefined scripts](README.md#using-predefined-scripts) to load pretrained weights and run testing. +Users can also [use predefined scripts](README.md#using-predefined-scripts) to load pretrained weights and run testing. #### Training a model for 3D object detection + Similar as for inference, pipelines provide an interface for training a model on a dataset. @@ -252,7 +274,6 @@ Below is an example of visualization using KITTI. The example shows the use of b - For more examples see [`examples/`](https://github.com/isl-org/Open3D-ML/tree/master/examples) and the [`scripts/`](https://github.com/isl-org/Open3D-ML/tree/master/scripts) directories. You can also enable saving training summaries in the config file and visualize ground truth and @@ -274,8 +295,8 @@ either SemanticSegmentation or ObjectDetection in the `pipeline` parameter. Note that `extra args` will be prioritized over the same parameter present in the configuration file. So instead of changing param in config file, you may pass the same as a command line argument while launching the script. - For eg. + ``` # Launch training for RandLANet on SemanticKITTI with torch. python scripts/run_pipeline.py torch -c ml3d/configs/randlanet_semantickitti.yml --dataset.dataset_path --pipeline SemanticSegmentation --dataset.use_cache True @@ -284,35 +305,34 @@ python scripts/run_pipeline.py torch -c ml3d/configs/randlanet_semantickitti.yml python scripts/run_pipeline.py torch -c ml3d/configs/pointpillars_kitti.yml --split test --dataset.dataset_path --pipeline ObjectDetection --dataset.use_cache True ``` + For further help, run `python scripts/run_pipeline.py --help`. ## Repository structure + The core part of Open3D-ML lives in the `ml3d` subfolder, which is integrated into Open3D in the `ml` namespace. In addition to the core part, the directories `examples` and `scripts` provide supporting scripts for getting started with setting up a training pipeline or running a network on a dataset. -``` -├─ docs # Markdown and rst files for documentation -├─ examples # Place for example scripts and notebooks -├─ ml3d # Package root dir that is integrated in open3d - ├─ configs # Model configuration files - ├─ datasets # Generic dataset code; will be integratede as open3d.ml.{tf,torch}.datasets - ├─ metrics # Metrics available for evaluating ML models - ├─ utils # Framework independent utilities; available as open3d.ml.{tf,torch}.utils - ├─ vis # ML specific visualization functions - ├─ tf # Directory for TensorFlow specific code. same structure as ml3d/torch. - │ # This will be available as open3d.ml.tf - ├─ torch # Directory for PyTorch specific code; available as open3d.ml.torch - ├─ dataloaders # Framework specific dataset code, e.g. wrappers that can make use of the - │ # generic dataset code. - ├─ models # Code for models - ├─ modules # Smaller modules, e.g., metrics and losses - ├─ pipelines # Pipelines for tasks like semantic segmentation - ├─ utils # Utilities for <> -├─ scripts # Demo scripts for training and dataset download scripts -``` - + ├─ docs # Markdown and rst files for documentation + ├─ examples # Place for example scripts and notebooks + ├─ ml3d # Package root dir that is integrated in open3d + ├─ configs # Model configuration files + ├─ datasets # Generic dataset code; will be integratede as open3d.ml.{tf,torch}.datasets + ├─ metrics # Metrics available for evaluating ML models + ├─ utils # Framework independent utilities; available as open3d.ml.{tf,torch}.utils + ├─ vis # ML specific visualization functions + ├─ tf # Directory for TensorFlow specific code. same structure as ml3d/torch. + │ # This will be available as open3d.ml.tf + ├─ torch # Directory for PyTorch specific code; available as open3d.ml.torch + ├─ dataloaders # Framework specific dataset code, e.g. wrappers that can make use of the + │ # generic dataset code. + ├─ models # Code for models + ├─ modules # Smaller modules, e.g., metrics and losses + ├─ pipelines # Pipelines for tasks like semantic segmentation + ├─ utils # Utilities for <> + ├─ scripts # Demo scripts for training and dataset download scripts ## Tasks and Algorithms @@ -321,19 +341,18 @@ setting up a training pipeline or running a network on a dataset. For the task of semantic segmentation, we measure the performance of different methods using the mean intersection-over-union (mIoU) over all classes. The table shows the available models and datasets for the segmentation task and the respective scores. Each score links to the respective weight file. +| Model / Dataset | SemanticKITTI | Toronto 3D | S3DIS | Semantic3D | Paris-Lille3D | ScanNet | +| ------------------------ | ------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | +| RandLA-Net (tf) | [53.7](https://storage.googleapis.com/open3d-releases/model-zoo/randlanet_semantickitti_202201071330utc.zip) | [73.7](https://storage.googleapis.com/open3d-releases/model-zoo/randlanet_toronto3d_202201071330utc.zip) | [70.9](https://storage.googleapis.com/open3d-releases/model-zoo/randlanet_s3dis_202201071330utc.zip) | [76.0](https://storage.googleapis.com/open3d-releases/model-zoo/randlanet_semantic3d_202201071330utc.zip) | [70.0](https://storage.googleapis.com/open3d-releases/model-zoo/randlanet_parislille3d_202201071330utc.zip)\* | - | +| RandLA-Net (torch) | [52.8](https://storage.googleapis.com/open3d-releases/model-zoo/randlanet_semantickitti_202201071330utc.pth) | [74.0](https://storage.googleapis.com/open3d-releases/model-zoo/randlanet_toronto3d_202201071330utc.pth) | [70.9](https://storage.googleapis.com/open3d-releases/model-zoo/randlanet_s3dis_202201071330utc.pth) | [76.0](https://storage.googleapis.com/open3d-releases/model-zoo/randlanet_semantic3d_202201071330utc.pth) | [70.0](https://storage.googleapis.com/open3d-releases/model-zoo/randlanet_parislille3d_202201071330utc.pth)\* | - | +| KPConv (tf) | [58.7](https://storage.googleapis.com/open3d-releases/model-zoo/kpconv_semantickitti_202010021102utc.zip) | [65.6](https://storage.googleapis.com/open3d-releases/model-zoo/kpconv_toronto3d_202012221551utc.zip) | [65.0](https://storage.googleapis.com/open3d-releases/model-zoo/kpconv_s3dis_202010091238.zip) | - | [76.7](https://storage.googleapis.com/open3d-releases/model-zoo/kpconv_parislille3d_202011241550utc.zip) | - | +| KPConv (torch) | [58.0](https://storage.googleapis.com/open3d-releases/model-zoo/kpconv_semantickitti_202009090354utc.pth) | [65.6](https://storage.googleapis.com/open3d-releases/model-zoo/kpconv_toronto3d_202012221551utc.pth) | [60.0](https://storage.googleapis.com/open3d-releases/model-zoo/kpconv_s3dis_202010091238.pth) | - | [76.7](https://storage.googleapis.com/open3d-releases/model-zoo/kpconv_parislille3d_202011241550utc.pth) | - | +| SparseConvUnet (torch) | - | - | - | - | - | [68](https://storage.googleapis.com/open3d-releases/model-zoo/sparseconvunet_scannet_202105031316utc.pth) | +| SparseConvUnet (tf) | - | - | - | - | - | [68.2](https://storage.googleapis.com/open3d-releases/model-zoo/sparseconvunet_scannet_202105031316utc.zip) | +| PointTransformer (torch) | - | - | [69.2](https://storage.googleapis.com/open3d-releases/model-zoo/pointtransformer_s3dis_202109241350utc.pth) | - | - | - | +| PointTransformer (tf) | - | - | [69.2](https://storage.googleapis.com/open3d-releases/model-zoo/pointtransformer_s3dis_202109241350utc.zip) | - | - | - | -| Model / Dataset | SemanticKITTI | Toronto 3D | S3DIS | Semantic3D | Paris-Lille3D | ScanNet | -|--------------------|---------------|----------- |-------|--------------|-------------|---------| -| RandLA-Net (tf) | [53.7](https://storage.googleapis.com/open3d-releases/model-zoo/randlanet_semantickitti_202201071330utc.zip) | [73.7](https://storage.googleapis.com/open3d-releases/model-zoo/randlanet_toronto3d_202201071330utc.zip) | [70.9](https://storage.googleapis.com/open3d-releases/model-zoo/randlanet_s3dis_202201071330utc.zip) | [76.0](https://storage.googleapis.com/open3d-releases/model-zoo/randlanet_semantic3d_202201071330utc.zip) | [70.0](https://storage.googleapis.com/open3d-releases/model-zoo/randlanet_parislille3d_202201071330utc.zip)* | - | -| RandLA-Net (torch) | [52.8](https://storage.googleapis.com/open3d-releases/model-zoo/randlanet_semantickitti_202201071330utc.pth) | [74.0](https://storage.googleapis.com/open3d-releases/model-zoo/randlanet_toronto3d_202201071330utc.pth) | [70.9](https://storage.googleapis.com/open3d-releases/model-zoo/randlanet_s3dis_202201071330utc.pth) | [76.0](https://storage.googleapis.com/open3d-releases/model-zoo/randlanet_semantic3d_202201071330utc.pth) | [70.0](https://storage.googleapis.com/open3d-releases/model-zoo/randlanet_parislille3d_202201071330utc.pth)* | - | -| KPConv (tf) | [58.7](https://storage.googleapis.com/open3d-releases/model-zoo/kpconv_semantickitti_202010021102utc.zip) | [65.6](https://storage.googleapis.com/open3d-releases/model-zoo/kpconv_toronto3d_202012221551utc.zip) | [65.0](https://storage.googleapis.com/open3d-releases/model-zoo/kpconv_s3dis_202010091238.zip) | - | [76.7](https://storage.googleapis.com/open3d-releases/model-zoo/kpconv_parislille3d_202011241550utc.zip) | - | -| KPConv (torch) | [58.0](https://storage.googleapis.com/open3d-releases/model-zoo/kpconv_semantickitti_202009090354utc.pth) | [65.6](https://storage.googleapis.com/open3d-releases/model-zoo/kpconv_toronto3d_202012221551utc.pth) | [60.0](https://storage.googleapis.com/open3d-releases/model-zoo/kpconv_s3dis_202010091238.pth) | - | [76.7](https://storage.googleapis.com/open3d-releases/model-zoo/kpconv_parislille3d_202011241550utc.pth) | - | -| SparseConvUnet (torch)| - | - | - | - | - | [68](https://storage.googleapis.com/open3d-releases/model-zoo/sparseconvunet_scannet_202105031316utc.pth) | -| SparseConvUnet (tf)| - | - | - | - | - | [68.2](https://storage.googleapis.com/open3d-releases/model-zoo/sparseconvunet_scannet_202105031316utc.zip) | -| PointTransformer (torch)| - | - | [69.2](https://storage.googleapis.com/open3d-releases/model-zoo/pointtransformer_s3dis_202109241350utc.pth) | - | - | - | -| PointTransformer (tf)| - | - | [69.2](https://storage.googleapis.com/open3d-releases/model-zoo/pointtransformer_s3dis_202109241350utc.zip) | - | - | - | - -(*) Using weights from original author. +(\*) Using weights from original author. ### Object Detection @@ -342,80 +361,74 @@ The table shows the available models and datasets for the object detection task For the evaluation, the models were evaluated using the validation subset, according to KITTI's validation criteria. The models were trained for three classes (car, pedestrian and cyclist). The calculated values are the mean value over the mAP of all classes for all difficulty levels. For the Waymo dataset, the models were trained on three classes (pedestrian, vehicle, cyclist). +| Model / Dataset | KITTI [BEV / 3D] @ 0.70 | Waymo (BEV / 3D) @ 0.50 | +| -------------------- | -------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| PointPillars (tf) | [61.6 / 55.2](https://storage.googleapis.com/open3d-releases/model-zoo/pointpillars_kitti_202012221652utc.zip) | - | +| PointPillars (torch) | [61.2 / 52.8](https://storage.googleapis.com/open3d-releases/model-zoo/pointpillars_kitti_202012221652utc.pth) | avg: 61.01 / 48.30 \| [best: 61.47 / 57.55](https://storage.googleapis.com/open3d-releases/model-zoo/pointpillars_waymo_202211200158utc_seed2_gpu16.pth) [^wpp-train] | +| PointRCNN (tf) | [78.2 / 65.9](https://storage.googleapis.com/open3d-releases/model-zoo/pointrcnn_kitti_202105071146utc.zip) | - | +| PointRCNN (torch) | [78.2 / 65.9](https://storage.googleapis.com/open3d-releases/model-zoo/pointrcnn_kitti_202105071146utc.pth) | - | -| Model / Dataset | KITTI [BEV / 3D] @ 0.70| Waymo (BEV / 3D) @ 0.50 | -|--------------------|------------------------|------------------| -| PointPillars (tf) | [61.6 / 55.2](https://storage.googleapis.com/open3d-releases/model-zoo/pointpillars_kitti_202012221652utc.zip) | - | -| PointPillars (torch) | [61.2 / 52.8](https://storage.googleapis.com/open3d-releases/model-zoo/pointpillars_kitti_202012221652utc.pth) | avg: 61.01 / 48.30 \| [best: 61.47 / 57.55](https://storage.googleapis.com/open3d-releases/model-zoo/pointpillars_waymo_202211200158utc_seed2_gpu16.pth) [^wpp-train] | -| PointRCNN (tf) | [78.2 / 65.9](https://storage.googleapis.com/open3d-releases/model-zoo/pointrcnn_kitti_202105071146utc.zip) | - | -| PointRCNN (torch) | [78.2 / 65.9](https://storage.googleapis.com/open3d-releases/model-zoo/pointrcnn_kitti_202105071146utc.pth) | - | - -[^wpp-train]: The avg. metrics are the average of three sets of training runs with 4, 8, 16 and 32 GPUs. Training was for halted after 30 epochs. Model checkpoint is available for the best training run. +[^wpp-train]: The avg. metrics are the average of three sets of training runs with 4, 8, 16 and 32 GPUs. Training was for halted after 30 epochs. Model checkpoint is available for the best training run. #### Training PointRCNN To use ground truth sampling data augmentation for training, we can generate the ground truth database as follows: -``` -python scripts/collect_bboxes.py --dataset_path -``` + + python scripts/collect_bboxes.py --dataset_path + This will generate a database consisting of objects from the train split. It is recommended to use this augmentation for dataset like KITTI where objects are sparse. The two stages of PointRCNN are trained separately. To train the proposal generation stage of PointRCNN with PyTorch, run the following command: -``` -# Train RPN for 100 epochs. -python scripts/run_pipeline.py torch -c ml3d/configs/pointrcnn_kitti.yml --dataset.dataset_path --mode RPN --epochs 100 -``` -After getting a well trained RPN network, we can train RCNN network with frozen RPN weights. -``` -# Train RCNN for 70 epochs. -python scripts/run_pipeline.py torch -c ml3d/configs/pointrcnn_kitti.yml --dataset.dataset_path --mode RCNN --model.ckpt_path --epochs 100 -``` + # Train RPN for 100 epochs. + python scripts/run_pipeline.py torch -c ml3d/configs/pointrcnn_kitti.yml --dataset.dataset_path --mode RPN --epochs 100 + +After getting a well trained RPN network, we can train RCNN network with frozen RPN weights. + # Train RCNN for 70 epochs. + python scripts/run_pipeline.py torch -c ml3d/configs/pointrcnn_kitti.yml --dataset.dataset_path --mode RCNN --model.ckpt_path --epochs 100 ## Model Zoo For a full list of all weight files see [model_weights.txt](https://storage.googleapis.com/open3d-releases/model-zoo/model_weights.txt) and the MD5 checksum file [model_weights.md5](https://storage.googleapis.com/open3d-releases/model-zoo/integrity.txt). - ## Datasets The following is a list of datasets for which we provide dataset reader classes. -* SemanticKITTI ([project page](http://semantic-kitti.org/)) -* Toronto 3D ([github](https://github.com/WeikaiTan/Toronto-3D)) -* Semantic 3D ([project-page](http://www.semantic3d.net/)) -* S3DIS ([project-page](http://buildingparser.stanford.edu/dataset.html)) -* Paris-Lille 3D ([project-page](https://npm3d.fr/paris-lille-3d)) -* Argoverse ([project-page](https://www.argoverse.org/)) -* KITTI ([project-page](http://www.cvlibs.net/datasets/kitti/eval_object.php?obj_benchmark=3d)) -* Lyft ([project-page](https://level-5.global/data)) -* nuScenes ([project-page](https://www.nuscenes.org/)) -* Waymo ([project-page](https://waymo.com/open/)) -* ScanNet([project-page](http://www.scan-net.org/)) - +- SemanticKITTI ([project page](http://semantic-kitti.org/)) +- Toronto 3D ([github](https://github.com/WeikaiTan/Toronto-3D)) +- Semantic 3D ([project-page](http://www.semantic3d.net/)) +- S3DIS ([project-page](http://buildingparser.stanford.edu/dataset.html)) +- Paris-Lille 3D ([project-page](https://npm3d.fr/paris-lille-3d)) +- Argoverse ([project-page](https://www.argoverse.org/)) +- KITTI ([project-page](http://www.cvlibs.net/datasets/kitti/eval_object.php?obj_benchmark=3d)) +- Lyft ([project-page](https://level-5.global/data)) +- nuScenes ([project-page](https://www.nuscenes.org/)) +- Waymo ([project-page](https://waymo.com/open/)) +- ScanNet([project-page](http://www.scan-net.org/)) For downloading these datasets visit the respective webpages and have a look at the scripts in [`scripts/download_datasets`](https://github.com/isl-org/Open3D-ML/tree/master/scripts/download_datasets). - - ## How-tos -* [Visualize network predictions](docs/howtos.md#visualize-network-predictions) -* [Visualize custom data](docs/howtos.md#visualize-custom-data) -* [Adding a new model](docs/howtos.md#adding-a-new-model) -* [Adding a new dataset](docs/howtos.md#adding-a-new-dataset) -* [Distributed training](docs/howtos.md#distributed-training) -* [Visualize and compare input data, ground truth and results in TensorBoard](docs/tensorboard.md) -* [Inference with Intel OpenVINO](docs/openvino.md) +- [Visualize network predictions](docs/howtos.md#visualize-network-predictions) +- [Visualize custom data](docs/howtos.md#visualize-custom-data) +- [Adding a new model](docs/howtos.md#adding-a-new-model) +- [Adding a new dataset](docs/howtos.md#adding-a-new-dataset) +- [Distributed training](docs/howtos.md#distributed-training) +- [Visualize and compare input data, ground truth and results in TensorBoard](docs/tensorboard.md) +- [Inference with Intel OpenVINO](docs/openvino.md) ## Contribute + There are many ways to contribute to this project. You can: -* Implement a new model -* Add code for reading a new dataset -* Share parameters and weights for an existing model -* Report problems and bugs + +- Implement a new model +- Add code for reading a new dataset +- Share parameters and weights for an existing model +- Report problems and bugs Please, **make your pull requests to the** [**dev**](https://github.com/isl-org/Open3D-ML/tree/dev) branch. Open3D is a community effort. We welcome and celebrate contributions from the @@ -429,9 +442,10 @@ Please also check out our communication channels to get in contact with the comm ## Communication channels -* [Forum](https://github.com/isl-org/Open3D/discussions): discussion on the usage of Open3D. -* [Discord Chat](https://discord.com/invite/D35BGvn): online chats, discussions, - and collaboration with other users and developers. + +- [Forum](https://github.com/isl-org/Open3D/discussions): discussion on the usage of Open3D. +- [Discord Chat](https://discord.com/invite/D35BGvn): online chats, discussions, + and collaboration with other users and developers. ## Citation diff --git a/ci/run_ci.sh b/ci/run_ci.sh index 608d3aade..f4ae82eca 100755 --- a/ci/run_ci.sh +++ b/ci/run_ci.sh @@ -1,54 +1,42 @@ #!/usr/bin/env bash -# -# The following environment variables are required: -# - NPROC -# -TENSORFLOW_VER="2.8.2" -TORCH_GLNX_VER="1.13.1+cpu" -# OPENVINO_DEV_VER="2021.4.2" # Numpy version conflict with TF 2.8.2 -PIP_VER="21.1.1" -WHEEL_VER="0.38.4" -STOOLS_VER="67.3.2" -YAPF_VER="0.30.0" -PYTEST_VER="7.1.2" -PYTEST_RANDOMLY_VER="3.8.0" set -euo pipefail +NPROC=${NPROC:?'env var must be set to number of available CPUs.'} +PIP_VER="23.2.1" + echo 1. Prepare the Open3D-ML repo and install dependencies echo -export PATH_TO_OPEN3D_ML=$(pwd) +export PATH_TO_OPEN3D_ML="$PWD" +echo "$PATH_TO_OPEN3D_ML" # the build system of the main repo expects a master branch. make sure master exists git checkout -b master || true -python -m pip install -U pip==$PIP_VER \ - wheel=="$WHEEL_VER" \ - setuptools=="$STOOLS_VER" \ - yapf=="$YAPF_VER" \ - pytest=="$PYTEST_VER" \ - pytest-randomly=="$PYTEST_RANDOMLY_VER" - -python -m pip install -r requirements.txt -echo $PATH_TO_OPEN3D_ML +python -m pip install -U pip==$PIP_VER +python -m pip install -r requirements.txt \ + -r requirements-torch.txt +# -r requirements-tensorflow.txt # TF disabled on Linux (Open3D PR#6288) +# -r requirements-openvino.txt # Numpy version conflict with TF 2.8.2 cd .. python -m pip install -U Cython echo 2. clone Open3D and install dependencies echo -git clone --recursive --branch master https://github.com/isl-org/Open3D.git +git clone --branch master https://github.com/isl-org/Open3D.git ./Open3D/util/install_deps_ubuntu.sh assume-yes -python -m pip install -U tensorflow-cpu==$TENSORFLOW_VER \ - torch==${TORCH_GLNX_VER} --extra-index-url https://download.pytorch.org/whl/cpu/ -# openvino-dev=="$OPENVINO_DEV_VER" +python -m pip install -r Open3D/python/requirements.txt \ + -r Open3D/python/requirements_style.txt \ + -r Open3D/python/requirements_test.txt echo 3. Configure for bundling the Open3D-ML part echo mkdir Open3D/build pushd Open3D/build +# TF disabled on Linux (Open3D PR#6288) cmake -DBUNDLE_OPEN3D_ML=ON \ - -DOPEN3D_ML_ROOT=$PATH_TO_OPEN3D_ML \ + -DOPEN3D_ML_ROOT="${PATH_TO_OPEN3D_ML}" \ -DGLIBCXX_USE_CXX11_ABI=OFF \ - -DBUILD_TENSORFLOW_OPS=ON \ + -DBUILD_TENSORFLOW_OPS=OFF \ -DBUILD_PYTORCH_OPS=ON \ -DBUILD_GUI=ON \ -DBUILD_UNIT_TESTS=OFF \ @@ -66,12 +54,13 @@ echo popd mkdir test_workdir pushd test_workdir -mv $PATH_TO_OPEN3D_ML/tests . +mv "$PATH_TO_OPEN3D_ML/tests" . echo Add --randomly-seed=SEED to the test command to reproduce test order. python -m pytest tests -echo ... now do the same but in dev mode by setting OPEN3D_ML_ROOT -export OPEN3D_ML_ROOT=$PATH_TO_OPEN3D_ML +echo "... now do the same but in dev mode by setting OPEN3D_ML_ROOT" +echo +export OPEN3D_ML_ROOT="$PATH_TO_OPEN3D_ML" echo Add --randomly-seed=SEED to the test command to reproduce test order. python -m pytest tests unset OPEN3D_ML_ROOT diff --git a/requirements-tensorflow.txt b/requirements-tensorflow.txt index 2c8fb571e..c3a69761c 100644 --- a/requirements-tensorflow.txt +++ b/requirements-tensorflow.txt @@ -1,2 +1 @@ -tensorflow~=2.8.4 ; sys_platform != 'darwin' or platform_machine != 'arm64' -tensorflow-macos==2.8.0 ; sys_platform == 'darwin' and platform_machine == 'arm64' +tensorflow~=2.13.0 \ No newline at end of file diff --git a/requirements-torch-cuda.txt b/requirements-torch-cuda.txt index a38029d98..b69670fc5 100644 --- a/requirements-torch-cuda.txt +++ b/requirements-torch-cuda.txt @@ -1,5 +1,5 @@ -f https://download.pytorch.org/whl/torch/ -torch==1.13.1+cu116 +torch==2.0.1+cu117 -f https://download.pytorch.org/whl/torchvision/ -torchvision==0.14.1+cu116 +torchvision==0.15.2+cu117 tensorboard diff --git a/requirements-torch.txt b/requirements-torch.txt index c77695075..fb417a316 100644 --- a/requirements-torch.txt +++ b/requirements-torch.txt @@ -1,6 +1,6 @@ --extra-index-url https://download.pytorch.org/whl/cpu/ -torch==1.13.1+cpu ; sys_platform != 'darwin' -torchvision==0.14.1+cpu ; sys_platform != 'darwin' -torch==1.13.1 ; sys_platform == 'darwin' -torchvision==0.14.1 ; sys_platform == 'darwin' +torch==2.0.1+cpu ; sys_platform != 'darwin' +torchvision==0.15.2+cpu ; sys_platform != 'darwin' +torch==2.0.1 ; sys_platform == 'darwin' +torchvision==0.15.2 ; sys_platform == 'darwin' tensorboard diff --git a/requirements.txt b/requirements.txt index 93f5af8d2..f3724a455 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ addict pillow>=9.3.0 matplotlib>=3 -numpy>1.15 +numpy>1.18 pandas>=1.0 pyyaml>=5.4.1 scikit-learn>=0.21 diff --git a/tests/test_integration.py b/tests/test_integration.py index b7aed07fd..d6b66628a 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -1,5 +1,6 @@ import pytest import os +import open3d as o3d if 'PATH_TO_OPEN3D_ML' in os.environ.keys(): base = os.environ['PATH_TO_OPEN3D_ML'] @@ -8,6 +9,7 @@ # base = '../Open3D-ML' +@pytest.mark.skipif("not o3d._build_config['BUILD_PYTORCH_OPS']") def test_integration_torch(): import torch import open3d.ml.torch as ml3d @@ -25,6 +27,7 @@ def test_integration_torch(): print(model) +@pytest.mark.skipif("not o3d._build_config['BUILD_TENSORFLOW_OPS']") def test_integration_tf(): import tensorflow as tf import open3d.ml.tf as ml3d @@ -45,15 +48,10 @@ def test_integration_tf(): def test_integration_openvino(): try: from openvino.inference_engine import IECore - openvino_available = True - except: - openvino_available = False - - if not openvino_available: + except ImportError: return - from open3d.ml.torch.models import OpenVINOModel - from open3d.ml.tf.models import OpenVINOModel - - -test_integration_torch() + if o3d._build_config['BUILD_TORCH_OPS']: + from open3d.ml.torch.models import OpenVINOModel + if o3d._build_config['BUILD_TENSORFLOW_OPS']: + from open3d.ml.tf.models import OpenVINOModel diff --git a/tests/test_models.py b/tests/test_models.py index 2ef4a13d2..45dd8e171 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,22 +1,28 @@ import pytest import os import numpy as np -import torch -import tensorflow as tf +import open3d as o3d +try: + import torch +except ImportError: + torch = None if 'PATH_TO_OPEN3D_ML' in os.environ.keys(): base = os.environ['PATH_TO_OPEN3D_ML'] else: base = '.' -gpus = tf.config.list_physical_devices('GPU') -if gpus: - # Use first GPU and restrict memory growth. - try: +try: + import tensorflow as tf + gpus = tf.config.list_physical_devices('GPU') + if gpus: + # Use first GPU and restrict memory growth. tf.config.experimental.set_visible_devices(gpus[0], 'GPU') - tf.config.set_memory_growth(gpu[0], True) - except RuntimeError as e: - print(e) + tf.config.set_memory_growth(gpus[0], True) +except RuntimeError as e: + print(e) +except ImportError: + tf = None try: from open3d.ml.torch.models import OpenVINOModel @@ -25,6 +31,7 @@ openvino_available = False +@pytest.mark.skipif("not o3d._build_config['BUILD_PYTORCH_OPS']") def test_randlanet_torch(): import open3d.ml.torch as ml3d @@ -66,6 +73,7 @@ def test_randlanet_torch(): assert out.shape == (1, 5000, 10) +@pytest.mark.skipif("not o3d._build_config['BUILD_TENSORFLOW_OPS']") def test_randlanet_tf(): import open3d.ml.tf as ml3d @@ -106,6 +114,7 @@ def test_randlanet_tf(): assert np.max(np.abs(ov_out - out)) < 1e-6 +@pytest.mark.skipif("not o3d._build_config['BUILD_PYTORCH_OPS']") def test_kpconv_torch(): import open3d.ml.torch as ml3d @@ -144,6 +153,7 @@ def test_kpconv_torch(): assert np.max(np.abs(ov_out - out)) < 1e-7 +@pytest.mark.skipif("not o3d._build_config['BUILD_TENSORFLOW_OPS']") def test_kpconv_tf(): import open3d.ml.tf as ml3d @@ -191,6 +201,7 @@ def test_kpconv_tf(): assert np.max(np.abs(ov_out - out)) < 1e-5 +@pytest.mark.skipif("not o3d._build_config['BUILD_PYTORCH_OPS']") def test_pointpillars_torch(): import open3d.ml.torch as ml3d from open3d.ml.utils import Config @@ -225,6 +236,7 @@ def test_pointpillars_torch(): assert torch.max(torch.abs(out - ref)) < 1e-5 +@pytest.mark.skipif("not o3d._build_config['BUILD_TENSORFLOW_OPS']") def test_pointpillars_tf(): import open3d.ml.tf as ml3d from open3d.ml.utils import Config From c7511921ce81f6476468a727ca6d4ae92d1de629 Mon Sep 17 00:00:00 2001 From: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> Date: Mon, 11 Dec 2023 22:34:13 +0530 Subject: [PATCH 6/7] master -> main for branches (#626) master -> primary elsewhere --- .github/ISSUE_TEMPLATE/0-build-issue-report.yml | 2 +- .github/ISSUE_TEMPLATE/1-bug-report.yml | 2 +- .github/ISSUE_TEMPLATE/2-questions.yml | 2 +- .github/ISSUE_TEMPLATE/3-feature-request.yml | 2 +- .github/workflows/style.yml | 2 +- .github/workflows/ubuntu.yml | 2 +- README.md | 16 ++++++++-------- ci/run_ci.sh | 6 +++--- model_zoo.md | 2 +- scripts/run_pipeline.py | 4 ++-- scripts/train_scripts/pointpillars_waymo.sh | 8 ++++---- 11 files changed, 24 insertions(+), 24 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/0-build-issue-report.yml b/.github/ISSUE_TEMPLATE/0-build-issue-report.yml index 2f72f3e7e..ee49885e2 100644 --- a/.github/ISSUE_TEMPLATE/0-build-issue-report.yml +++ b/.github/ISSUE_TEMPLATE/0-build-issue-report.yml @@ -13,7 +13,7 @@ body: required: true - label: "I have tested with the [latest development wheel](http://www.open3d.org/docs/latest/getting_started.html#development-version-pip)." required: true - - label: "I have checked the [release documentation](http://www.open3d.org/docs/release/) and the [latest documentation](http://www.open3d.org/docs/latest/) (for `master` branch)." + - label: "I have checked the [release documentation](http://www.open3d.org/docs/release/) and the [latest documentation](http://www.open3d.org/docs/latest/) (for `main` branch)." required: true - type: textarea diff --git a/.github/ISSUE_TEMPLATE/1-bug-report.yml b/.github/ISSUE_TEMPLATE/1-bug-report.yml index f5a114eae..010b2e1e2 100644 --- a/.github/ISSUE_TEMPLATE/1-bug-report.yml +++ b/.github/ISSUE_TEMPLATE/1-bug-report.yml @@ -13,7 +13,7 @@ body: required: true - label: "I have tested with the [latest development wheel](http://www.open3d.org/docs/latest/getting_started.html#development-version-pip)." required: true - - label: "I have checked the [release documentation](http://www.open3d.org/docs/release/) and the [latest documentation](http://www.open3d.org/docs/latest/) (for `master` branch)." + - label: "I have checked the [release documentation](http://www.open3d.org/docs/release/) and the [latest documentation](http://www.open3d.org/docs/latest/) (for `main` branch)." required: true - type: textarea diff --git a/.github/ISSUE_TEMPLATE/2-questions.yml b/.github/ISSUE_TEMPLATE/2-questions.yml index bdc78c53d..30fd60fb4 100644 --- a/.github/ISSUE_TEMPLATE/2-questions.yml +++ b/.github/ISSUE_TEMPLATE/2-questions.yml @@ -13,7 +13,7 @@ body: required: true - label: "I have tested with the [latest development wheel](http://www.open3d.org/docs/latest/getting_started.html#development-version-pip)." required: true - - label: "I have checked the [release documentation](http://www.open3d.org/docs/release/) and the [latest documentation](http://www.open3d.org/docs/latest/) (for `master` branch)." + - label: "I have checked the [release documentation](http://www.open3d.org/docs/release/) and the [latest documentation](http://www.open3d.org/docs/latest/) (for `main` branch)." required: true - type: textarea diff --git a/.github/ISSUE_TEMPLATE/3-feature-request.yml b/.github/ISSUE_TEMPLATE/3-feature-request.yml index 73478dccd..eb3645bc0 100644 --- a/.github/ISSUE_TEMPLATE/3-feature-request.yml +++ b/.github/ISSUE_TEMPLATE/3-feature-request.yml @@ -13,7 +13,7 @@ body: required: true - label: "I have tested with the [latest development wheel](http://www.open3d.org/docs/latest/getting_started.html#development-version-pip)." required: true - - label: "I have checked the [release documentation](http://www.open3d.org/docs/release/) and the [latest documentation](http://www.open3d.org/docs/latest/) (for `master` branch)." + - label: "I have checked the [release documentation](http://www.open3d.org/docs/release/) and the [latest documentation](http://www.open3d.org/docs/latest/) (for `main` branch)." required: true - type: textarea diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index 0ebf751fd..c64247c92 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -3,7 +3,7 @@ name: Style check on: workflow_dispatch: push: - branches: [master, dev] + branches: [main, dev] pull_request: types: [opened, reopened, synchronize] diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 0362236f0..a8ed5e533 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -3,7 +3,7 @@ name: Ubuntu CI on: workflow_dispatch: push: - branches: [master, dev] + branches: [main, dev] pull_request: types: [opened, reopened, synchronize] diff --git a/README.md b/README.md index f565eb5bb..e710989ed 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- + ML

@@ -199,8 +199,8 @@ pipeline.run_train() ``` -For more examples see [`examples/`](https://github.com/isl-org/Open3D-ML/tree/master/examples) -and the [`scripts/`](https://github.com/isl-org/Open3D-ML/tree/master/scripts) directories. You +For more examples see [`examples/`](https://github.com/isl-org/Open3D-ML/tree/main/examples) +and the [`scripts/`](https://github.com/isl-org/Open3D-ML/tree/main/scripts) directories. You can also enable saving training summaries in the config file and visualize ground truth and results with tensorboard. See this [tutorial](docs/tensorboard.md#3dml-models-training-and-inference) for details. @@ -272,10 +272,10 @@ pipeline.run_train() Below is an example of visualization using KITTI. The example shows the use of bounding boxes for the KITTI dataset. - + -For more examples see [`examples/`](https://github.com/isl-org/Open3D-ML/tree/master/examples) -and the [`scripts/`](https://github.com/isl-org/Open3D-ML/tree/master/scripts) directories. You +For more examples see [`examples/`](https://github.com/isl-org/Open3D-ML/tree/main/examples) +and the [`scripts/`](https://github.com/isl-org/Open3D-ML/tree/main/scripts) directories. You can also enable saving training summaries in the config file and visualize ground truth and results with tensorboard. See this [tutorial](docs/tensorboard.md#3dml-models-training-and-inference) for details. @@ -284,7 +284,7 @@ for details. ### Using predefined scripts -[`scripts/run_pipeline.py`](https://github.com/isl-org/Open3D-ML/blob/master/scripts/run_pipeline.py) +[`scripts/run_pipeline.py`](https://github.com/isl-org/Open3D-ML/blob/main/scripts/run_pipeline.py) provides an easy interface for training and evaluating a model on a dataset. It saves the trouble of defining specific model and passing exact configuration. @@ -409,7 +409,7 @@ The following is a list of datasets for which we provide dataset reader classes. - Waymo ([project-page](https://waymo.com/open/)) - ScanNet([project-page](http://www.scan-net.org/)) -For downloading these datasets visit the respective webpages and have a look at the scripts in [`scripts/download_datasets`](https://github.com/isl-org/Open3D-ML/tree/master/scripts/download_datasets). +For downloading these datasets visit the respective webpages and have a look at the scripts in [`scripts/download_datasets`](https://github.com/isl-org/Open3D-ML/tree/main/scripts/download_datasets). ## How-tos diff --git a/ci/run_ci.sh b/ci/run_ci.sh index f4ae82eca..0d10a3d6c 100755 --- a/ci/run_ci.sh +++ b/ci/run_ci.sh @@ -9,8 +9,8 @@ echo 1. Prepare the Open3D-ML repo and install dependencies echo export PATH_TO_OPEN3D_ML="$PWD" echo "$PATH_TO_OPEN3D_ML" -# the build system of the main repo expects a master branch. make sure master exists -git checkout -b master || true +# the build system of the main repo expects a main branch. make sure main exists +git checkout -b main || true python -m pip install -U pip==$PIP_VER python -m pip install -r requirements.txt \ -r requirements-torch.txt @@ -21,7 +21,7 @@ python -m pip install -U Cython echo 2. clone Open3D and install dependencies echo -git clone --branch master https://github.com/isl-org/Open3D.git +git clone --branch main https://github.com/isl-org/Open3D.git ./Open3D/util/install_deps_ubuntu.sh assume-yes python -m pip install -r Open3D/python/requirements.txt \ diff --git a/model_zoo.md b/model_zoo.md index 829c4a3bc..482d8f000 100644 --- a/model_zoo.md +++ b/model_zoo.md @@ -34,5 +34,5 @@ The following is a list of datasets for which we provide dataset reader classes. * S3DIS ([project-page](http://3dsemantics.stanford.edu/)) * Paris-Lille 3D ([project-page](https://npm3d.fr/paris-lille-3d)) -For downloading these datasets visit the respective webpages and have a look at the scripts in [`scripts/download_datasets`](https://github.com/isl-org/Open3D-ML/tree/master/scripts/download_datasets). +For downloading these datasets visit the respective webpages and have a look at the scripts in [`scripts/download_datasets`](https://github.com/isl-org/Open3D-ML/tree/main/scripts/download_datasets). diff --git a/scripts/run_pipeline.py b/scripts/run_pipeline.py index 91a9e409a..895f470f7 100644 --- a/scripts/run_pipeline.py +++ b/scripts/run_pipeline.py @@ -199,8 +199,8 @@ def main(): def setup(rank, world_size, args): - os.environ['MASTER_ADDR'] = args.host - os.environ['MASTER_PORT'] = args.port + os.environ['PRIMARY_ADDR'] = args.host + os.environ['PRIMARY_PORT'] = args.port # initialize the process group dist.init_process_group(args.backend, rank=rank, world_size=world_size) diff --git a/scripts/train_scripts/pointpillars_waymo.sh b/scripts/train_scripts/pointpillars_waymo.sh index a1cd7dadc..f135e9083 100644 --- a/scripts/train_scripts/pointpillars_waymo.sh +++ b/scripts/train_scripts/pointpillars_waymo.sh @@ -15,9 +15,9 @@ if [ "$#" -ne 2 ]; then exit 1 fi -# Use launch node as master, if not set -export MASTER_ADDR=${MASTER_ADDR:-$SLURMD_NODENAME} -export MASTER_PORT=${MASTER_PORT:-29500} +# Use launch node as main, if not set +export PRIMARY_ADDR=${PRIMARY_ADDR:-$SLURMD_NODENAME} +export PRIMARY_PORT=${PRIMARY_PORT:-29500} # Use all available GPUs, if not set. Must be the same for ALL nodes. export DEVICE_IDS=${DEVICE_IDS:-$(nvidia-smi --list-gpus | cut -f2 -d' ' | tr ':\n' ' ')} export NODE_RANK=${NODE_RANK:-SLURM_NODEID} # Pass name of env var @@ -32,7 +32,7 @@ srun -l python scripts/run_pipeline.py "$1" -c ml3d/configs/pointpillars_waymo.y --backend nccl \ --nodes $SLURM_JOB_NUM_NODES \ --node_rank "$NODE_RANK" \ - --host "$MASTER_ADDR" --port "$MASTER_PORT" + --host "$PRIMARY_ADDR" --port "$PRIMARY_PORT" echo Completed at: $(date) popd From a3ced7af339cc25d81ebfcf08da4df1d555898c1 Mon Sep 17 00:00:00 2001 From: Sameer Sheorey Date: Fri, 22 Dec 2023 14:31:09 -0800 Subject: [PATCH 7/7] [@SulekBartek] Added Pandaset autonomous driving dataset class and configuration file for RandLANet. (#611) Fixup to earlier merge commit. Add dataset info (license, citation, website, download, etc.) Style fix Author: SulekBartek --- ml3d/datasets/pandaset.py | 188 +++++++++++++++++++++----------------- 1 file changed, 106 insertions(+), 82 deletions(-) diff --git a/ml3d/datasets/pandaset.py b/ml3d/datasets/pandaset.py index 6037fc816..d39c620b3 100644 --- a/ml3d/datasets/pandaset.py +++ b/ml3d/datasets/pandaset.py @@ -1,25 +1,46 @@ import os from os.path import join -import numpy as np -import pandas as pd from pathlib import Path import logging +import numpy as np +import pandas as pd from .base_dataset import BaseDataset, BaseDatasetSplit from ..utils import make_dir, DATASET log = logging.getLogger(__name__) + class Pandaset(BaseDataset): - """ This class is used to create a dataset based on the Pandaset autonomous + """This class is used to create a dataset based on the Pandaset autonomous driving dataset. - https://pandaset.org/ - - The dataset includes 42 semantic classes and covers more than 100 scenes, - each of which is 8 seconds long. - + PandaSet aims to promote and advance research and development in autonomous + driving and machine learning. The first open-source AV dataset available for + both academic and commercial use, PandaSet combines Hesai’s best-in-class + LiDAR sensors with Scale AI’s high-quality data annotation. + + PandaSet features data collected using a forward-facing LiDAR with + image-like resolution (PandarGT) as well as a mechanical spinning LiDAR + (Pandar64). The collected data was annotated with a combination of cuboid + and segmentation annotation (Scale 3D Sensor Fusion Segmentation). + + It features:: + + - 48,000+ camera images + - 16,000+ LiDAR sweeps + - 100+ scenes of 8s each + - 28 annotation classes + - 37 semantic segmentation labels + - Full sensor suite: 1x mechanical spinning LiDAR, 1x forward-facing LiDAR, 6x cameras, On-board GPS/IMU + + Website: https://pandaset.org/ + Code: https://github.com/scaleapi/pandaset-devkit + Download: https://www.kaggle.com/datasets/usharengaraju/pandaset-dataset/data + Data License: CC0: Public Domain (https://scale.com/legal/pandaset-terms-of-use) + Citation: https://arxiv.org/abs/2112.12610 """ + def __init__(self, dataset_path, name="Pandaset", @@ -27,30 +48,34 @@ def __init__(self, use_cache=False, ignored_label_inds=[], test_result_folder='./logs/test_log', - test_split=['115', '116', '117', '119', '120', '124', '139', '149', '158'], + test_split=[ + '115', '116', '117', '119', '120', '124', '139', '149', + '158' + ], training_split=[ - '001', '002', '003', '005', '011', '013', '015', '016', - '017', '019', '021', '023', '024', '027', '028', '029', - '030', '032', '033', '034', '035', '037', '038', '039', - '040', '041', '042', '043', '044', '046', '052', '053', - '054', '056', '057', '058', '064', '065', '066', '067', - '070', '071', '072', '073', '077', '078', '080', '084', - '088', '089', '090', '094', '095', '097', '098', '101', - '102', '103', '105', '106', '109', '110', '112', '113' + '001', '002', '003', '005', '011', '013', '015', '016', + '017', '019', '021', '023', '024', '027', '028', '029', + '030', '032', '033', '034', '035', '037', '038', '039', + '040', '041', '042', '043', '044', '046', '052', '053', + '054', '056', '057', '058', '064', '065', '066', '067', + '070', '071', '072', '073', '077', '078', '080', '084', + '088', '089', '090', '094', '095', '097', '098', '101', + '102', '103', '105', '106', '109', '110', '112', '113' + ], + validation_split=['122', '123'], + all_split=[ + '001', '002', '003', '005', '011', '013', '015', '016', + '017', '019', '021', '023', '024', '027', '028', '029', + '030', '032', '033', '034', '035', '037', '038', '039', + '040', '041', '042', '043', '044', '046', '052', '053', + '054', '056', '057', '058', '064', '065', '066', '067', + '069', '070', '071', '072', '073', '077', '078', '080', + '084', '088', '089', '090', '094', '095', '097', '098', + '101', '102', '103', '105', '106', '109', '110', '112', + '113', '115', '116', '117', '119', '120', '122', '123', + '124', '139', '149', '158' ], - validation_split=['122', '123'], - all_split=['001', '002', '003', '005', '011', '013', '015', '016', - '017', '019', '021', '023', '024', '027', '028', '029', - '030', '032', '033', '034', '035', '037', '038', '039', - '040', '041', '042', '043', '044', '046', '052', '053', - '054', '056', '057', '058', '064', '065', '066', '067', - '069', '070', '071', '072', '073', '077', '078', '080', - '084', '088', '089', '090', '094', '095', '097', '098', - '101', '102', '103', '105', '106', '109', '110', '112', - '113', '115', '116', '117', '119', '120', '122', '123', - '124', '139', '149', '158'], **kwargs): - """Initialize the function by passing the dataset and other details. Args: @@ -79,7 +104,7 @@ def __init__(self, self.label_to_names = self.get_label_to_names() self.num_classes = len(self.label_to_names) self.label_values = np.sort([k for k, v in self.label_to_names.items()]) - + @staticmethod def get_label_to_names(): """Returns a label to names dictionary object. @@ -89,48 +114,48 @@ def get_label_to_names(): values are the corresponding names. """ label_to_names = { - 1: "Reflection", - 2: "Vegetation", - 3: "Ground", - 4: "Road", - 5: "Lane Line Marking", - 6: "Stop Line Marking", - 7: "Other Road Marking", - 8: "Sidewalk", - 9: "Driveway", - 10: "Car", - 11: "Pickup Truck", - 12: "Medium-sized Truck", - 13: "Semi-truck", - 14: "Towed Object", - 15: "Motorcycle", - 16: "Other Vehicle - Construction Vehicle", - 17: "Other Vehicle - Uncommon", - 18: "Other Vehicle - Pedicab", - 19: "Emergency Vehicle", - 20: "Bus", - 21: "Personal Mobility Device", - 22: "Motorized Scooter", - 23: "Bicycle", - 24: "Train", - 25: "Trolley", - 26: "Tram / Subway", - 27: "Pedestrian", - 28: "Pedestrian with Object", - 29: "Animals - Bird", - 30: "Animals - Other", - 31: "Pylons", - 32: "Road Barriers", - 33: "Signs", - 34: "Cones", - 35: "Construction Signs", - 36: "Temporary Construction Barriers", - 37: "Rolling Containers", - 38: "Building", + 1: "Reflection", + 2: "Vegetation", + 3: "Ground", + 4: "Road", + 5: "Lane Line Marking", + 6: "Stop Line Marking", + 7: "Other Road Marking", + 8: "Sidewalk", + 9: "Driveway", + 10: "Car", + 11: "Pickup Truck", + 12: "Medium-sized Truck", + 13: "Semi-truck", + 14: "Towed Object", + 15: "Motorcycle", + 16: "Other Vehicle - Construction Vehicle", + 17: "Other Vehicle - Uncommon", + 18: "Other Vehicle - Pedicab", + 19: "Emergency Vehicle", + 20: "Bus", + 21: "Personal Mobility Device", + 22: "Motorized Scooter", + 23: "Bicycle", + 24: "Train", + 25: "Trolley", + 26: "Tram / Subway", + 27: "Pedestrian", + 28: "Pedestrian with Object", + 29: "Animals - Bird", + 30: "Animals - Other", + 31: "Pylons", + 32: "Road Barriers", + 33: "Signs", + 34: "Cones", + 35: "Construction Signs", + 36: "Temporary Construction Barriers", + 37: "Rolling Containers", + 38: "Building", 39: "Other Static Object" } return label_to_names - + def get_split(self, split): """Returns a dataset split. @@ -142,7 +167,7 @@ def get_split(self, split): A dataset split object providing the requested subset of the data. """ return PandasetSplit(self, split=split) - + def get_split_list(self, split): """Returns the list of data splits available. @@ -154,8 +179,8 @@ def get_split_list(self, split): A dataset split object providing the requested subset of the data. Raises: - ValueError: Indicates that the split name passed is incorrect. The split name should be one of - 'training', 'test', 'validation', or 'all'. + ValueError: Indicates that the split name passed is incorrect. The + split name should be one of 'training', 'test', 'validation', or 'all'. """ cfg = self.cfg dataset_path = cfg.dataset_path @@ -179,7 +204,7 @@ def get_split_list(self, split): file_list.append(join(pc_path, f)) return file_list - + def is_tested(self, attr): """Checks if a datum in the dataset has been tested. @@ -224,7 +249,7 @@ def save_test_result(self, results, attr): class PandasetSplit(BaseDatasetSplit): """This class is used to create a split for Pandaset dataset. - Args: + Args: dataset: The dataset to split. split: A string identifying the dataset split that is usually one of 'training', 'test', 'validation', or 'all'. @@ -233,6 +258,7 @@ class PandasetSplit(BaseDatasetSplit): Returns: A dataset split object providing the requested subset of the data. """ + def __init__(self, dataset, split='train'): super().__init__(dataset, split=split) log.info("Found {} pointclouds for {}".format(len(self.path_list), @@ -244,19 +270,16 @@ def __len__(self): def get_data(self, idx): pc_path = self.path_list[idx] label_path = pc_path.replace('lidar', 'annotations/semseg') - + points = pd.read_pickle(pc_path) labels = pd.read_pickle(label_path) - + intensity = points['i'].to_numpy().astype(np.float32) - points = points.drop(columns=['i', 't', 'd']).to_numpy().astype(np.float32) + points = points.drop(columns=['i', 't', 'd']).to_numpy().astype( + np.float32) labels = labels.to_numpy().astype(np.int32) - data = { - 'point': points, - 'intensity': intensity, - 'label': labels - } + data = {'point': points, 'intensity': intensity, 'label': labels} return data @@ -269,4 +292,5 @@ def get_attr(self, idx): attr = {'name': name, 'path': pc_path, 'split': self.split} return attr + DATASET._register_module(Pandaset)