5
$\begingroup$

I have a path of 6 DOF camera poses. Say my path has 100 frames, so I have a 6x100 matrix.

Now in blender, I want my camera to follow exactly that path. I have seen tutorials about creating a path and modify the path shape. However, that won't give me precisely the path I want.

How can I do this?

$\endgroup$
2
  • $\begingroup$ Can you elaborate on "that won't give me precisely the path I want"? What exactly isn't as it should be? Is it the actual shape of the path or is it the timing that's lacking? $\endgroup$ Mar 18 '16 at 8:56
  • $\begingroup$ If it is about the timing (i.e. controlling when the camera is at what point of your path), then you might want to look at blender.stackexchange.com/a/21046/1259 $\endgroup$ Mar 18 '16 at 9:29
3
$\begingroup$

I have been searching for help about this for a long time. The answer would be "not using follow path", use "keyframe on camera transform" instead. Path is a set of points with no direction, while you need a 6 DOF description of camera motion.

Here's some Python scripts. Assuming you have camera pose in translation + quaternion format.

camera name in blender: Camera

camera pose data: PATHTO_pose.txt

data_format:
           frame  x            y           z            qx         qy           qz           qw
PATHTO_pose.txt:
           1      26.211       7.1004      24.8995      0.97571    0.0402501    -0.212604    -0.034192
           2      26.1915      7.12682      24.8661     0.975725    0.0402611    -0.212369   -0.0351898
           3      26.1693      7.15424      24.9107     0.975688    0.0406072    -0.212337   -0.0360215
           ...
import bpy
import numpy as np
import math
from mathutils import *

bridge_M_CV_blender = Quaternion((0.0, 1.0, 0.0, 0.0)).to_matrix().to_4x4()


def read_pose(pose_path):
    camera_pose_raw = np.loadtxt(pose_path)
    rows = camera_pose_raw.shape[0]
    cols = camera_pose_raw.shape[1]  # cols should be 8,  [frame x y z dx dy dz dw]
    if cols != 8:
        raise ValueError('cols of pose.txt should be 8,  [frame x y z dx dy dz dw]')
    start_frame = int(camera_pose_raw[0][0])
    end_frame = int(camera_pose_raw[-1][0])
    movement_list = []  # identity mat for unknown pose
    frame_id_list = []
    for x in range(0, rows):
        curr_quat = Quaternion(
            (camera_pose_raw[x][7], camera_pose_raw[x][4], camera_pose_raw[x][5], camera_pose_raw[x][6]))  # (w,x,y,z)
        curr_loca = Vector((camera_pose_raw[x][1], camera_pose_raw[x][2], camera_pose_raw[x][3]))  # (x,y,z)
        curr_loca *= 1   # modify if translation in blender is not appropriate
        curr_mat = curr_quat.to_matrix()
        curr_mat = curr_mat.to_4x4()
        curr_mat.translation = curr_loca
        movement_list.append(curr_mat)
        frame_id_list.append(int(camera_pose_raw[x][0]))
    return movement_list, frame_id_list, start_frame, end_frame


def key_frame_insert(obj_name, camera_tracefile, set_scene_frame=True):
    bpy.ops.object.select_all(action='DESELECT')
    cam_obj = bpy.data.objects[obj_name]
    cam_obj.rotation_mode = 'QUATERNION'
    start_pose = Euler((math.pi / 2, 0, math.pi / 2)).to_matrix().to_4x4()
    movement_poses, frame_id_list, start_frame_id, end_frame_id = read_pose(camera_tracefile)
    if set_scene_frame:
        bpy.data.scenes["Scene"].frame_start = start_frame_id
        bpy.data.scenes["Scene"].frame_end = end_frame_id
    global_poses = []
    for mat, f_id in zip(movement_poses, frame_id_list):
        curr_frame_global_pose = start_pose @ bridge_M_CV_blender.inverted() @ mat @ bridge_M_CV_blender
        global_poses.append(start_pose @ mat)
        cam_obj.location = curr_frame_global_pose.to_translation()
        cam_obj.rotation_quaternion = curr_frame_global_pose.to_quaternion()
        cam_obj.keyframe_insert(data_path="location", frame=f_id)
        cam_obj.keyframe_insert(data_path="rotation_quaternion", frame=f_id)
        print("keyframe insterted in frame", f_id)
    return 0


if __name__ == '__main__':
    trace_path = "PATHTO_pose.txt"
    key_frame_insert("Camera", trace_path)

bridge_M_CV_blender is used for converting camera pose between different camera coordinate system "Blender(z-backward,x-rightward,y-upward) and CV(z-forward, x-right ward,y-downward)"

function read_pose read camera pose from file and turn them into bpy matrix format.

function key_frame_insert insert movement generated by read_poseto camera keyframe.

If you want to use Euler rotation (since you were saying 6x100 matrix), you need to do some work on function read_pose.

$\endgroup$
2
  • 1
    $\begingroup$ Since you have read the data into a numpy array could bake this to fcurves very quickly. See the fcurve method in this example blender.stackexchange.com/a/239656/15543 $\endgroup$
    – batFINGER
    Oct 9 '21 at 3:12
  • 1
    $\begingroup$ That's an idea. Modify object.animation_data directly will truly be quicker. $\endgroup$
    – vicky Lin
    Oct 9 '21 at 7:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.