1 minute read

Esta semana me he dedicado a mejorar la Segmentación Geométrica por Parches Planos, para ello he utilizado el método de Open3d detect_planar_patches. He ido ajustando los parámetros del modelo de segmentación en función de las características de las muestras en exteriores.

He decidido separar la segmentación geomeétrica en dos ramas, la segmentación del suelo y segmentación de otros objetos que presenten geometrías planas. La segmentación del suelo aún no la he mejorado para tener en cuenta las inclinaciones posibles que puede tener.

Resultado de la SPP

Visor 3D
Visor 3D
Visor 3D

Configuración actual del Modelo

Esta es la configuración actual del algoritmo de segmentación

plane_patches = point_cloud.detect_planar_patches(
    normal_variance_threshold_deg=30.0, # Limite de la variacion permitida entre las normales de los puntos de una region (grados) Cuanto más pequeño, más estricto
    coplanarity_deg=20, # Establece la distribucion de la distancia entre los puntos que forman el plano. Cuanto mas pequeño, mas estricto. (grados)
    outlier_ratio=1.0, # Porcentaje maximo de puntos rechazados en la región para rechazar el plano estimado (100%) Restringe demasiado en muestras de este tipo
    min_plane_edge_length=0.1, # Distancia minima de la arista mas larga del plano (filtrar planos pequeños)
    min_num_points=0, # Cantidad minima de puntos para que se acepte el plano en la region
    search_param=o3d.geometry.KDTreeSearchParamKNN(knn=30) # Define el parametro de vecinos mas cercanos para la realizacion de los otros algoritmos (Región)
)

Se escogen solo los planos con una inclinación mayor a +20 o menor a -20 DEG para reservar esa segmentación del sulo a otro algoritmo con una configuración adecuada a sus características

Primero se calcula el ángulo del plano obtenido respecto del plano XY y después se compara con la restricción

def calcular_angulo(v1, v2):
    v1 = v1 / np.linalg.norm(v1)
    v2 = v2 / np.linalg.norm(v2)
    return np.arccos(np.clip(np.dot(v1, v2), -1.0, 1.0)) * (180.0 / np.pi)

. . .

vertical_tolerance = 20
meshes = []
for patch in plane_patches:
    # La normal del plano es la tercera columna de la matriz de rotación R del OrientedBoundingBox
    normal = patch.R[:, 2]
    angulo_con_vertical = calcular_angulo(normal, np.array([0, 0, 1]))

    # Incluir solo parches oblicuos o verticales, excluir horizontales con una tolerancia de ±20°
    if angulo_con_vertical > vertical_tolerance and angulo_con_vertical < (180 - vertical_tolerance):
        mesh = o3d.geometry.TriangleMesh.create_from_oriented_bounding_box(
            patch, scale=[1, 1, 0.0001]  # Escala para aplanar los parches
        )
        mesh.paint_uniform_color([1, 0, 0])  # Color rojo para los parches
        meshes.append(mesh)