Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBastien Montagne <montagne29@wanadoo.fr>2014-11-10 17:30:52 +0300
committerBastien Montagne <montagne29@wanadoo.fr>2014-11-10 17:30:52 +0300
commit9d276937de26f82a4bbaded388ed474b951f338a (patch)
treed6aef9e3277d5d162f29644209f79180a6c94c82
parent95a71fb815641a6b55c8d796c0106d1a159fd125 (diff)
Fix T42476: Mocap addon: "Clean noise" didn't work properly.
Patch by sybrenstuvel (Sybren Stüvel), with own tweaking (main issue was, fcurve needs to be updated when you have changed their keyframes), thanks!
-rw-r--r--mocap/__init__.py9
-rw-r--r--mocap/mocap_tools.py89
2 files changed, 62 insertions, 36 deletions
diff --git a/mocap/__init__.py b/mocap/__init__.py
index 62e3709f..fd69f89f 100644
--- a/mocap/__init__.py
+++ b/mocap/__init__.py
@@ -579,18 +579,19 @@ class OBJECT_OT_LooperButton(bpy.types.Operator):
class OBJECT_OT_DenoiseButton(bpy.types.Operator):
#Operator to denoise impluse noise on the active object's fcurves
- """Denoise active armature's animation (good for dealing """ \
- """with 'bad' frames inherent in mocap animation)"""
+ """Removes spikes from all fcurves on the selected object"""
bl_idname = "mocap.denoise"
bl_label = "Denoise Mocap"
def execute(self, context):
- mocap_tools.denoise_median()
+ obj = context.active_object
+ mocap_tools.denoise(obj, obj.animation_data.action.fcurves)
return {'FINISHED'}
@classmethod
def poll(cls, context):
- return context.active_object.animation_data
+ obj = context.active_object
+ return obj and obj.animation_data and obj.animation_data.action
class OBJECT_OT_LimitDOFButton(bpy.types.Operator):
diff --git a/mocap/mocap_tools.py b/mocap/mocap_tools.py
index 8ecd3c08..91c92a98 100644
--- a/mocap/mocap_tools.py
+++ b/mocap/mocap_tools.py
@@ -570,39 +570,64 @@ def fcurves_simplify(context, obj, sel_opt="all", error=0.002, group_mode=True):
return
-# Implementation of non-linear median filter, with variable kernel size
-# Double pass - one marks spikes, the other smooths them
-# Expects sampled keyframes on everyframe
-# IN: None. Performs the operations on the active_object's fcurves. Expects animation_data.action to exist!
-# OUT: None. Fixes the fcurves "in-place".
-def denoise_median():
- context = bpy.context
- obj = context.active_object
- fcurves = obj.animation_data.action.fcurves
- medKernel = 1 # actually *2+1... since it this is offset
- flagKernel = 4
- highThres = (flagKernel * 2) - 1
- lowThres = 0
+def detect_min_max(v):
+ """
+ Converted from MATLAB script at http://billauer.co.il/peakdet.html
+
+ Yields indices of peaks, i.e. local minima/maxima.
+
+ % Eli Billauer, 3.4.05 (Explicitly not copyrighted).
+ % This function is released to the public domain; Any use is allowed.
+ """
+
+ min_val, max_val = float('inf'), -float('inf')
+
+ check_max = True
+
+ for i, val in enumerate(v):
+ if val > max_val:
+ max_val = val
+ if val < min_val:
+ min_val = val
+
+ if check_max:
+ if val < max_val:
+ yield i
+ min_val = val
+ check_max = False
+ else:
+ if val > min_val:
+ yield i
+ max_val = val
+ check_max = True
+
+
+def denoise(obj, fcurves):
+ """
+ Implementation of non-linear blur filter.
+ Finds spikes in the fcurve, and replaces spikes that are too big with the average of the surrounding keyframes.
+ """
for fcurve in fcurves:
- orgPts = fcurve.keyframe_points[:]
- flaggedFrames = []
- # mark frames that are spikes by sorting a large kernel
- for i in range(flagKernel, len(fcurve.keyframe_points) - flagKernel):
- center = orgPts[i]
- neighborhood = orgPts[i - flagKernel: i + flagKernel]
- neighborhood.sort(key=lambda pt: pt.co[1])
- weight = neighborhood.index(center)
- if weight >= highThres or weight <= lowThres:
- flaggedFrames.append((i, center))
- # clean marked frames with a simple median filter
- # averages all frames in the kernel equally, except center which has no weight
- for i, pt in flaggedFrames:
- newValue = 0
- sumWeights = 0
- neighborhood = [neighpt.co[1] for neighpt in orgPts[i - medKernel: i + medKernel + 1] if neighpt != pt]
- newValue = sum(neighborhood) / len(neighborhood)
- pt.co[1] = newValue
- return
+ org_pts = fcurve.keyframe_points[:]
+
+ for idx in detect_min_max(pt.co.y for pt in fcurve.keyframe_points[1:-1]):
+ # Find the neighbours
+ prev_pt = org_pts[idx - 1].co.y
+ next_pt = org_pts[idx + 1].co.y
+ this_pt = org_pts[idx]
+
+ # Check the distance from the min/max to the average of the surrounding points.
+ avg = (prev_pt + next_pt) / 2
+ is_peak = abs(this_pt.co.y - avg) > avg * 0.02
+
+ if is_peak:
+ diff = avg - fcurve.keyframe_points[idx].co.y
+ fcurve.keyframe_points[idx].co.y = avg
+ fcurve.keyframe_points[idx].handle_left.y += diff
+ fcurve.keyframe_points[idx].handle_right.y += diff
+
+ # Important to update the curve after modifying it!
+ fcurve.update()
# Recieves armature, and rotations all bones by 90 degrees along the X axis