Code source de Grounded.Tools.PointCloudProcessor.CloudCompare

from .PointCloudProcessor import PointCloudProcessor
from Grounded.DataObject import PointCloud, Raster
from Grounded.utils import (find_files_regex, rename_file, move_file_to_directory, config_builer,
                            check_module_executable_path)

import os
import re
import Grounded.logger as logger
import logging


[docs] class CloudCompare(PointCloudProcessor): """ Implémente l'interface PointCloudProcessor et fournit des méthodes pour traiter les nuages de points en utilisant l'outil CloudCompare. """ def __init__(self, path_cloud_compare: str, working_directory: str, output_dir: str): """ Constructeur de la classe CloudCompare. """ super().__init__(working_directory, output_dir) check_module_executable_path(path_cloud_compare, "CloudCompare") self.path_cloud_compare = path_cloud_compare self.set_up_working_space() self.is_v2_12_or_higher = self.check_version(path_cloud_compare)
[docs] def check_version(self, path_cloud_compare): # on crée un appel de cloudCompare en logguant les sorties dans un fichier et en appellant l'option CSF, disponible seulement à partir de la 2.12 # comme on fait exprès d'aller chercher (ou pas) une erreur dans un appel à CloudCompare, on met temporairement le niveau de débugage au mini log = logger.get_logger() saveLogLevel = log.level log.level = logging.INFO self.subprocess([path_cloud_compare,"-SILENT", "-LOG_FILE", "logCC.txt", "-CSF"], os.path.join(self.working_directory, "DummyCCCall.log")) # Une fois l'appel CloudCompare réalisé on restaure le niveau de debug à l'initial log.level = saveLogLevel # On ouvre ensuite ce texte text_file = open("logCC.txt", "r") # a priori si pas d'erreur on est en version >=2.12 is_v2_12_or_higher = True for line in text_file: # on cherche le pattern "Unknown" if re.search("Unknown", line): # et si c'est associé au pattern "CSF" c'est qu'on est en version 2.11 ou inférieure if re.search("CSF", line): is_v2_12_or_higher = False # ici il faudrait supprimer le fichier logCC.txt os.remove("logCC.txt") return is_v2_12_or_higher
[docs] def mise_a_echelle(self, point_cloud: PointCloud, facteur: float) -> PointCloud: """ Méthode abstraite pour effectuer une mise à l'échelle d'un nuage de points. Args: point_cloud (PointCloud): Le nuage de points à mettre à l'échelle. facteur (float): Le facteur d'échelle à appliquer. Returns: PointCloud: Le nuage de points mis à l'échelle. """ nom_matrice = os.path.join(self.working_directory, "scale_factor_matrix.txt") # création de la matrice qui va permettre la transformation with open(nom_matrice, 'w') as file: file.write(f"{facteur} 0 0 0\n" f"0 {facteur} 0 0\n" f"0 0 {facteur} 0\n" f"0 0 0 1") # transformation du nuage de point self.subprocess([self.path_cloud_compare, "-SILENT", "-NO_TIMESTAMP", "-C_EXPORT_FMT", f"{point_cloud.extension.upper()}", "-O", f"{point_cloud.path}", "-APPLY_TRANS", nom_matrice], os.path.join(self.working_directory, "Rescale.log")) # déplacement du nuage de point nouvellement généré se trouvant dans le dossier du nuage de points # donné en paramètre path_point_cloud = self.move_file_to_working_directory(point_cloud.get_path_directory(), "TRANSFORMED", point_cloud.get_name_without_extension()) #TODO : ajouter un CC_rescaled au nom de base # suppression de la matrice try: os.remove(nom_matrice) except FileNotFoundError: raise Exception("Fichier introuvable") # on retourne le nouveau nuage de point return PointCloud(path_point_cloud)
[docs] def cloud_to_cloud_distance(self, point_cloud_before_excavation: PointCloud, point_cloud_after_excavation: PointCloud) -> Raster: """ Méthode pour calculer la distance entre deux nuages de points. Args: point_cloud_before_excavation (PointCloud): Le nuage de points avant l'excavation. point_cloud_after_excavation (PointCloud): Le nuage de points après l'excavation. Returns: Raster: Un objet Raster correspondant à un fichier raster représentant la distance entre les deux nuages de points. """ output_raster_option = "-OUTPUT_RASTER_Z" if self.is_v2_12_or_higher: output_raster_option += "_and_SF" self.subprocess([self.path_cloud_compare, "-SILENT", "-NO_TIMESTAMP", "-O", "-GLOBAL_SHIFT", "0", "0", "0", point_cloud_before_excavation.path, "-O", "-GLOBAL_SHIFT", "0", "0", "0", point_cloud_after_excavation.path, "-c2c_dist", "-MAX_DIST", "0.1", "-AUTO_SAVE", "OFF", "-RASTERIZE", "-GRID_STEP", "0.001", "-EMPTY_FILL", "INTERP", output_raster_option], os.path.join(self.working_directory, "Distance.log")) postfix = "_C2C_DIST_MAX_DIST_0.1_RASTER_Z" raster_path = self.move_file_to_working_directory(point_cloud_before_excavation.get_path_directory(), f"{point_cloud_before_excavation.get_name_without_extension()}{postfix}", f"{point_cloud_before_excavation.get_name_without_extension()}_CLOUD_TO_CLOUD_DISTANCE") return Raster(raster_path)
[docs] def crop_point_cloud(self, point_cloud: PointCloud, coordonnees_trace: list[tuple[float, float]]) -> PointCloud: """ Méthode permettant de découper un nuage de points à partir d'une liste de coordonnées délimitant les contours. Args: point_cloud (PointCloud): le nuage de point dans lequel effectuer la découpe coordonnees_trace (list): coordonnées détourant la zone de découpe Returns: PointCloud: nuage de points de la partie découpée """ formated_coordinates = [str(coord) for point in coordonnees_trace for coord in point] command = ([self.path_cloud_compare, "-SILENT", "-C_EXPORT_FMT", "ASC", "-O", "-GLOBAL_SHIFT", "0", "0", "0", point_cloud.path, "-CROP2D", "Z", str(len(coordonnees_trace))] + formated_coordinates + ["-DELAUNAY", "-BEST_FIT", "-SAMPLE_MESH", "DENSITY", "10000000"]) self.subprocess(command, os.path.join(self.working_directory, "Crop.log")) path_cloud = self.move_file_to_working_directory(point_cloud.get_path_directory(), f"{point_cloud.get_name_without_extension()}_CROPPED_SAMPLED_POINTS", f"{point_cloud.get_name_without_extension()}_CROPPED") return PointCloud(path_cloud)
[docs] def volume_between_clouds(self, crop_before: PointCloud, crop_after: PointCloud) -> float: """ Méthode pour calculer le volume se trouvant entre deux nuages de points Args: crop_before (PointCloud): Le nuage de points découpé avant l'excavation. crop_after (PointCloud): Le nuage de points découpé après l'excavation. Returns: float: le volume calculé """ arguments = [self.path_cloud_compare, "-SILENT", "-O", "-GLOBAL_SHIFT", "0", "0", "0", crop_after.path, "-O", "-GLOBAL_SHIFT", "0", "0", "0", crop_before.path, "-VOLUME", "-GRID_STEP", "0.001"] self.subprocess(arguments, os.path.join(self.working_directory, "Volume.log")) # Lecture des résultats dans le fichier généré automatiquement report_path = find_files_regex(crop_before.get_path_directory(), "VolumeCalculationReport")[0] with open(report_path, 'r') as file: content = file.read() volume = float(content.split("\n")[0].split()[1]) # Suppression du fichier contenant les résultats os.remove(report_path) return volume
[docs] def move_file_to_working_directory(self, source_directory: str, regex: str, new_name: str): path_point_cloud = find_files_regex(source_directory, regex)[0] path_point_cloud = move_file_to_directory(path_point_cloud, self.working_directory) path_point_cloud = rename_file(path_point_cloud, new_name) return path_point_cloud
[docs] def get_config(self) -> str: return config_builer(self, "CloudCompare")