H3 Face Track + Crop
The node that fixes H3's tiny, smeared distant faces
- images
- identity_reference
- crops
- transform
- preview
- report
- canvas_w
- canvas_h
H3 Face Track + Crop is the engine of the ComfyUI-H3-FaceRefine pack - the node that takes a video clip, finds the face on every frame, and hands MiniMax H3 a batch of crops where the face fills the canvas instead of being a few dozen pixels of smudge. Everything else in the pack hangs off what this node produces.
Why it exists: H3 renders faces poorly when the head occupies a small fraction of the frame. That's a property of head-size-in-frame, not of output resolution, so it persists at 720p and above. The fix is the same loop FaceDetailer made standard for stills - detect, crop, re-render at proper resolution, paste back - adapted to video. This node is the detect-and-crop half, and it's where all the temporal thinking happens.
How it works
Per frame, a YOLO face detector (face_yolov8m.pt by default, the same Bingsu/adetailer weights ADetailer and Impact Pack use) finds the face. Frames where detection fails get their gaps filled by interpolation, and the crop trajectory is then smoothed with a gaussian window - because a crop box that shivers frame-to-frame makes the refined face boil, even when the original subject sat still. Each crop is sized so the face holds a constant fraction, then everything is resized to one canvas size, since H3 generates a single fixed WxH for a whole sequence.
Two optional inputs make it smarter. Give it an identity_reference image and it'll pick the subject by face identity (via InsightFace buffalo_l embeddings) instead of by size - the difference between tracking your person through a crowd and latching onto whoever's briefly biggest. And a fallback_detector (a person/body model) supplies a real head position on frames where the face detector finds nothing, rather than blindly interpolating.
The inputs that actually matter
images- your clip from the video loader.detector-face_yolov8m.ptis the good default.confidence(0.35) - lower catches more profiles, at the cost of false positives.crop_factor(2.5) - crop side as a multiple of face height; 2.0–3.0 is the useful range. Bigger gives the seam more context to land in, but less magnification.canvas_mode-auto_capped_768is the sane default: sizes the canvas from the largest crop, clamped to 768 (H3's native short edge, and a sensible VRAM ceiling).manualuses the two values below;auto_no_downscalenever shrinks a crop but can get expensive on clips with close-ups.smooth_window(21) /size_smooth_window(51) - raise the first if the box shivers, and note the second is deliberately bigger: size jitter makes the crop breathe, which reads as shimmer.
Outputs - and one you must not skip
crops→ H3 Inject Video Latent'simages(and the SAM mask node).transform→ the spine of the whole pack: H3 Face Stitch Back, H3 Per-Frame Denoise and H3 Face Mask (SAM) all need it.preview- a debug view of the tracked boxes.report- detection stats; watch formagnification < 1.0x, which means a crop is being downscaled and real detail discarded. Raise the canvas, or skip clips that are close-ups throughout.canvas_w/canvas_h→ must be wired to the H3 node'swidth/height. In the auto modes the tracker chooses the size; if the H3 node disagrees, the latent shapes differ and injection refuses.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/Carasibana/ComfyUI-H3-FaceRefine.git
Restart, and the nodes appear under MiniMax H3/Face Refine (ComfyUI Manager finds the pack by title too). ultralytics, scipy and insightface auto-install. The one model you must supply yourself is the face detector, face_yolov8m.pt, dropped in models/ultralytics/bbox/. Everything else - the H3 diffusion model, text encoder, VAEs - you already have if you're generating H3 video.
One onnxruntime gotcha worth knowing before identity tracking: insightface needs it, and the pack deliberately pins no variant. Install onnxruntime-gpu alongside an existing CPU onnxruntime and the CPU package shadows it, CUDAExecutionProvider disappears, and identity matching silently runs on CPU. Check with python -c "import onnxruntime; print(onnxruntime.get_available_providers())".
For multiple people in one shot: run the pipeline once per subject, each with that person's identity_reference and their own refs on the H3 node, then chain them - feed run 1's stitched output in as run 2's base_images. The composites accumulate.
Inputs (17)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | — | |
| detector | COMBO | 1 options: face_yolov8m.pt | |
| confidence | FLOAT | 0.350.05–0.95 | — |
| crop_factor | FLOAT | 2.51.2–8 | Crop side as a multiple of detected face HEIGHT. 2.5 puts the face at ~40% of the crop, comfortably inside H3's good regime. Bigger = more context so the seam lands in hair/background, but less magnification. 2.0-3.0 is the useful range. |
| canvas_width | INT | 512128–1344 | Resolution H3 generates at. 512 is cheap; 768 is H3's native short edge and gives the best faces. Ignored when canvas_mode is not 'manual'. Cost scales with area: 768 is 2.25x the latent tokens of 512. |
| canvas_height | INT | 512128–1344 | — |
| canvas_mode | COMBO | manual | manual: use canvas_width/height as given. auto_no_downscale: size the canvas from the LARGEST crop in the clip so no frame is ever downscaled (magnification never drops below 1.0x). Can get expensive on clips that include close-ups. auto_capped_768: same, but clamped to 768 - H3's native short edge and a sane VRAM ceiling. |
| smooth_window | INT | 211–201 | Frames of smoothing on the crop CENTRE. 21 at 24fps is ~0.9s. Raise if the box still shivers; lower if it lags behind fast head movement. |
| size_smooth_window | INT | 511–201 | Frames of smoothing on the crop SIZE. Wants MORE than the centre: size jitter makes the crop breathe, which changes the resample factor every frame and reads as shimmer. Real zoom moves are slow, so heavy smoothing here costs nothing. |
| smooth_method | COMBO | gaussian | gaussian: best jitter rejection. savgol: preserves the shape of a push-in better at large windows. moving_average: the old boxcar, leaves residual jitter. |
| size_mode | COMBO | per_frame | per_frame: constant face-fraction in every crop (correct for push-ins). max_of_clip: one size for the whole clip, only useful when the shot is genuinely static. |
| identity_referenceopt | IMAGE | A clear face image of the person to track. When supplied, the subject is chosen by FACE IDENTITY rather than by size, so a crowd scene locks onto the right person even when someone else is briefly larger or nearer. Without it, 'largest' has no notion of WHO it is following - it just takes the biggest box each frame, which switches subject whenever the framing changes. FOR MULTIPLE PEOPLE: run the pipeline once per subject, each with that person's reference here and their own refs on the H3 node, and chain them - feed run 1's stitched output in as run 2's base_images. The composites accumulate. | |
| identity_trackopt | BOOLEAN | true | Hold one subject through a crowd. Continuity (nearest box to the previous position) decides most frames; the face-identity embedding is consulted only when two candidates are similarly plausible or their boxes overlap - which is both the accurate and the cheap arrangement, since the embedding model then runs on a handful of frames instead of all of them. The anchor is taken FROM THE CLIP by default (frames where one face clearly dominates), because an external stylised reference sits in a different domain - measured similarity between an illustration and a render of the same character was only 0.305, where same-domain faces score 0.5-0.7. |
| identity_thresholdopt | FLOAT | 0.280–1 | Minimum cosine similarity to accept a face as the reference person. Below this the frame falls back to continuity (nearest to the previous position at a similar size), which is what carries tracking through profiles and partial occlusion where embeddings become unreliable. |
| selectopt | COMBO | largest | Used only when no identity_reference is connected, and as the first-frame tie-break. |
| fallback_detectoropt | COMBO | none | Used only on frames where the FACE detector finds nothing (subject turned away). A person/body model such as segm\person_yolov8m-seg.pt gives a real head position from the top of the body box, which beats interpolating blindly between the last and next face. Set 'none' to interpolate instead. |
| fallback_head_fracopt | FLOAT | 0.500–1.5 | Head centre as a multiple of face height below the top of the person box. 0.5 puts it half a face-height down, which is about right for a head seen from behind. |
Outputs (6)
| Name | Type | Description |
|---|---|---|
| crops | IMAGE | — |
| transform | H3FACEXFORM | — |
| preview | IMAGE | — |
| report | STRING | — |
| canvas_w | INT | — |
| canvas_h | INT | — |