Library / SDK
1adrianb/face-alignment avatar
1adrianb/face-alignment

face-alignment: 2D and 3D Facial Landmarks in PyTorch, and the Detector Choice That Decides Your Latency

:fire: 2D and 3D Face alignment library build using pytorch

7,538 stars1,383 forksPythonBSD-3-Clause

At a glance

What is it?
A Python wrapper around the FAN face alignment network that returns 68-point landmarks in two or three dimensions, with five swappable face detectors. The interesting engineering decision is not the network, it is the detector you pass in.
Who is it for?
Adopt face-alignment if you need 68-point landmarks in 2D or 3D from a PyTorch pipeline and you are willing to tune the detector backend rather than accept the default. Do not adopt it if you need published-evaluation parity, since the README itself points numerical work at the separate Lua implementation, or if you need a detector that runs on MPS, because YuNet and SCRFD are CPU-only.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 162 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

What face-alignment actually returns, and who needs that shape

The library exposes one class, face_alignment.FaceAlignment, and one primary method, get_landmarks, which takes an image array and returns predicted landmark coordinates. The LandmarksType enum selects between TWO_D and THREE_D, so the same call site produces either flat pixel coordinates or coordinates that include a depth component. That depth component is the reason this project exists in its current form: most landmark libraries stop at 2D, and the README frames the 3D output as the distinguishing feature, calling it a network "capable of detecting points in both 2D and 3D coordinates." The intended audience is someone building a downstream pipeline that consumes landmarks rather than someone training a model. Typical consumers would be head-pose estimation, face swapping, gaze work, or alignment preprocessing before a recognition model. The README describes the library as a black box and directs readers to the original paper for the internals, which tells you the maintainer expects you to treat it as an inference component, not a research codebase to modify.

The detector is the real architecture decision

Face alignment runs in two stages: a detector proposes a bounding box, then the FAN network predicts landmarks inside a crop of that box. The README documents five detector backends plus a deprecated dlib path, selected through the face_detector keyword. SFD is the default and the README calls it "the most accurate, but slower." The alternative backends (BlazeFace, YuNet, RetinaFace, SCRFD) exist to trade accuracy for speed. The README publishes a timing table for detection only, median over 20 runs on a single face in a 450x450 image on an Apple M2. SFD is listed at 138.8 ms on CPU and 33.1 ms on MPS, while YuNet is listed at 5.6 ms on CPU and BlazeFace at 10.9 ms CPU and 8.2 ms MPS. Those numbers are detection only, not end-to-end landmark prediction, and the README states them as such. The device column matters more than the milliseconds: SFD, BlazeFace and RetinaFace run on CPU, CUDA and MPS, while YuNet is CPU only through OpenCV DNN and SCRFD is CPU only through ONNX Runtime. If your deployment target is an Apple GPU, the two fastest detectors in the table are unavailable to you. That is a structural constraint, not a tuning knob.

Getting it running: install, device, dtype, compile

The documented install path is a single pip command: pip install face-alignment. The stated requirements are Python 3.9 or newer, Linux, Windows or macOS, and PyTorch 2.0 or newer. Building from source means cloning the repository, then running pip install -r requirements.txt followed by pip install . The repository also ships a Dockerfile, and the README gives docker build -t face-alignment . as the build command, noting the image includes CUDA and cuDNN support. For a minimal call, the README's own example constructs the aligner with face_alignment.FaceAlignment(face_alignment.LandmarksType.TWO_D, flip_input=False), reads an image with skimage.io.imread, and calls fa.get_landmarks(input). Device selection is explicit: pass device='cuda' or device='mps', and dtype=torch.bfloat16 is shown alongside device='cuda'. One default deserves attention. The README states the landmark network is compiled with torch.compile by default, that compilation artifacts are cached to disk, and that the first run is slow, roughly 25 seconds. Passing compile=False skips this for instant startup. If you are writing a CLI tool or a short-lived process, that default will dominate your runtime. For multi-face images on memory-constrained GPUs, max_batch_size defaults to 1 and can be raised, with the README showing max_batch_size=8.

Two conveniences that change how you integrate it

