wrote a Python script to strip redundant keyframes from Blender mocap imports, the curve bloat is unreal

148 views 0 replies

Every time I import mocap data into Blender, BVH or FBX from Rokoko, doesn't matter, every bone gets a keyframe on every single frame. 120fps capture, 60-second take, 50 bones. The graph editor becomes completely unusable. You can't see the actual curve shape because it's just wall-to-wall dots.

Finally wrote a script to fix it. It walks every fcurve in every action, checks consecutive triplets of keyframes, and removes the middle one if it lies close enough to the linear interpolation between its neighbors. Threshold is adjustable.

import bpy

def strip_redundant_keys(action, threshold=0.001):
    for fcurve in action.fcurves:
        keyframes = fcurve.keyframe_points
        if len(keyframes) < 3:
            continue

        to_remove = []
        points = [(kf.co[0], kf.co[1]) for kf in keyframes]

        i = 1
        while i < len(points) - 1:
            prev = points[i - 1]
            curr = points[i]
            nxt  = points[i + 1]

            # linear interp from prev to nxt evaluated at curr.x
            t = (curr[0] - prev[0]) / (nxt[0] - prev[0])
            interp_y = prev[1] + t * (nxt[1] - prev[1])

            if abs(interp_y - curr[1]) < threshold:
                to_remove.append(i)
            i += 1

        for idx in reversed(to_remove):
            keyframes.remove(keyframes[idx])

        fcurve.update()

for action in bpy.data.actions:
    strip_redundant_keys(action, threshold=0.002)

print("Done.")

0.002 works well for body mocap in my experience. Cleans things up without visibly changing the curve shape. For facial data I'd go lower, around 0.0005; the subtlety matters a lot more there and you don't want the script eating into brow raises or lip corners.

Known limitation: this assumes Linear interpolation on the keyframes. If you run it on Bezier curves it doesn't account for handle influence between keys, so it'll happily remove keyframes that are actually doing real work. My workaround is converting everything to Linear first, running the script, then switching back, which feels gross but works. The proper fix would be evaluating the actual spline at each candidate frame instead of doing a linear approximation, but that's a fair bit more math.

Curious whether MotionBuilder's Reduce Curves does something fundamentally smarter under the hood, or if it's basically the same threshold idea. And has anyone handled the Bezier case properly in a script? Feels like there should be a clean way to do it with the curve evaluation API.

Moonjump
Forum Search Shader Sandbox
Sign In Register