MeshGraphormer Hand Refiner
The node that fixes mangled AI hands
- image
- IMAGE
- INPAINTING_MASK
Six fingers. Fused knuckles. A thumb growing out of a palm. Every diffusion model does it, and this node is the purpose-built fix. MeshGraphormer (the ComfyUI wrapping of HandRefiner) finds the hands in an image, fits a correct 3D hand mesh to each one, and outputs a depth map of that fixed hand plus an inpaint mask around it. You then feed both into a hand-depth inpaint ControlNet, and the model regenerates just the hands - anatomically plausible this time - while leaving the rest of the image alone. It's a targeted repair tool, not a general preprocessor.
How it works
It's two models cooperating. A detector locates hands and their bounding boxes; MeshGraphormer then reconstructs a full 3D hand mesh from each detected hand - including the parts the mangled original got wrong - and renders that mesh as a depth map. The mask marks the region to repaint. So instead of asking the model to "draw a better hand" from a text prompt (which never works), you're handing it a geometrically correct depth template and saying "match this, here." That's why it succeeds where inpainting-and-praying fails.
The inputs that matter
Most of the seven knobs you'll leave alone, but a few earn their keep:
mask_type(defaultbased_on_depth) - how the inpaint mask is shaped.based_on_depthhugs the actual hand geometry (usually the best, least collateral repainting);tight_bboxesuses rectangular boxes;originalis the raw detection. Start with the default.mask_bbox_padding(default 30) andmask_expand(default 5) - how much breathing room the mask gets around the hand. If the repaired hand blends badly at the wrist, grow these a little so the model has room to reconcile the seam.detect_thr/presence_thr(both 0.6) - detection confidence gates. Lower them if it's missing a hand it should be finding (a partially-occluded or oddly-lit one); raise them if it's hallucinating hands that aren't there.rand_seed(default 88) affects the mesh fitting, andresolution(512) the working size.
The two outputs - and you need both
IMAGE- the depth map of the corrected hand mesh.INPAINTING_MASK(a MASK) - the region to regenerate.
Wire the depth IMAGE into a ControlNet Apply node using the HandRefiner depth ControlNet (control_sd15_inpaint_depth_hand), and use the MASK to drive the inpaint. Miss either half and it won't work: the depth map without the mask repaints the whole frame; the mask without the depth gives the model no template to follow. This is an SD1.5-era tool built around that specific ControlNet, so it lives in an SD1.5 hand-fix workflow.
Installing it
ComfyUI Manager: search ComfyUI's ControlNet Auxiliary Preprocessors, install, restart. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/Fannovel16/comfyui_controlnet_aux
pip install -r requirements.txt
Restart ComfyUI. On first run it downloads two weights from HuggingFace - the Graphormer hand state dict and an HRNet backbone - so the first execution pauses to fetch them.
Where people get burned
The number one failure is skipping the ControlNet - people run the node, see a nice hand depth map, and wonder why nothing improves. The depth map is inert without control_sd15_inpaint_depth_hand loaded and the mask driving an inpaint pass. Number two: it can only fix a hand it can detect. If the original hand is so destroyed the detector can't find it, or it's motion-blurred or heavily occluded, MeshGraphormer has nothing to fit a mesh to - lower detect_thr, and if that fails, you may need to fix the pose upstream first. Number three, keep expectations calibrated: it fixes hand structure, not fine skin detail or perfect five-finger counts on every subject in a crowd. For a hero shot with one or two hands, it's excellent. For a festival scene with forty background hands, don't expect miracles.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| mask_bbox_paddingopt | INT | 300–100 | — |
| resolutionopt | INT | 51264–16384 | — |
| mask_typeopt | COMBO | based_on_depth | 3 options: based_on_depth, tight_bboxes, original |
| mask_expandopt | INT | 5-16384–16384 | — |
| rand_seedopt | INT | 880–18446744073709550000 | — |
| detect_thropt | FLOAT | 0.600.1–1 | — |
| presence_thropt | FLOAT | 0.600.1–1 | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | — |
| INPAINTING_MASK | MASK | — |