Mesh deformation

If we want to deform a triangle mesh according to a small number of constraints, we can use mesh deformation algorithms. Open3D implements the as-rigid-as-possible method by [SorkineAndAlexa2007] that optimizes the following energy function

ijN(i)wij||(pipj)Ri(pipj)||2,

where Ri are the rotation matrices that we want to optimize for, and pi and pi are the vertex positions before and after the optimization, respectively. N(i) is the set of neighbors of vertex i. The weights wij are cotangent weights.

Open3D implements this method in deform_as_rigid_as_possible. The first argument to this method is a set of constraint_ids that refer to the vertices in the triangle mesh. The second argument constrint_pos defines at which position those vertices should be after the optimization. The optimization process is an iterative scheme. Hence, we also can define the number of iterations via max_iter.

[2]:
mesh = o3dtut.get_armadillo_mesh()

vertices = np.asarray(mesh.vertices)
static_ids = [idx for idx in np.where(vertices[:, 1] < -30)[0]]
static_pos = []
for id in static_ids:
    static_pos.append(vertices[id])
handle_ids = [2490]
handle_pos = [vertices[2490] + np.array((-40, -40, -40))]
constraint_ids = o3d.utility.IntVector(static_ids + handle_ids)
constraint_pos = o3d.utility.Vector3dVector(static_pos + handle_pos)

with o3d.utility.VerbosityContextManager(
        o3d.utility.VerbosityLevel.Debug) as cm:
    mesh_prime = mesh.deform_as_rigid_as_possible(constraint_ids,
                                                  constraint_pos,
                                                  max_iter=50)
[Open3D DEBUG] [DeformAsRigidAsPossible] setting up S'
[Open3D DEBUG] [DeformAsRigidAsPossible] done setting up S'
[Open3D DEBUG] [DeformAsRigidAsPossible] setting up system matrix L
[Open3D DEBUG] [DeformAsRigidAsPossible] done setting up system matrix L
[Open3D DEBUG] [DeformAsRigidAsPossible] setting up sparse solver
[Open3D DEBUG] [DeformAsRigidAsPossible] done setting up sparse solver
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=0, energy=4.199704e+03
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=1, energy=1.902613e+03
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=2, energy=1.315890e+03
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=3, energy=1.004800e+03
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=4, energy=8.093276e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=5, energy=6.758837e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=6, energy=5.799615e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=7, energy=5.084362e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=8, energy=4.535641e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=9, energy=4.104631e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=10, energy=3.759097e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=11, energy=3.477058e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=12, energy=3.243155e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=13, energy=3.046429e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=14, energy=2.878899e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=15, energy=2.734642e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=16, energy=2.609177e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=17, energy=2.499063e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=18, energy=2.401625e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=19, energy=2.314760e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=20, energy=2.236803e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=21, energy=2.166421e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=22, energy=2.102539e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=23, energy=2.044279e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=24, energy=1.990918e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=25, energy=1.941850e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=26, energy=1.896564e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=27, energy=1.854625e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=28, energy=1.815662e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=29, energy=1.779354e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=30, energy=1.745426e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=31, energy=1.713637e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=32, energy=1.683781e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=33, energy=1.655677e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=34, energy=1.629167e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=35, energy=1.604115e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=36, energy=1.580397e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=37, energy=1.557908e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=38, energy=1.536553e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=39, energy=1.516247e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=40, energy=1.496916e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=41, energy=1.478493e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=42, energy=1.460920e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=43, energy=1.444141e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=44, energy=1.428109e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=45, energy=1.412779e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=46, energy=1.398114e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=47, energy=1.384075e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=48, energy=1.370629e+02
[Open3D DEBUG] [DeformAsRigidAsPossible] iter=49, energy=1.357746e+02
[3]:
print('Original Mesh')
R = mesh.get_rotation_matrix_from_xyz((0, np.pi, 0))
o3d.visualization.draw_geometries([mesh.rotate(R, center=mesh.get_center())])
print('Deformed Mesh')
mesh_prime.compute_vertex_normals()
o3d.visualization.draw_geometries(
    [mesh_prime.rotate(R, center=mesh_prime.get_center())])
Original Mesh
../../_images/tutorial_Advanced_mesh_deformation_3_1.png
Deformed Mesh
../../_images/tutorial_Advanced_mesh_deformation_3_3.png

Smoothed ARAP

Open3D also implements a smoothed version of the ARAP objective defined as

ijN(i)wij||(pipj)Ri(pipj)||2+αA||RiRj||2,

that penalizes a deviation of neighboring rotation matrices. α is a trade-off parameter for the regularization term and A is the surface area.

The smoothed objective can be used in deform_as_rigid_as_possible by using the argument energy with the parameter Smoothed.