Skip to the content.

Prérequis

Pour visualiser des nuages de points, il faut le module Viz de OpenCV. Un tuto est disponible ici.

Lecture des nuages

source = cv.viz.readCloud("Datasets/pointscloud_000.ply")
target = cv.viz.readCloud("Datasets/pointscloud_100.ply")

Les nuages de points sont de la formes suivantes : [[positions], [couleurs]].

Il est possible que vous ayez des difficultés à lire les nuages de points. Pour cela, consultez ceci.

Estimation des normales

Pour améliorer la qualité du recalage, on peut estimer les normales de chaque point. Pour cela, on va utiliser cv.ppf_match_3d.computeNormalsPC3d. computeNormalsPC3d prend 4 paramètres :

Nous allons estimer les normales que sur les positions des points, c’est pour ca que nous allons prendre l’élément [0][0] des nuages de points.

retval, source_normals = cv.ppf_match_3d.computeNormalsPC3d(source[0][0], 30, False, (0, 0, 0))
retval2, targets_normals = cv.ppf_match_3d.computeNormalsPC3d(target[0][0], 30, False, (0, 0, 0))

Echantillonnage

Pour augmenter la rapidité et la possibilité de convergence, on va échantilloner le nuage de points. On va utiliser cv.ppf_match_3d.samplePCByQuantization. Cette fonction prend 6 paramètres :

Exemple

import cv2 as cv
import numpy as np

pcd = cv.viz.readCloud("Datasets/pointscloud_000.ply")

min_x = 0
max_x = 0
min_y = 0
max_y = 0
min_z = 0
max_z = 0

for ligne in pcd[0][0]:
    if(ligne[0] > max_x):
        max_x = float(ligne[0])
    if(ligne[0] < min_x):
        min_x = float(ligne[0])
    if(ligne[1] > max_y):
        max_y = float(ligne[1])
    if(ligne[1] < min_y):
        min_y = float(ligne[1])
    if(ligne[2] > max_z):
        max_z = float(ligne[2])
    if(ligne[2] < min_z):
        min_z = float(ligne[2])

pcd_sample = cv.ppf_match_3d.samplePCByQuantization(pcd[0][0], (min_x, max_x), (min_y, max_y), (min_z, max_z), 0.01)

viewer = cv.viz_Viz3d("Nuages de points")


source = cv.viz_WCloud(pcd[0])
viewer.showWidget("Source", source)

cloud_pose = cv.viz_Affine3d()
cloud_pose = cloud_pose.translate((40, 0, 0))

source_sample = cv.viz_WCloud(np.array([np.array(pcd_sample)]))
viewer.showWidget("Source_sample", source_sample, cloud_pose)

viewer.spin()

Résultat :

Sampling

Recalage

Pour le recalage, nous allons utiliser cv.ppf_match_3d_ICP. cv.ppf_match_3d_ICP prend 4 paramètres :

import cv2 as cv
import numpy as np

source = cv.viz.readCloud("Datasets/pointscloud_000.ply")
target = cv.viz.readCloud("Datasets/pointscloud_100.ply")

retval, source_normals = cv.ppf_match_3d.computeNormalsPC3d(source[0][0], 30, False, (0, 0, 0))
retval2, target_normals = cv.ppf_match_3d.computeNormalsPC3d(target[0][0], 30, False, (0, 0, 0))


min_x = 0
max_x = 0
min_y = 0
max_y = 0
min_z = 0
max_z = 0

for ligne in source_normals:
    if(ligne[0] > max_x):
        max_x = float(ligne[0])
    if(ligne[0] < min_x):
        min_x = float(ligne[0])
    if(ligne[1] > max_y):
        max_y = float(ligne[1])
    if(ligne[1] < min_y):
        min_y = float(ligne[1])
    if(ligne[2] > max_z):
        max_z = float(ligne[2])
    if(ligne[2] < min_z):
        min_z = float(ligne[2])

source_sample = cv.ppf_match_3d.samplePCByQuantization(source_normals, (min_x, max_x), (min_y, max_y), (min_z, max_z), 0.005, 2)

min_x = 0
max_x = 0
min_y = 0
max_y = 0
min_z = 0
max_z = 0

for ligne in target_normals:
    if(ligne[0] > max_x):
        max_x = float(ligne[0])
    if(ligne[0] < min_x):
        min_x = float(ligne[0])
    if(ligne[1] > max_y):
        max_y = float(ligne[1])
    if(ligne[1] < min_y):
        min_y = float(ligne[1])
    if(ligne[2] > max_z):
        max_z = float(ligne[2])
    if(ligne[2] < min_z):
        min_z = float(ligne[2])

target_sample = cv.ppf_match_3d.samplePCByQuantization(target_normals, (min_x, max_x), (min_y, max_y), (min_z, max_z), 0.005, 2)

icp = cv.ppf_match_3d_ICP(1000000, 0.001, 1.5, 4)
result = icp.registerModelToScene(source_sample, target_sample)


res = cv.ppf_match_3d.transformPCPose(source[0][0], result[2])

source[0][0] = res

viewer = cv.viz_Viz3d("Nuages de points")

axe = cv.viz_WCoordinateSystem()
viewer.showWidget("axe",axe)

source = cv.viz_WCloud(source[0], source[1])
viewer.showWidget("Source", source)

target = cv.viz_WCloud(target[0], target[1])
viewer.showWidget("Target", target)

viewer.spin()

Résultat : Registration

Valeur de result :

(0, 0.005591467522456486, array([[ 0.99207945,  0.01622667, -0.12455947, -2.78593678],
     				  [ 0.01647234,  0.96625151,  0.25707332, 11.44790077],
     				  [ 0.12452722, -0.25708894,  0.95833097,  4.23495627],
     				  [ 0.        ,  0.        ,  0.        ,  1.        ]]))

Bibliographie

Surface matching d’OpenCV Docupentation d’OpenCV