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_pose
to 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
.