Open3D
Open3D copied to clipboard
How can I apply a transformation only once in open3D
Checklist
- [X] I have searched for similar issues.
- [X] For Python issues, I have tested with the latest development wheel.
- [X] I have checked the release documentation and the latest documentation (for
mainbranch).
My Question
I want to apply a simple rotation to my 3D model, say 10° pitch, with respect to the recent rotation only once but the model keeps rotating indefinitely. I tried using rotation matrix in a transformation but I get the same issue.
Later I want to be able to generate a pose (rotation+translation) from the video processing and apply it the 3D model.
Here is an example code:
from colorama import init
import numpy as np
import open3d as o3d
import cv2
# Paths to the 3D object and video file
obj_path = "Compressor HD.obj"
# Callback function to update the texture of the plane with video frames
def update(vis):
global obj_mesh, initial_rotation
obj_mesh.rotate(o3d.geometry.get_rotation_matrix_from_xyz(np.radians([10, 0, 0])), center=[0, 0, 0])
vis.update_geometry(obj_mesh)
vis.poll_events()
vis.update_renderer()
return False
if __name__ == "__main__":
vis = o3d.visualization.VisualizerWithKeyCallback()
vis.create_window()
# Load and add the 3D object to the scene
obj_mesh = o3d.io.read_triangle_mesh(obj_path)
obj_mesh.compute_vertex_normals()
vis.add_geometry(obj_mesh)
initial_rotation = obj_mesh.get_rotation_matrix_from_xyz([0, 0, 0])
# Create and add coordinate frame
axis_length = 200
coordinate_frame = o3d.geometry.TriangleMesh.create_coordinate_frame(size=axis_length, origin=[0, 0, 0])
vis.add_geometry(coordinate_frame)
# Register callback to update video frame texture
vis.register_animation_callback(update)
vis.run()
vis.destroy_window()
cv2.destroyAllWindows()
First of, I don't think you have to call vis.poll_events(), vis.update_renderer() and vis.update_geometry(obj_mesh) in the callback function.
Calling vis.run() already does this for you:
https://github.com/isl-org/Open3D/blob/f02e7d24ea115e716445a7fae5093bce60a37d20/cpp/open3d/visualization/visualizer/Visualizer.cpp#L274-L288
But you should return True or False to signal if UpdateGeometry() needs to be called.
I think if you just want to rotate once, you could just add an if in the callback:
target_rotation = 10
applied_rotation = 0
def update(vis):
global obj_mesh, target_rotation, applied_rotation
if applied_rotation < target_rotation:
applied_rotation += 10
obj_mesh.rotate(o3d.geometry.get_rotation_matrix_from_xyz(np.radians([10, 0, 0])), center=[0, 0, 0])
return True
return False
This is just a quick and dirty way to make it rotate just once.
You might want to extend on this by having a list of poses and a current index, and then in each update-call apply the pose at the current index and increment the index. That way you could apply a list of movements.
Also, you might want to use the current time and update only every x msecs to control the speed of animation. Interpolation between poses might also be of interest for smooth transitions.
Note that I never used the animation callback, but this is just some ideas how I would try to do it. Hope my ideas help you with this. :)
Thank you for your time.
I don't have a list of poses because I do generate poses inside update function and they are changing at every frame of the animation. What I found as a solution is to re apply the initial vertices at the beginning of the update function:
# Initialize the initial vertices
initial_obj_mesh_vertices = np.asarray(obj_mesh.vertices).copy()
# and reply them in the update
obj_mesh.vertices = o3d.utility.Vector3dVector(initial_obj_mesh_vertices)
obj_mesh.compute_vertex_normals()
But I'm still looking for a different way to do it. Ideally, I want to be able to apply the transformation with respect to a fixed reference frame. So that way no matter how many times I apply the transformation, it will be the same and it won't be an accumulated one.