diff --git a/nibio_preprocessing/distance_filtering_dem_based.py b/nibio_preprocessing/distance_filtering_dem_based.py
new file mode 100644
index 0000000000000000000000000000000000000000..224300bc00699a782dc246079e0fb367b6fefc68
--- /dev/null
+++ b/nibio_preprocessing/distance_filtering_dem_based.py
@@ -0,0 +1,153 @@
+
+import argparse
+
+import laspy
+import numpy as np
+import pandas as pd
+
+from scipy.interpolate import griddata
+from scipy.spatial import KDTree
+
+
+class DistanceFilteringDemBased(object):
+    # MLS data : 1 - ground, 2 - vegetation, 3 - CWD, 4 - trunk
+    GROUND_CLASS = 1
+    TARGET_CLASS = 2 
+
+    def __init__(self, las_file, distance, verbose=False):
+        # compute distance filtering based on DEM
+        # remove points which are smaller than distance from the DEM
+        # low vegetation - 0.01 m
+
+        self.point_cloud_file = las_file
+        self.distance = distance
+        self.verbose = verbose
+
+    def read_las_and_put_to_pandas(self, las_file):
+        # Read the file
+        file_content = laspy.read(las_file)
+
+        # Put x, y, z, label into a numpy array
+        points = np.vstack((file_content.x, file_content.y, file_content.z, file_content.label, file_content.treeID)).T
+
+        # Put x, y, z, label into a pandas dataframe
+        points_df = pd.DataFrame(points, columns=['x', 'y', 'z', 'label', 'treeID'])
+
+        # get points which belong to the ground class
+        ground_points = points_df[points_df['label'] == self.GROUND_CLASS]
+
+        # get points which belong to the target class
+        target_points = points_df[points_df['label'] == self.TARGET_CLASS]
+
+        # get just x, y, z columns from ground_points
+        ground_points = ground_points[['x', 'y', 'z']]
+
+        # get just x, y, z columns from target_points
+        target_points = target_points[['x', 'y', 'z']]
+
+        return points_df, ground_points, target_points
+
+    def compute_dem_for_ground(self, ground_points):
+        # DEM - Digital Elevation Model
+
+        # create digital elevation model (DEM) from label 1 points
+        x = ground_points['x']
+        y = ground_points['y']
+        z = ground_points['z']
+
+        # create a grid of points
+        xi = np.linspace(x.min(), x.max(), 1000)
+        yi = np.linspace(y.min(), y.max(), 1000)
+        xi, yi = np.meshgrid(xi, yi)
+
+        # interpolate
+        zi = griddata((x, y), z, (xi, yi), method='linear')
+
+        # fill the NaN values and missing and numertical values  with 0
+        zi = np.nan_to_num(zi, copy=False)
+
+        # replace non-numerical values with 0
+        zi[zi == np.inf] = 0
+
+        # reduce precision of the DEM to 2 decimal places
+        zi = np.around(zi, decimals=2)
+
+        # put DEM into pandas dataframe 
+        dem = pd.DataFrame({'x': xi.flatten(), 'y': yi.flatten(), 'z': zi.flatten(), })
+
+        return dem
+
+    def compute_distance_between_dem_and_target(self, dem, target_points):
+        original_target_points = target_points.copy()
+        target_points = target_points.values
+
+        # Create the KD-tree using the DEM data
+        dem_tree = KDTree(dem[:, :2])
+
+        # Compute distances for all target points
+        _, id = dem_tree.query(target_points[:, :2], workers=-1)
+
+        # get distance in z direction between target points and dem
+        original_target_points['z_distance'] = original_target_points['z'] - dem[id, 2]
+
+        # add the distance in z direction to the original dataframe as the column 'z_distance'
+        original_target_points['z_distance'] = original_target_points['z_distance'].values
+
+        return original_target_points
+
+
+    def filter_points(self, target_points_with_distances):
+        # filter points
+        target_points_with_distances = target_points_with_distances[target_points_with_distances['z_distance'] < self.distance]
+
+        return target_points_with_distances
+  
+
+    def update_las_file(self, points_df, target_points_with_distances):
+        # remove all the points which belong to the target class and are in the target_points_with_distances dataframe
+        points_df = points_df[~points_df.isin(target_points_with_distances)].dropna()
+
+        return points_df
+
+    def save_las_file(self):
+        # save las file
+        pass
+
+    def run(self):
+        points_df, ground_points, target_points = self.read_las_and_put_to_pandas(las_file=self.point_cloud_file) 
+        dem = self.compute_dem_for_ground(ground_points=ground_points)
+
+        # save pandas dataframe to csv
+        dem.to_csv('maciek/dem_from_class.csv', index=False, header=True, sep=',')
+
+        target_points_with_distances = self.compute_distance_between_dem_and_target(dem=dem.values, target_points=target_points)
+
+        # save pandas dataframe to csv
+        target_points_with_distances.to_csv('maciek/target_points_with_distances.csv', index=False, header=True, sep=',')
+
+        filtered_points = self.filter_points(target_points_with_distances=target_points_with_distances)
+
+        # save pandas dataframe to csv
+        filtered_points.to_csv('maciek/filtered_points.csv', index=False, header=True, sep=',')
+
+        points_df = self.update_las_file(points_df=points_df, target_points_with_distances=filtered_points)
+
+        # save pandas dataframe to csv
+        points_df.to_csv('maciek/updated_points.csv', index=False, header=True, sep=',')
+
+
+if __name__ == '__main__':
+    # parse the arguments
+    parser = argparse.ArgumentParser(description='Distance filtering based on DEM')
+    parser.add_argument('-i', '--input', help='Input file', required=True)
+    parser.add_argument('-d', '--distance', help='Distance', default=0.01, required=False, type=float)
+    parser.add_argument('-v', '--verbose', help='Verbose', required=False)
+    args = vars(parser.parse_args())
+
+    # get the arguments
+    LAS_FILE = args['input']    
+    DISTANCE = args['distance']
+    VERBOSE = args['verbose']
+
+    # run the script
+    DistanceFilteringDemBased(LAS_FILE, DISTANCE, VERBOSE).run()