diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 03dc47a0..7371105a 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -40,8 +40,8 @@ jobs: - name: Example inference run via Docker with default config and checkpoint run: > docker run - -v /var/data/cicd/CICD_github_assets/myria3d_V3.3.0/inputs/:/inputs/ - -v /var/data/cicd/CICD_github_assets/myria3d_V3.3.0/outputs/:/outputs/ + -v /var/data/cicd/CICD_github_assets/myria3d_V3.4.0/inputs/:/inputs/ + -v /var/data/cicd/CICD_github_assets/myria3d_V3.4.0/outputs/:/outputs/ --ipc=host --shm-size=2gb myria3d @@ -54,14 +54,14 @@ jobs: - name: Example inference run via Docker with inference-time subtiles overlap to smooth-out results. run: > docker run - -v /var/data/cicd/CICD_github_assets/myria3d_V3.3.0/inputs/:/inputs/ - -v /var/data/cicd/CICD_github_assets/myria3d_V3.3.0/outputs/:/outputs/ + -v /var/data/cicd/CICD_github_assets/myria3d_V3.4.0/inputs/:/inputs/ + -v /var/data/cicd/CICD_github_assets/myria3d_V3.4.0/outputs/:/outputs/ --ipc=host --shm-size=2gb myria3d python run.py --config-path /inputs/ - --config-name proto151_V2.0_epoch_100_Myria3DV3.1.0_predict_config_V3.3.0 + --config-name proto151_V2.0_epoch_100_Myria3DV3.1.0_predict_config_V3.4.0 predict.ckpt_path=/inputs/proto151_V2.0_epoch_100_Myria3DV3.1.0.ckpt predict.src_las=/inputs/792000_6272000_subset_buildings.las predict.output_dir=/outputs/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..d627d075 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,28 @@ +# main + +# 3.4.8 +- Raise an informative error in case of unexpected task_name. + +# 3.4.7 +- Remove tqdm when splitting a lidar tile to avoid cluttered logs during data preparation. + +# 3.4.6 +- Document the possible use of ign-pdal-tools for colorization. + +# 3.4.5 +- Set a default task_name (fit) to avoid common error at lauch time. + +# 3.4.4 +- Remove duplicated experiment configuration. + +# 3.4.3 +- Remove outdated and incorrect hydra parameter in config.yaml. + +# 3.4.2 +- Reconstruct absolute path of input LAS files explicitely, removing a costly glob operation. + +# 3.4.1 +- Fix dataset description for pacasam: there was an unwanted int-to-int mapping in classification_dict. + +# 3.4.0 +- Allow inference for the smallest possible patches (num_nodes=1) to have consistent inference behavior \ No newline at end of file diff --git a/configs/config.yaml b/configs/config.yaml index 80463192..13a07eb7 100755 --- a/configs/config.yaml +++ b/configs/config.yaml @@ -42,7 +42,3 @@ defaults: # enable color logging - override hydra/hydra_logging: colorlog - override hydra/job_logging: colorlog - -hydra: - searchpath: - - pkg://default_files_for_predict diff --git a/configs/datamodule/hdf5_datamodule.yaml b/configs/datamodule/hdf5_datamodule.yaml index e430d1dc..c39147a3 100755 --- a/configs/datamodule/hdf5_datamodule.yaml +++ b/configs/datamodule/hdf5_datamodule.yaml @@ -15,7 +15,7 @@ pre_filter: _target_: functools.partial _args_: - "${get_method:myria3d.pctl.dataset.utils.pre_filter_below_n_points}" - min_num_nodes: 50 + min_num_nodes: 1 tile_width: 1000 subtile_width: 50 diff --git a/configs/dataset_description/20230601_lidarhd_pacasam_dataset.yaml b/configs/dataset_description/20230601_lidarhd_pacasam_dataset.yaml new file mode 100644 index 00000000..98ef0c72 --- /dev/null +++ b/configs/dataset_description/20230601_lidarhd_pacasam_dataset.yaml @@ -0,0 +1,16 @@ +_convert_: all # For omegaconf struct to be converted to python dictionnaries +# classification_preprocessing_dict = {source_class_code_int: target_class_code_int}, +# 3: medium vegetation -> vegetation +# 4: high vegetation -> vegetation +# 0: no processing --> unclassified +# 66: synthetic points --> noise (synthetic points are useful for specific modelling task on already classified data). +# We set them to noise so that they are ignored during training. +# Codes that should not have been in the data: 100, 101. +classification_preprocessing_dict: {3: 5, 4: 5, 0: 1, 66: 65, 100: 1, 101: 1} + +# classification_dict = {code_int: name_str, ...} and MUST be sorted (increasing order). +classification_dict: {1: "unclassified", 2: "ground", 5: vegetation, 6: "building", 9: water, 17: bridge, 64: lasting_above} + +# Input and output dims of neural net are dataset dependant: +d_in: 9 +num_classes: 7 \ No newline at end of file diff --git a/configs/experiment/RandLaNet_base_run_FR_pyg_randla_net.yaml b/configs/experiment/RandLaNet_base_run_FR_pyg_randla_net.yaml deleted file mode 100755 index 5083ac1d..00000000 --- a/configs/experiment/RandLaNet_base_run_FR_pyg_randla_net.yaml +++ /dev/null @@ -1,18 +0,0 @@ -# @package _global_ -defaults: - - override /model: pyg_randla_net_model.yaml - - override /datamodule/transforms/augmentations: light.yaml - -logger: - comet: - experiment_name: "Pyg RandLaNet - FR Data" - -trainer: - num_sanity_val_steps: 2 - min_epochs: 100 - max_epochs: 150 - # gpus: [1] - -predict: - interpolator: - interpolation_k: 10 \ No newline at end of file diff --git a/docs/source/tutorials/prepare_dataset.md b/docs/source/tutorials/prepare_dataset.md index c9646672..1dff18a6 100644 --- a/docs/source/tutorials/prepare_dataset.md +++ b/docs/source/tutorials/prepare_dataset.md @@ -2,13 +2,13 @@ ## Peprocessing functions -The loading function is dataset dependant, and is `lidar_hd_pre_transform` by default. The function takes points loaded from a LAS file via pdal as input, and returns a `pytorch_geometric.Data` object following the standard naming convention of `pytorch_geometric`, plus a list of features names for later use in transforms. +The loading function is dataset dependant, and is `lidar_hd_pre_transform` by default. The function takes points loaded from a LAS file via pdal as input, and returns a `pytorch_geometric.Data` object following the standard naming convention of `pytorch_geometric`, plus a list of features names for later use in transforms. In the loading function, the return number and color information (RGBI) are scaled to 0-1 interval, a NDVI and an average color ((R+G+B)/3) dimension are created, and points that may be occluded (as indicated by higher return number) have their color set to 0. -It is adapted to the French Lidar HD data provided by IGN (see [the official page](https://geoservices.ign.fr/lidarhd) - link in French). Return number and color information (RGBI) are scaled to 0-1 interval, a NDVI and an average color ((R+G+B)/3) dimension are created, and points that may be occluded (as indicated by higher return number) have their color set to 0. +Customization: You may want to implement your own logic (e.g. with custom, additional features) in directory `points_pre_transform`. It then needs to be referenced similarly to `lidar_hd_pre_transform`. -You may want to implement your own logic (e.g. with custom, additional features) in directory `points_pre_transform`. It then needs to be referenced similarly to `lidar_hd_pre_transform`. +The loading function is designed for the French Lidar HD data provided by IGN (see [the official page](https://geoservices.ign.fr/lidarhd) - link in French). Note that the clouds are shared without color information, and should be colorized (RGB+Infrared) to use myria3d. The [open-source ign-pdal-tools library](https://pypi.org/project/ign-pdal-tools/) is a convenient toolkit that can be used to colorize the raw clouds with IGN aerial imagery (see function 'pdaltools.color.color(...)'). -If you use your own classification convention , you will need to create a `dataset_description` configuration (for an example see `configs/dataset_description/20220607_151_dalles_proto.yaml`). +Customization: If you use a different classification (e.g. additional classes), you will need to create a `dataset_description` configuration (similar to `configs/dataset_description/20220607_151_dalles_proto.yaml`). Additionnaly, you can control cloud sampling parameters via two configurations: - `configs/datamodule/transforms/preparations/points_budget.yaml`: (defaut) allows variable cloud size within lower and higher boundaries. @@ -17,15 +17,17 @@ Additionnaly, you can control cloud sampling parameters via two configurations: ## Preparing the dataset -Input point clouds need to be splitted in subtiles that can be digested by segmentation models. We found that a receptive field of 50m*50m was a good balance between context and memory intensity. For faster training, this split can be done once, to avoid loading large file in memory multiple times. - -To perform a training, you will need to specify these parameters of the datamodule config group: -- `data_dir`: path to a directory in which a set of LAS files are stored (can be nested in subdirectories). +To perform a training, you will need to specify these parameters in the datamodule config group: +- `data_dir`: path to a directory in which a set of LAS files are stored. Clouds must be nested in subdirectories named according to their spli: train, val, or test. - `split_csv_path`: path to a CSV file with schema `basename,split`, specifying a train/val/test spit for your data. -These will be composed into a single file dataset for which you can specify a path via the `datamodule.hdf5_file_path` parameter. This happens on the fly, therefore a first training might take some time, but this should only happens once. +Under the hood, the path of each LAS file will be reconstructed like this: '{data_dir}/{split}/{basename}'. + +Large input point clouds need to be divided in smaller clouds that can be digested by segmentation models. We found that a receptive field of 50m x 50m was a good balance between context and memory intensity. The division is performed once, to avoid loading large file in memory multiple times during training. + +After division, the smaller clouds are preprocessed (i.e. selection of specific LAS dimensions, on-the-fly creation of dimensions) and regrouped into a single HDF5 file whose path is specified via the `datamodule.hdf5_file_path` parameter. -Once this is done, you do not need sources anymore, and simply specifying the path to the HDF5 dataset is enough. +The HDF5 dataset is created at training time. It should only happens once. Once this is done, you do not need sources anymore, and simply specifying the path to the HDF5 dataset is enough (there is no need for data_dir or split_csv_path parameters anymore). It's also possible to create the hdf5 file without training any model: just fill the `datamodule.hdf5_file_path` parameter as before to specify the file path, but use `task=create_hdf5` instead of `task=fit`. diff --git a/myria3d/pctl/dataset/utils.py b/myria3d/pctl/dataset/utils.py index 1a23f397..7e62e958 100644 --- a/myria3d/pctl/dataset/utils.py +++ b/myria3d/pctl/dataset/utils.py @@ -1,6 +1,7 @@ import glob import json import math +from pathlib import Path import subprocess as sp from numbers import Number from typing import Dict, List, Literal, Union @@ -10,7 +11,6 @@ import pdal from scipy.spatial import cKDTree from shapely.geometry import Point -from tqdm import tqdm SPLIT_TYPE = Union[Literal["train"], Literal["val"], Literal["test"]] SHAPE_TYPE = Union[Literal["disk"], Literal["square"]] @@ -31,9 +31,7 @@ def find_file_in_dir(data_dir: str, basename: str) -> str: return files[0] -def get_mosaic_of_centers( - tile_width: Number, subtile_width: Number, subtile_overlap: Number = 0 -): +def get_mosaic_of_centers(tile_width: Number, subtile_width: Number, subtile_overlap: Number = 0): if subtile_overlap < 0: raise ValueError("datamodule.subtile_overlap must be positive.") @@ -63,9 +61,7 @@ def pdal_read_las_array(las_path: str): def pdal_read_las_array_as_float32(las_path: str): """Read LAS as a a named array, casted to floats.""" arr = pdal_read_las_array(las_path) - all_floats = np.dtype( - {"names": arr.dtype.names, "formats": ["f4"] * len(arr.dtype.names)} - ) + all_floats = np.dtype({"names": arr.dtype.names, "formats": ["f4"] * len(arr.dtype.names)}) return arr.astype(all_floats) @@ -101,6 +97,7 @@ def get_pdal_info_metadata(las_path: str) -> Dict: return json_info["metadata"] + # hdf5, iterable @@ -125,14 +122,10 @@ def split_cloud_into_samples( """ points = pdal_read_las_array_as_float32(las_path) - pos = np.asarray( - [points["X"], points["Y"], points["Z"]], dtype=np.float32 - ).transpose() + pos = np.asarray([points["X"], points["Y"], points["Z"]], dtype=np.float32).transpose() kd_tree = cKDTree(pos[:, :2] - pos[:, :2].min(axis=0)) - XYs = get_mosaic_of_centers( - tile_width, subtile_width, subtile_overlap=subtile_overlap - ) - for center in tqdm(XYs, desc="Centers"): + XYs = get_mosaic_of_centers(tile_width, subtile_width, subtile_overlap=subtile_overlap) + for center in XYs: radius = subtile_width // 2 # Square receptive field. minkowski_p = np.inf if shape == "disk": @@ -140,9 +133,7 @@ def split_cloud_into_samples( # Adapt radius to have complete coverage of the data, with a slight overlap between samples. minkowski_p = 2 radius = radius * math.sqrt(2) - sample_idx = np.array( - kd_tree.query_ball_point(center, r=radius, p=minkowski_p) - ) + sample_idx = np.array(kd_tree.query_ball_point(center, r=radius, p=minkowski_p)) if not len(sample_idx): # no points in this receptive fields continue @@ -150,7 +141,7 @@ def split_cloud_into_samples( yield sample_idx, sample_points -def pre_filter_below_n_points(data, min_num_nodes=50): +def pre_filter_below_n_points(data, min_num_nodes=1): return data.pos.shape[0] < min_num_nodes @@ -171,16 +162,15 @@ def make_circle_wkt(center, subtile_width): return wkt -def get_las_paths_by_split_dict(data_dir: str, split_csv_path: str) -> LAS_PATHS_BY_SPLIT_DICT_TYPE: +def get_las_paths_by_split_dict( + data_dir: str, split_csv_path: str +) -> LAS_PATHS_BY_SPLIT_DICT_TYPE: las_paths_by_split_dict: LAS_PATHS_BY_SPLIT_DICT_TYPE = {} split_df = pd.read_csv(split_csv_path) for phase in ["train", "val", "test"]: - basenames = split_df[ - split_df.split == phase - ].basename.tolist() - las_paths_by_split_dict[phase] = [ - find_file_in_dir(data_dir, b) for b in basenames - ] + basenames = split_df[split_df.split == phase].basename.tolist() + # Reminder: an explicit data structure with ./val, ./train, ./test subfolder is required. + las_paths_by_split_dict[phase] = [str(Path(data_dir) / phase / b) for b in basenames] if not las_paths_by_split_dict: raise FileNotFoundError( diff --git a/myria3d/pctl/transforms/transforms.py b/myria3d/pctl/transforms/transforms.py index a8f7a991..38852322 100755 --- a/myria3d/pctl/transforms/transforms.py +++ b/myria3d/pctl/transforms/transforms.py @@ -124,6 +124,8 @@ def standardize_channel(self, channel_data: torch.Tensor, clamp_sigma: int = 3): """Sample-wise standardization y* = (y-y_mean)/y_std. clamping to ignore large values.""" mean = channel_data.mean() std = channel_data.std() + 10**-6 + if torch.isnan(std): + std = 1.0 standard = (channel_data - mean) / std clamp = clamp_sigma * std clamped = torch.clamp(input=standard, min=-clamp, max=clamp) @@ -177,7 +179,6 @@ def __init__( classification_preprocessing_dict: Dict[int, int], classification_dict: Dict[int, str], ): - self._set_preprocessing_mapper(classification_preprocessing_dict) self._set_mapper(classification_dict) diff --git a/package_metadata.yaml b/package_metadata.yaml index 082464fe..72a706c6 100644 --- a/package_metadata.yaml +++ b/package_metadata.yaml @@ -1,4 +1,4 @@ -__version__: "3.3.3" +__version__: "3.4.8" __name__: "myria3d" __url__: "https://github.com/IGNF/myria3d" __description__: "Deep Learning for the Semantic Segmentation of Aerial Lidar Point Clouds" diff --git a/run.py b/run.py index af1bdaf3..3d56cad1 100755 --- a/run.py +++ b/run.py @@ -2,7 +2,9 @@ # It is safer to import comet before all other imports. import comet_ml # noqa except ImportError: - print("Warning: package comet_ml not found. This may break things if you use a comet callback.") + print( + "Warning: package comet_ml not found. This may break things if you use a comet callback." + ) from enum import Enum @@ -20,7 +22,7 @@ TASK_NAME_DETECTION_STRING = "task.task_name=" DEFAULT_DIRECTORY = "trained_model_assets/" -DEFAULT_CONFIG_FILE = "proto151_V2.0_epoch_100_Myria3DV3.1.0_predict_config_V3.3.0.yaml" +DEFAULT_CONFIG_FILE = "proto151_V2.0_epoch_100_Myria3DV3.1.0_predict_config_V3.4.0.yaml" DEFAULT_CHECKPOINT = "proto151_V2.0_epoch_100_Myria3DV3.1.0.ckpt" DEFAULT_ENV = "placeholder.env" @@ -33,15 +35,20 @@ class TASK_NAMES(Enum): HDF5 = "create_hdf5" +DEFAULT_TASK = TASK_NAMES.FIT.value + log = utils.get_logger(__name__) @hydra.main(config_path="configs/", config_name="config.yaml") -def launch_train(config: DictConfig): # pragma: no cover (it's just an initialyzer of a class/method tested elsewhere) +def launch_train( + config: DictConfig, +): # pragma: no cover (it's just an initialyzer of a class/method tested elsewhere) """Training, evaluation, testing, or finetuning of a neural network.""" # Imports should be nested inside @hydra.main to optimize tab completion # Read more here: https://github.com/facebookresearch/hydra/issues/934 from myria3d.train import train + utils.extras(config) # Pretty print config using Rich library @@ -59,7 +66,9 @@ def launch_predict(config: DictConfig): # hydra changes current directory, so we make sure the checkpoint has an absolute path if not os.path.isabs(config.predict.ckpt_path): - config.predict.ckpt_path = os.path.join(os.path.dirname(__file__), config.predict.ckpt_path) + config.predict.ckpt_path = os.path.join( + os.path.dirname(__file__), config.predict.ckpt_path + ) # Pretty print config using Rich library if config.get("print_config"): @@ -79,7 +88,9 @@ def launch_hdf5(config: DictConfig): if config.get("print_config"): utils.print_config(config, resolve=False) - las_paths_by_split_dict = get_las_paths_by_split_dict(config.datamodule.get("data_dir"), config.datamodule.get("split_csv_path")) + las_paths_by_split_dict = get_las_paths_by_split_dict( + config.datamodule.get("data_dir"), config.datamodule.get("split_csv_path") + ) create_hdf5( las_paths_by_split_dict=las_paths_by_split_dict, hdf5_file_path=config.datamodule.get("hdf5_file_path"), @@ -88,35 +99,36 @@ def launch_hdf5(config: DictConfig): subtile_shape=config.datamodule.get("subtile_shape"), pre_filter=hydra.utils.instantiate(config.datamodule.get("pre_filter")), subtile_overlap_train=config.datamodule.get("subtile_overlap_train"), - points_pre_transform=hydra.utils.instantiate(config.datamodule.get("points_pre_transform")) + points_pre_transform=hydra.utils.instantiate( + config.datamodule.get("points_pre_transform") + ), ) if __name__ == "__main__": + task_name = "fit" for arg in sys.argv: if TASK_NAME_DETECTION_STRING in arg: _, task_name = arg.split("=") break - try: - log.info(f"task selected: {task_name}") - - if task_name in [TASK_NAMES.FIT.value, TASK_NAMES.TEST.value, TASK_NAMES.FINETUNE.value]: - # load environment variables from `.env` file if it exists - # recursively searches for `.env` in all folders starting from work dir - dotenv.load_dotenv(override=True) - launch_train() + log.info(f"Task: {task_name}") - elif task_name == TASK_NAMES.PREDICT.value: - dotenv.load_dotenv(os.path.join(DEFAULT_DIRECTORY, DEFAULT_ENV)) - launch_predict() + if task_name in [TASK_NAMES.FIT.value, TASK_NAMES.TEST.value, TASK_NAMES.FINETUNE.value]: + # load environment variables from `.env` file if it exists + # recursively searches for `.env` in all folders starting from work dir + dotenv.load_dotenv(override=True) + launch_train() - elif task_name == TASK_NAMES.HDF5.value: - launch_hdf5() + elif task_name == TASK_NAMES.PREDICT.value: + dotenv.load_dotenv(os.path.join(DEFAULT_DIRECTORY, DEFAULT_ENV)) + launch_predict() - else: - log.warning("Task unknown") + elif task_name == TASK_NAMES.HDF5.value: + launch_hdf5() - except NameError as e: - log.error('a task name must be defined, with the argument "task.task_name=..."') - raise e + else: + choices = ", ".join(task.value for task in TASK_NAMES) + raise ValueError( + f"Task '{task_name}' is not known. Specify a valid task name via task.task_name. Valid choices are: {choices})" + ) diff --git a/tests/conftest.py b/tests/conftest.py index f824a613..09f9e34b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,6 +8,8 @@ from myria3d.pctl.dataset.toy_dataset import make_toy_dataset_from_test_file +SINGLE_POINT_CLOUD = "tests/data/single-point-cloud.laz" + @pytest.fixture(scope="session") def toy_dataset_hdf5_path(tmpdir_factory): diff --git a/tests/myria3d/test_train_and_predict.py b/tests/myria3d/test_train_and_predict.py index 2c9eaac1..b057cb12 100644 --- a/tests/myria3d/test_train_and_predict.py +++ b/tests/myria3d/test_train_and_predict.py @@ -8,7 +8,11 @@ from myria3d.pctl.dataset.utils import pdal_read_las_array from myria3d.predict import predict from myria3d.train import train -from tests.conftest import make_default_hydra_cfg, run_hydra_decorated_command +from tests.conftest import ( + make_default_hydra_cfg, + run_hydra_decorated_command, + SINGLE_POINT_CLOUD, +) from tests.runif import RunIf """ @@ -56,8 +60,7 @@ def test_FrenchLidar_RandLaNetDebug_with_gpu(toy_dataset_hdf5_path, tmpdir_facto # Attention to concurrency with other processes using the GPU when running tests. gpu_id = 0 cfg_one_epoch = make_default_hydra_cfg( - overrides=["experiment=RandLaNetDebug", f"trainer.gpus=[{gpu_id}]"] - + tmp_paths_overrides + overrides=["experiment=RandLaNetDebug", f"trainer.gpus=[{gpu_id}]"] + tmp_paths_overrides ) train(cfg_one_epoch) @@ -84,9 +87,22 @@ def test_predict_as_command(one_epoch_trained_RandLaNet_checkpoint, tmpdir): run_hydra_decorated_command(command) -def test_RandLaNet_predict_with_invariance_checks( - one_epoch_trained_RandLaNet_checkpoint, tmpdir -): +def test_predict_on_single_point_cloud(one_epoch_trained_RandLaNet_checkpoint, tmpdir): + """Test running inference by CLI for cloud with a single point (edge case addressed in V3.4.0)""" + # Hydra changes CWD, and therefore absolute paths are preferred + abs_path_to_single_point_cloud = osp.abspath(SINGLE_POINT_CLOUD) + command = [ + "run.py", + f"predict.ckpt_path={one_epoch_trained_RandLaNet_checkpoint}", + f"predict.src_las={abs_path_to_single_point_cloud}", + f"predict.output_dir={tmpdir}", + "+predict.interpolator.probas_to_save=[building,unclassified]", + "task.task_name=predict", + ] + run_hydra_decorated_command(command) + + +def test_RandLaNet_predict_with_invariance_checks(one_epoch_trained_RandLaNet_checkpoint, tmpdir): """Train a model for one epoch, and run test and predict functions using the trained model. Args: @@ -235,9 +251,7 @@ def check_las_invariance(las_path_1: str, las_path_2: str): assert pytest.approx(np.sum(a2[d]), rel_tolerance) == np.sum(a1[d]) -def _make_list_of_necesary_hydra_overrides_with_tmp_paths( - toy_dataset_hdf5_path: str, tmpdir: str -): +def _make_list_of_necesary_hydra_overrides_with_tmp_paths(toy_dataset_hdf5_path: str, tmpdir: str): """Get list of overrides for hydra, the ones that are always needed when calling train/test. Args: diff --git a/trained_model_assets/proto151_V2.0_epoch_100_Myria3DV3.1.0_predict_config_V3.3.0.yaml b/trained_model_assets/proto151_V2.0_epoch_100_Myria3DV3.1.0_predict_config_V3.4.0.yaml similarity index 99% rename from trained_model_assets/proto151_V2.0_epoch_100_Myria3DV3.1.0_predict_config_V3.3.0.yaml rename to trained_model_assets/proto151_V2.0_epoch_100_Myria3DV3.1.0_predict_config_V3.4.0.yaml index e89f39b1..acd15868 100644 --- a/trained_model_assets/proto151_V2.0_epoch_100_Myria3DV3.1.0_predict_config_V3.3.0.yaml +++ b/trained_model_assets/proto151_V2.0_epoch_100_Myria3DV3.1.0_predict_config_V3.4.0.yaml @@ -129,7 +129,7 @@ datamodule: _target_: functools.partial _args_: - ${get_method:myria3d.pctl.dataset.utils.pre_filter_below_n_points} - min_num_nodes: 50 + min_num_nodes: 1 tile_width: 1000 subtile_width: 50 subtile_shape: square