ComfyUI Node

EAR Blink Detector

Count blinks on your driving video so you know whether your animated character blinks at all

By Code2Collapse·Created 8 months ago·Updated 8 days ago· 17
EAR Blink Detector
  • pose_data
  • blink_report_json
  • ear_series_json
  • blink_count
  • blink_rate_hz
threshold0.210
min_consecutive_frames2
fps30.0
smooth_window3

EARBlinkDetectorC2C counts blinks. That's the whole job, and it's more useful than it sounds if you've ever watched a Wan Animate output where the character stares with doll eyes for five seconds - because the driving video never blinked, or the blink landed on a segment boundary and got discarded. Feed it the pose_data from PoseAndFaceDetectionV2 and it tells you how many blinks are actually in your source, per frame.

This node is one of the pack's KANIBUS "eye-feature series" - analytics nodes that measure the eye signals rather than generate. It doesn't run a model, doesn't touch the sampler, and it's cheap. Think of it as the QA step before you spend twenty minutes rendering a character that turns out to have forgotten how to blink.

How it works

The eye aspect ratio (EAR) is the classic blink metric from the face-landmark literature: for each eye's six contour points, it's the ratio of vertical to horizontal eye opening - roughly 0.28–0.35 for an open eye, 0.05–0.15 for a full blink. The node computes EAR for both eyes from the ViTPose/MediaPipe face landmarks in pose_data, averages them, smooths with a small causal median window, then flags a blink when the value drops below threshold for at least min_consecutive_frames consecutive frames. Blinks are counted by rising edges in that mask, so a single long blink (say, a slow double-blink) can be counted as one or two depending on how it dips.

Inputs worth touching

  • threshold (0.21) - the EAR value below which a frame counts as "closed". Natural open eyes sit around 0.30, so 0.21 catches real blinks without flagging heavy-lidded frames. Lower it if you're getting false positives.
  • min_consecutive_frames (2) - how long the eye must stay below threshold to count. This is your noise filter: at 30fps a single frame dip is usually eyelid flutter, not a blink.
  • fps (30) - used for the blink rate in Hz. Set it to your actual video fps or the rate number is meaningless.
  • smooth_window (3) - the causal median smoothing width, in frames.

Outputs: blink_count (INT) and blink_rate_hz (FLOAT) are the ones you'd wire anywhere; blink_report_json and ear_series_json are the full per-frame detail - ear series for both eyes plus a per-frame blink mask - for when you want to check whether a specific expression frame was a blink or just a shadow.

The honest take

A human blinks roughly 10–20 times a minute, and dead-eye output is one of the cheapest tells that video is synthetic. Running this on your driving footage tells you what you're actually asking Wan Animate to reproduce, and the pack's own notes warn that a brief microexpression landing on a 77-frame segment boundary risks getting smoothed away in the splice. So the workflow-level fix is: detect blinks on the source, and if the count is suspiciously low, that's your problem before it's the model's. It's a diagnostic node, not a magic fix - but knowing the problem exists is half the battle.

Install is the shared pack path: ComfyUI Manager or git clone https://github.com/Code2Collapse/ComfyUI-WanAnimatePreprocessV2, pip install -r requirements.txt, restart. It has no model files of its own - it reads pose_data that already exists.

CategoryWanAnimatePreprocess_V2/KANIBUS

Inputs (5)

NameTypeDefaultDescription
pose_dataPOSEDATA
thresholdFLOAT0.2100.05–0.5
min_consecutive_framesINT21–30
fpsFLOAT30.01–240
smooth_windowINT31–9

Outputs (4)

NameTypeDescription
blink_report_jsonSTRING
ear_series_jsonSTRING
blink_countINT
blink_rate_hzFLOAT