Nodes/ComfyUI-WanAnimatePreprocessV2/Wan Quality Scorer — Temporal Jitter (V2)
ComfyUI Node

Wan Quality Scorer — Temporal Jitter (V2)

A 0-to-1 quality score for your driving clip, before you burn an hour on a render

By Code2Collapse·Created 8 months ago·Updated 8 days ago· 17
Wan Quality Scorer — Temporal Jitter (V2)
    • metrics_json
    • quality_score
    • mean_body_velocity
    • mean_face_velocity
    • mean_expression_jitter
    • bad_frame_count
    pose_data_json[]
    image_diagonal_px1500
    confidence_threshold0.30
    max_velocity_px60
    expression_window8
    bad_velocity_thr_px40
    bad_visibility_thr0.50
    expression_coeffs_json

    WanQualityScorerJitterV2 gives you a number: an aggregate quality score in [0,1] for a driving clip, built from per-frame pose jitter, keypoint visibility, and expression stability. The idea is simple and genuinely useful - instead of eyeballing whether a clip is good enough to drive Wan Animate, you get an objective score you can wire into auto-skip / auto-retry logic or just read before committing to a long render.

    It's a stateless analysis node: pure numpy over JSON inputs, no model loaded, near-zero cost. You can run it on dozens of candidate clips and pick the winner, which is exactly the workflow it's built for.

    How it works

    The node consumes a pose_data_json string (the format PoseAndFaceDetectionV2 produces - a per-frame list with body/face/hand keypoints and confidences) and optionally an expression_coeffs_json from a run with export_expression_coeffs=True. From those it computes:

    • Pose landmark velocity - average per-keypoint pixel motion between frames, normalised by image_diagonal_px (default 1500). This is the jitter signal: a clip where the person is still but the detector is bouncing should score badly.
    • Pose visibility ratio - the fraction of keypoints above confidence_threshold (0.3). Occluded or flickering detections tank this.
    • Face landmark velocity - face-only motion, kept separate from body so a talking head and a dancer don't get compared on the same scale.
    • Expression jitter - blendshape variance over a rolling expression_window (8 frames), from the optional expression coefficients.
    • Bad frames - frames flagged for excessive velocity (bad_velocity_thr_px, 40) or poor visibility (bad_visibility_thr, 0.5).

    Everything folds into the quality_score (higher = better). max_velocity_px (60) is the calibration: a body-keypoint velocity at that level scores 0.

    Outputs: quality_score, metrics_json (the full per-frame breakdown), mean_body_velocity, mean_face_velocity, mean_expression_jitter, and bad_frame_count.

    The inputs a beginner actually sets

    • pose_data_json - the input, a plain multiline JSON string of the schema the description documents (per-frame list with body/face/hand keypoints). Fair warning: the pack doesn't ship a dedicated "pose → JSON" serializer node, so in practice you paste it from a scripted run, assemble it from the detection node's JSON debug outputs (key_frame_body_points, iris_data), or feed it programmatically. The expression_coeffs_json optional input, by contrast, is a direct output from a PoseAndFaceDetectionV2 run with export_expression_coeffs=True - that one you can wire.
    • image_diagonal_px (1500) - must reflect your actual working resolution or velocities are scaled wrong. If your plates are 832×480, the diagonal is ~960, so set it accordingly.
    • confidence_threshold (0.3) - same meaning as pose_threshold upstream; keep them consistent or visibility scores misbehave.
    • expression_window (8) - the rolling window for expression-jitter variance.

    The honest take

    The score is a heuristic, not a verdict - a clip with fast but intentional motion (dance, action) will show high velocity and may score lower than it deserves, because the node can't tell intentional movement from detector jitter. That's the built-in limitation of a stateless scorer, and the mean_body_velocity output exists precisely so you can see whether a low score is real jitter or just a lot of motion. Where it shines is comparison: score ten candidate takes, feed the winner. It's also a genuinely useful tool for tuning the pack's own detection - raise use_blur_for_pose or lower pose_threshold upstream and watch the score move, and you've got an objective measure of whether your preprocessing tweak helped.

    Install: shared pack path - ComfyUI Manager search "WanAnimatePreprocessV2", or git clone https://github.com/Code2Collapse/ComfyUI-WanAnimatePreprocessV2 + pip install -r requirements.txt + restart. No models.

    CategoryWanAnimatePreprocess_V2/Quality

    Inputs (8)

    NameTypeDefaultDescription
    pose_data_jsonSTRING[]
    image_diagonal_pxFLOAT150064–16384Used to normalise pixel velocities.
    confidence_thresholdFLOAT0.300–1
    max_velocity_pxFLOAT601–1000Body kp px velocity that would yield score 0.
    expression_windowINT82–60
    bad_velocity_thr_pxFLOAT401–1000
    bad_visibility_thrFLOAT0.500–1
    expression_coeffs_jsonoptSTRING

    Outputs (6)

    NameTypeDescription
    metrics_jsonSTRING
    quality_scoreFLOAT
    mean_body_velocityFLOAT
    mean_face_velocityFLOAT
    mean_expression_jitterFLOAT
    bad_frame_countINT