diff --git a/nibio_postprocessing/las_to_pandas.py b/nibio_postprocessing/las_to_pandas.py
index 05ca58a38f87e7a518986e0183203269f7a9c63e..08acfa2e8222d7d6c2836ba2e49d1a80bc38954e 100644
--- a/nibio_postprocessing/las_to_pandas.py
+++ b/nibio_postprocessing/las_to_pandas.py
@@ -2,7 +2,7 @@ import numpy as np
 import pandas as pd
 import laspy
 
-def las_to_pandas(las_file_path, csv_file_path):
+def las_to_pandas(las_file_path, csv_file_path, save_csv=True):
     """
     Reads a LAS file and converts it to a pandas dataframe, then saves it to a CSV file.
 
@@ -30,4 +30,7 @@ def las_to_pandas(las_file_path, csv_file_path):
     points_df = pd.DataFrame(all_points, columns=all_columns)
 
     # Save pandas dataframe to csv
-    points_df.to_csv(csv_file_path, index=False, header=True, sep=',')
+    if save_csv:
+        points_df.to_csv(csv_file_path, index=False, header=True, sep=',')
+
+    return points_df
diff --git a/nibio_preprocessing/label_based_low_veg_relabelling.py b/nibio_preprocessing/label_based_low_veg_relabelling.py
new file mode 100644
index 0000000000000000000000000000000000000000..cbf4069bd20e3721db191f9e2dc4efba503176fb
--- /dev/null
+++ b/nibio_preprocessing/label_based_low_veg_relabelling.py
@@ -0,0 +1,58 @@
+from typing import Any
+from nibio_postprocessing.las_to_pandas import las_to_pandas
+from nibio_postprocessing.pandas_to_las import pandas_to_las
+
+
+class LabelBasedLowVegRelabelling(object):
+    GROUND_CLASS = 1
+    VEGETATION_CLASS = 2
+
+    def __init__(self, input_las_file, output_las_file, verbose) -> None:
+        self.input_las_file = input_las_file
+        self.output_las_file = output_las_file
+        self.verbose = verbose
+
+    def relabel(self):
+        # Read the file
+        df = las_to_pandas(self.input_las_file, self.verbose, save_csv=False)
+
+        # get all the rows which belong to the vegetation (2) class (label) and have treeID 0 and move it to ground class (1)
+        df.loc[(df['label'] == self.VEGETATION_CLASS) & (df['treeID'] == 0), 'label'] = self.GROUND_CLASS
+
+        # save the dataframe to a new las file
+        pandas_to_las(df, self.output_las_file, csv_file_provided=False, verbose=self.verbose)
+
+        if self.verbose:
+            print(f"Relabelled {self.input_las_file} and saved to {self.output_las_file}")
+    
+    def run(self):
+        self.relabel()
+        if self.verbose:
+            print("Relabelling complete.")
+
+    def __call__(self) -> Any:
+        self.run()
+
+
+if __name__ == '__main__':
+    import argparse
+    parser = argparse.ArgumentParser(description='Relabel low vegetation to ground class')
+    parser.add_argument('-i', '--input_las_file', help='Input LAS file', required=True)
+    parser.add_argument('-o', '--output_las_file', help='Output LAS file', required=True)
+    parser.add_argument('-v', '--verbose', action='store_true', help="Print information about the process")
+
+    args = vars(parser.parse_args())
+
+    input_las_file = args['input_las_file']
+    output_las_file = args['output_las_file']
+    verbose = args['verbose']
+
+    # create an instance of LabelBasedLowVegRelabelling class
+    label_based_low_veg_relabelling = LabelBasedLowVegRelabelling(
+        input_las_file=input_las_file,
+        output_las_file=output_las_file,
+        verbose=verbose
+    )
+
+    # call main function
+    label_based_low_veg_relabelling()
\ No newline at end of file
diff --git a/nibio_preprocessing/label_based_low_veg_relabelling_in_folder.py b/nibio_preprocessing/label_based_low_veg_relabelling_in_folder.py
new file mode 100644
index 0000000000000000000000000000000000000000..e803811f8d1fcf93d58d8235ed3a6674a430987c
--- /dev/null
+++ b/nibio_preprocessing/label_based_low_veg_relabelling_in_folder.py
@@ -0,0 +1,49 @@
+import os
+import glob
+from joblib import Parallel, delayed
+from nibio_preprocessing.label_based_low_veg_relabelling import LabelBasedLowVegRelabelling
+
+def process_single_file(input_file, output_folder, verbose):
+    try:
+        file_name = os.path.basename(input_file)
+        file_name = file_name.replace('.las', '_relabeled.las')
+        output_file = os.path.join(output_folder, file_name)
+        
+        if verbose:
+            print(f"Processing {file_name}...")
+        
+        relabeller = LabelBasedLowVegRelabelling(input_file, output_file, verbose)
+        relabeller()
+        
+    except Exception as e:
+        print(f"Error processing {file_name}: {e}")
+        import traceback
+        print(traceback.format_exc())
+
+def process_directory(input_folder, output_folder, verbose=False):
+    # Make sure output folder exists, if not, create it
+    if not os.path.exists(output_folder):
+        os.makedirs(output_folder)
+
+    # List all .las files in the input folder
+    input_files = glob.glob(os.path.join(input_folder, "*.las"))
+
+    # Parallel processing
+    n_jobs = -1  # Use all available cores
+    Parallel(n_jobs=n_jobs)(delayed(process_single_file)(input_file, output_folder, verbose) for input_file in input_files)
+
+
+if __name__ == "__main__":
+    import argparse
+    parser = argparse.ArgumentParser(description='Process all LAS files in a folder to relabel low vegetation to ground class.')
+    parser.add_argument('-i', '--input_folder', help='Input folder containing LAS files', required=True)
+    parser.add_argument('-o', '--output_folder', help='Output folder to store relabeled LAS files', required=True)
+    parser.add_argument('-v', '--verbose', action='store_true', help="Print information about the process")
+
+    args = vars(parser.parse_args())
+
+    input_folder = args['input_folder']
+    output_folder = args['output_folder']
+    verbose = args['verbose']
+
+    process_directory(input_folder, output_folder, verbose)