# How to make a camera exactly follow a path?

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?

• 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? Mar 18 '16 at 8:56
• 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 Mar 18 '16 at 9:29

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()

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.

• 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 Oct 9 '21 at 3:12
• That's an idea. Modify object.animation_data directly will truly be quicker. Oct 9 '21 at 7:29