The first is get_landmarks_from_directory, which takes a directory path and processes every image in it. This matters because the detector is the expensive stage and the library can batch internally rather than forcing you to write your own loop. The second is face_detector='folder', which skips detection entirely and loads pre-computed bounding boxes from .npy, .t7 or .pth files matching each image filename. The README describes this as useful for evaluation with ground truth boxes. Read that more broadly: if you already have boxes from an upstream detector that you trust, the folder backend removes the detection stage from your latency budget completely. It also removes the detector accuracy question, which is where most of the published timing spread lives. The cost is that you now own box quality. If your upstream detector drifts, landmark accuracy degrades and the library has no way to tell you. For detector-specific options, face_detector_kwargs is passed through; the README shows face_detector_kwargs={'back_model': True} to select BlazeFace's larger back-camera model, which it describes as better for distant faces. SCRFD requires an extra install, pip install onnxruntime, which the README states explicitly.

Where this is the wrong tool

The README contains a warning that is easy to skim past. It says that for numerical evaluations it is highly recommended to use the Lua version, which uses identical models to the ones evaluated in the paper. If your job is producing numbers that must match published results, this Python package is not the artifact you want, by the maintainer's own statement. A second limitation is device coverage. YuNet and SCRFD are CPU-only per the timing table, so a pipeline that needs both the fastest detector and MPS acceleration cannot have both. A third is the compile default: roughly 25 seconds on first run is fine for a server and hostile for a command-line tool, a Lambda-style cold start, or a test suite that constructs a new aligner per test. A fourth is that the library is inference-only in the documented surface. There is no documented training entry point, no dataset loader, and no fine-tuning path in the README. If your faces are outside the distribution the FAN network was trained on (heavy occlusion, unusual sensor characteristics, non-photographic input), you have no documented in-repo way to adapt it. The README does invite issue reports that include images where it fails, which is a maintenance signal but not a remedy.

How it compares to dlib's landmark predictor

The obvious comparison is dlib, which this project still lists as a deprecated face_detector value. The two take different approaches. dlib's landmark predictor is a cascade of regression trees operating on histogram-of-oriented-gradients features, and it is CPU-only in practice. face-alignment is a neural network with a swappable front-end detector, and it can run on CUDA or MPS. The practical difference shows up in three places. First, dlib returns 2D points only; face-alignment's THREE_D mode returns a depth component, which is the whole reason to pick it. Second, dlib's detector and predictor are tightly coupled in a way that makes swapping one stage awkward, whereas face-alignment treats the detector as a constructor argument with six documented values. Third, dlib's dependency footprint is small and its startup is immediate, while face-alignment pulls in PyTorch and, by default, pays a compilation cost on first use. If you need 2D landmarks on CPU with minimal dependencies and fast cold starts, dlib remains a reasonable choice and face-alignment is overkill. If you need depth, GPU acceleration, or a detector you can trade off against accuracy, the trade runs the other way.

Maintenance, releases and the licence

The project is active, not archived, with the most recent push and the v1.5.0 release both dated 2026-04-06. The gap before that is instructive: v1.4.0 landed in June 2023 and v1.4.1 in August 2023, so roughly two and a half years passed between v1.4.1 and v1.5.0. This is a low-frequency release cadence, which means you should expect to pin a version and carry it rather than track a moving target. The v1.5.0 release is also where the detector table and the torch.compile behaviour appear to belong, given the version boundary. The licence is BSD-3-Clause, a permissive licence that permits commercial use and modification provided the copyright notice and disclaimer are retained. That is a summary of the licence identifier, not legal advice; if you are redistributing the package or bundling its weights into a product, read the actual licence text and check the terms attached to the pretrained models separately, since model weights and code can carry different conditions and the README does not state the weight terms. The upgrade cost is mostly the torch.compile default and the detector keyword surface: code written against an older version that never passed face_detector or compile will pick up new defaults on upgrade. Pin the version in your requirements file and read the release notes before moving.

Editorial conclusion

Adopt face-alignment if you need 68-point landmarks in 2D or 3D from a PyTorch pipeline and you are willing to tune the detector backend rather than accept the default. Do not adopt it if you need published-evaluation parity, since the README itself points numerical work at the separate Lua implementation, or if you need a detector that runs on MPS, because YuNet and SCRFD are CPU-only. Before committing, verify three things on your own hardware: the first-run torch.compile delay, whether your chosen detector supports your device, and whether the folder detector with your own bounding boxes is accurate enough to skip detection entirely.

Official sources

  1. 1adrianb/face-alignment on GitHub
  2. License: BSD-3-Clause
  3. Project website
  4. README
  5. Releases
Community notes

Community notes