9
$\begingroup$

I need to find the number of animated frames after importing a bvh file in blender. scene.frame_start and scene.frame_end only returns the start and end frames in the timeline.

My bvh file has 880 animated frames. How can I set my scene length automatically?

Could you please suggest a solution?

$\endgroup$
2
  • $\begingroup$ you want to detect the first and last animation frames of a blender scene (not bvh file ) ? $\endgroup$
    – Chebhou
    Commented Mar 30, 2015 at 19:59
  • $\begingroup$ yes. (and more interested on the last frame) $\endgroup$
    – Manu Tom
    Commented Mar 31, 2015 at 20:54

4 Answers 4

15
$\begingroup$

If the duration of the scene is known, simply use bpy.context.scene.frame_end and assign a custom duration value to it - this will overwrite the current playback/rendering range:

import bpy

# get the current scene
scn = bpy.context.scene 

# assign new duration value
scn.frame_end = 880

In order to adjust the scene duration dynamically (based on the included animations) you'll need to iterate through all the relevant objects in the scene and read their keyframes by using either Animation Data or Actions.

Keyframes via Animation Data

Use animation_data to get the keyframes of an object. Following example reads the keyframes of the selected objects and prints the first and the last keyframe to the console.

import bpy
import math

# get keyframes of object list
def get_keyframes(obj_list):
    keyframes = []
    for obj in obj_list:
        anim = obj.animation_data
        if anim is not None and anim.action is not None:
            for fcu in anim.action.fcurves:
                for keyframe in fcu.keyframe_points:
                    x, y = keyframe.co
                    if x not in keyframes:
                        keyframes.append((math.ceil(x)))
    return keyframes

# get all selected objects
selection = bpy.context.selected_objects

# check if selection is not empty
if selection:

    # get all frames with assigned keyframes
    keys = get_keyframes(selection)

    # print all keyframes
    print (keys)   

    # print first and last keyframe
    print ("{} {}".format("first keyframe:", keys[0]))
    print ("{} {}".format("last keyframe:", keys[-1]))

else:
    print ('nothing selected')

Keyframes via Actions

Use bpy.data.actions to get a list of actions in the scene, but actions are not associated with objects, scenes or nodes. Following example reads the frame range of all F-Curves within the actions of the file and prints the first and the last keyframe to the console.

import bpy

# check if actions is empty
if bpy.data.actions:

    # get all actions
    action_list = [action.frame_range for action in bpy.data.actions]

    # sort, remove doubles and create a set
    keys = (sorted(set([item for sublist in action_list for item in sublist])))

    # print all keyframes
    print (keys)

    # print first and last keyframe
    print ("{} {}".format("first keyframe:", keys[0]))
    print ("{} {}".format("last keyframe:", keys[-1]))

else:
    print ("no actions")

Test scene

enter image description here

Console Output of both methods:

[1, 5, 7, 18, 21, 30, 47, 49, 67, 69, 75, 84, 100]
first keyframe: 1
last keyframe: 100

Speed comparison

  • Animation Data method takes 0.000446319580078125 sec.
  • Action method takes 0.00011038780212402344 sec.

Adjusting the timeline

As mentioned above, in order to set the beginning and end of the scene, you can assign the calculated values to bpy.context.scene.frame_start and bpy.context.scene.frame_end:

# get the current scene
scn = bpy.context.scene 

# assign new starting frame
scn.frame_start = keys[0]

# assign new end frame
scn.frame_end = keys[-1]

enter image description here

Final Note

It really depends on what you want to achieve. Using animation_data is slower, but using action.frame_range can be dangerous, because if you have to delete an object during the process, the actions of the objects remain until the blend file is saved again. See: https://blender.stackexchange.com/a/27235/3710

$\endgroup$
1
  • $\begingroup$ def get_keyframes(obj_list) will return [1, 20, 12, 12] when frame 12,5 is scale keyframe and subframe. return sorted(set(keyframes)) solves wrong order and subframe duplication problems. In addition removing doubles from list with set seems to be 100x faster than not creating them in first place with if x not in keyframes. $\endgroup$
    – Cmazay
    Commented Mar 16, 2021 at 7:53
3
$\begingroup$
a = bpy.context.object.animation_data.action
frame_start, frame_end = map(int, a.frame_range)

Test on my own data: enter image description here

$\endgroup$
2
  • 2
    $\begingroup$ This will just return the first and last keyframe of one object (the object in context). The question is: "How to find number of animated frames in a scene via python?" $\endgroup$
    – brockmann
    Commented May 25, 2021 at 7:59
  • $\begingroup$ For many cases, for example CMU bvh database, running above straight after a bvh import, when the rig is active object, will suffice. Remembering to take into account current frame when imported, may shift animation depending on import setting. (Some mocap has a ridiculous framerate, good idea to use the settings to add approx one kf per frame) Also of opinion that starting actions at 0 or 1 is the go, and lay them down with NLA for delayed start etc. $\endgroup$
    – batFINGER
    Commented May 25, 2021 at 13:16
1
$\begingroup$

This will output the first, last frames and duration for the whole scene :

import bpy

first_frame = 9999
last_frame = -9999
for action in bpy.data.actions :
        if  action.frame_range[1] > last_frame :
            last_frame = action.frame_range[1]
        if action.frame_range[0] < first_frame :
            first_frame = action.frame_range[0]

print ("first frame : ",first_frame, "\nlast frame : ", last_frame,"\nduration : ",last_frame-first_frame)
$\endgroup$
0
$\begingroup$

if you know what you are looking for (ie: only one item on stage, and you know the actions are keyframes) bpy.context.selected_objects[0].animation_data.action.frame_range[1]

$\endgroup$

You must log in to answer this question.

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