π₯ FLAME Parameter Optimizer
The refinement pass β gradient descent on your face parameters
- model
- image
- initial_params
- uv_coordinates
- surface_normals
- optimized_params
- mesh_data
- status
FaceReconstructor3D gives you a good-enough first guess at the FLAME parameters. This node is the polish pass. It takes those parameters and refines them with actual gradient descent, optionally using the UV coordinates and surface normals from the other predictor nodes as extra constraints so the fit lands closer to what the photo really shows. If you're chasing a tight, believable reconstruction instead of a rough blockout, this is the node you reach for before you export.
How it works
The mechanism is refreshingly honest: the optimizer takes the initial_params you feed it, makes them trainable, and runs Adam over them for optimization_steps iterations. Each step, it passes the current parameters through the FLAME model to get a mesh, then computes a loss and backprops. The final loss is a weighted sum of three terms:
total = uv_weight * uv_loss + normal_weight * normal_loss
+ regularization_weight * regularization
The UV and normal terms only contribute if you wire in the uv_coordinates (from UVPredictor) and surface_normals (from NormalPredictor) inputs - leave them unconnected and those terms are just zero. The regularization term keeps the parameters from wandering into implausible territory, which matters because FLAME coefficients have meaningful ranges and the optimizer doesn't know that on its own.
The inputs
- model - the
PIXEL3DMM_MODELcontainer from the loader. - image - the original face photo.
- initial_params -
FLAME_PARAMS, normally straight out of FaceReconstructor3D'sflame_parametersoutput. - optimization_steps - 10β1000, default 100. The one number you'll actually adjust.
Optional: learning_rate (default 0.01), uv_weight and normal_weight (0β10, default 1 each), regularization_weight (0β1, default 0.01). The defaults are a fine starting point; raise regularization if the mesh starts looking unstable or "jittery" between runs, and raise a constraint weight if you want the fit to lean harder on that map.
The outputs
- optimized_params (FLAME_PARAMS) - feed these back into a render or export path.
- mesh_data (MESH_DATA) - the final geometry, ready for MeshExporter.
- status (STRING) - steps run, final loss, and your weight settings. The final loss number is genuinely useful for comparing runs.
The honest caveat
This is real optimization code doing real Adam updates - but it's only as meaningful as what it optimizes. With the pack's placeholder weights, you're fitting noise to noise: the "optimized" parameters will converge to a lower loss and still have nothing to do with the person in the photo. The loop itself is exactly how the serious research tools (DECA, EMOCA) fit parametric models to images, so it's a great place to learn the mechanics. Just don't judge the optimizer until the base prediction and the constraint maps are coming from real weights.
Start with 100 steps, lr 0.01, all constraint weights at 1, and only touch the knobs when you can see a specific problem in the output - too-rigid fit, wandering geometry, or constraints that aren't being respected. And remember: UV and normal constraints only exist if you actually wire those nodes in.
Installing it
Installed with the whole pack: ComfyUI Manager (search "Pixel3DMM"), or clone https://github.com/A043-studios/comfyui-pixel3dmm into ComfyUI/custom_nodes, run pip install -r requirements.txt, restart. Slow runs? Cut optimization_steps in half - the optimizer is the most expensive part of this pipeline.
Inputs (10)
| Name | Type | Default | Description |
|---|---|---|---|
| model | PIXEL3DMM_MODEL | β | |
| image | IMAGE | β | |
| initial_params | FLAME_PARAMS | β | |
| optimization_steps | INT | 10010β1000 | β |
| uv_coordinatesopt | UV_COORDS | β | |
| surface_normalsopt | NORMALS | β | |
| learning_rateopt | FLOAT | 0.0100.001β0.1 | β |
| uv_weightopt | FLOAT | 1.00β10 | β |
| normal_weightopt | FLOAT | 1.00β10 | β |
| regularization_weightopt | FLOAT | 0.0100β1 | β |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| optimized_params | FLAME_PARAMS | β |
| mesh_data | MESH_DATA | β |
| status | STRING | β |