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, 2016 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, 2016 at 9:29

1 Answer 1

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, 2021 at 3:12
  • 1
    $\begingroup$ That's an idea. Modify object.animation_data directly will truly be quicker. $\endgroup$
    – vicky Lin
    Oct 9, 2021 at 7:29

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .