Wan Quality Scorer — Temporal Jitter (V2)
A 0-to-1 quality score for your driving clip, before you burn an hour on a render
- metrics_json
- quality_score
- mean_body_velocity
- mean_face_velocity
- mean_expression_jitter
- bad_frame_count
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. Theexpression_coeffs_jsonoptional input, by contrast, is a direct output from aPoseAndFaceDetectionV2run withexport_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 aspose_thresholdupstream; 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.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| pose_data_json | STRING | [] | — |
| image_diagonal_px | FLOAT | 150064–16384 | Used to normalise pixel velocities. |
| confidence_threshold | FLOAT | 0.300–1 | — |
| max_velocity_px | FLOAT | 601–1000 | Body kp px velocity that would yield score 0. |
| expression_window | INT | 82–60 | — |
| bad_velocity_thr_px | FLOAT | 401–1000 | — |
| bad_visibility_thr | FLOAT | 0.500–1 | — |
| expression_coeffs_jsonopt | STRING | — |
Outputs (6)
| Name | Type | Description |
|---|---|---|
| metrics_json | STRING | — |
| quality_score | FLOAT | — |
| mean_body_velocity | FLOAT | — |
| mean_face_velocity | FLOAT | — |
| mean_expression_jitter | FLOAT | — |
| bad_frame_count | INT | — |