Skip to content
Snippets Groups Projects
sparsify_las_based_sq_m.py 3.33 KiB
import argparse
import os
import numpy as np
import random
import laspy
from scipy.spatial import ConvexHull
import logging


class SparsifyLasBasedSqM:
    def __init__(self, input_file, output_folder=None, target_density=10, verbose=False):
        self.input_file = input_file
        if output_folder is None:
            self.output_folder = os.path.dirname(input_file)
        self.target_density = target_density
        self.desity = None
        self.new_point_cloud_density = None
        self.verbose = verbose

        # Initialize logging
        self.logger = logging.getLogger(__name__)
        if self.verbose:
            self.logger.setLevel(logging.INFO)
        else:
            self.logger.setLevel(logging.WARNING)

        self.logger.info(f"Initialized with input file: {self.input_file}, target density: {self.target_density}")

    def calculate_density_convex_hull(self, las):
        points_3D = np.vstack((las.x, las.y, las.z)).transpose()
        points_2D = points_3D[:, :2]
        hull = ConvexHull(points_2D)
        area = hull.volume
        point_count = len(points_2D)
        density = point_count / area
        return density

    def sparsify(self, point_cloud):


        self.density = self.calculate_density_convex_hull(point_cloud)
        self.logger.info(f"Point cloud density: {self.density} points per square meter.")
        x = point_cloud.x
        keep_count = int(len(x) * (self.target_density / self.density))
        sampled_indices = random.sample(range(len(x)), keep_count)
        filtered_point_cloud = point_cloud.points[sampled_indices]
        self.new_point_cloud_density =self.calculate_density_convex_hull(filtered_point_cloud)
        self.logger.info(f"Reduced point cloud size from {len(x)} to {len(filtered_point_cloud)} points.")
        self.logger.info(f"Reduced point cloud by {(1 - len(filtered_point_cloud) / len(x)) * 100}%.")
        self.logger.info(f"New point cloud density: {self.new_point_cloud_density} points per square meter.")
        return filtered_point_cloud

    def process(self):
        if not os.path.exists(self.output_folder):
            os.makedirs(self.output_folder)
        inFile = laspy.read(self.input_file)
        filtered_points = self.sparsify(inFile)
        self.logger.info("Creating output laspy object")
        outFile = laspy.create(point_format=inFile.point_format, file_version=inFile.header.version)
        outFile.header = inFile.header
        outFile.points = filtered_points
        output_file_path = os.path.join(self.output_folder, os.path.basename(self.input_file).replace(".las", f"_sparse_{self.target_density}.las"))
        outFile.write(output_file_path)


if __name__ == "__main__":
    # Configure logging
    logging.basicConfig(level=logging.INFO)

    parser = argparse.ArgumentParser()
    parser.add_argument("-i", "--input_file", help="The .las file to sparsify.")
    parser.add_argument("--output_folder", default=None, help="The folder where the sparse point cloud will be saved.")
    parser.add_argument("-d", "--target_density", help="Target density of points per square meter.", default=10, type=int)
    parser.add_argument("-v", "--verbose", help="Enable verbose logging", action="store_true")
    args = parser.parse_args()

    sparsifier = SparsifyLasBasedSqM(args.input_file, args.output_folder, args.target_density, args.verbose)
    sparsifier.process()