cv2.face.loadFacePoints
Read 68 landmark points out of a .pts file
- bool
- nparray
A file-parsing utility, and an honest one: cv2.face.loadFacePoints reads a landmark file and gives you the points as an array. That's it. No detection, no model, no inference. It exists because OpenCV's Facemark API needs ground-truth landmarks to train and test against, and those live in text files - the classic .pts format used by face datasets: a version line, a point count, then x y pairs inside braces.
Why you'd want it in ComfyUI
Because landmarks from anywhere else are useful data. Three concrete cases:
Visualising an existing landmark set. You have a .pts file, or a CSV you converted to one, and you want to see the 68 dots on the face. Load the points, draw them, done - and if you want them on top of an image, that's the sibling node cv2.face.drawFacemarks.
Ground truth for an experiment. Comparing a detector's output to a known-good landmark set, or measuring alignment error after a fit. The pack's array plumbing (CV Array To Numbers, CV Array Statistic, CV Box IoU Matrix) is where the comparison happens.
Landmarks as geometry input. Points from a file are a perfectly good input to cv2.estimateAffinePartial2D or CV Affine Shape Warp - if you have landmarks for the source face and a canonical template, that's a complete alignment problem with no detector involved. That alignment step is the load-bearing part of every face pipeline in the ecosystem; the identity tooling that dominates identity-preservation.md all begins with it.
Inputs and outputs
filename is a plain string and the wrapper hands it to OpenCV untouched - there is no resolution against ComfyUI's input folder, so pass a path cv2 can open (absolute is safest). The file must be in the format the loader expects: the .pts-style layout with a version: line, an n_points: line and the coordinate pairs. A CSV of numbers will not parse.
offset is optional and accepts a Python literal - leave it blank for the OpenCV default. Upstream only describes it as an offset applied to the loaded data, so the sensible rule is: don't set it unless you know exactly what you're correcting, and if the points come back shifted, suspect the file's coordinate frame rather than this parameter.
Out comes a boolean and the points as an NPARRAY, N x 2. The boolean is the load status - whether the file opened and parsed - so a false with an empty array means a path or format problem, not "no landmarks found". As with the rest of the face family, the points are raw numbers on an NPARRAY socket, so they wire into CV Draw Points, cv2.face.drawFacemarks, the estimator nodes, or CV Array To Numbers if you want to read them.
The gap worth knowing about
OpenCV's Facemark API has two halves: a landmark model (createFacemarkLBF, which fits a shape to a face) and these file utilities. Only the utility half is reachable here, because this pack's generator parses top-level functions and class-based APIs like createFacemarkLBF are invisible to it - the same reason cv2.CascadeClassifier only appears via a hand-written node. So there is no landmark detector in this node family. You bring the points; the pack draws them, stores them, and fits transforms to them. For actual landmark detection, the practical options are MediaPipe, or the 5-point output that comes with this pack's CV YuNet Face Detect - which is what people use when they need to align a crop in a hurry.
Installing it
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"
Or install comfyui_cv via Manager and restart. Python ≥ 3.12, recent V3-API ComfyUI. Node path: image/CV/low-level/face.
The contrib caveat applies, and it bites this family hardest: cv2.face is a contrib submodule, so if a plain non-contrib opencv-python wheel gets installed over your contrib one, all three cv2.face.* nodes vanish from the menu with no error. tools/repair_opencv_contrib.py --check diagnoses it; --apply repairs it.
Traps
The file must be a real landmark file - an HTML page from a failed download parses as garbage or fails outright. Watch the coordinate frame if you're overlaying points on an image that's been resized or letterboxed anywhere upstream: the loader has no idea what resolution your picture is, and a scale mismatch produces landmarks that look "nearly right", which is the hardest kind of bug to spot by eye. And remember these are point coordinates, not a mask or a probabilistic shape - if a downstream node wants a mask, convert deliberately, don't hope.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| filename | STRING | - - - | |
| offsetopt | STRING | - - - Optional - leave blank to use the OpenCV default. Accepts a Python literal, e.g. 3, 1.5, true, or (3, 3). |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| bool | BOOLEAN | — |
| nparray | NPARRAY | — |