# PFLD vs Other Facial Landmark Detection Methods: Key Architectural and Performance Differences

> Discover PFLD's unique architecture and performance advantages over HRNet and FAN. Achieve real-time facial landmark detection on CPUs with this lightweight, accurate method.

- Repository: [Guoqiang QI/pfld](https://github.com/guoqiangqi/pfld)
- Tags: deep-dive
- Published: 2026-03-03

---

**PFLD (Progressive Facial Landmark Detector) distinguishes itself from heavyweight competitors like HRNet and FAN through its MobileNet-V2 backbone, integrated pose-aware auxiliary head, and multi-scale feature fusion, delivering real-time performance on CPUs with only ~2M parameters while maintaining competitive accuracy on the WFLW dataset.**

The implementation in the `guoqiangqi/pfld` repository provides a practical TensorFlow 1.x realization of the architecture originally described in *"PFLD: A Practical Facial Landmark Detector"* (arXiv 1902.10859). Unlike monolithic high-accuracy networks, this codebase prioritizes mobile deployment through depth-wise separable convolutions and a novel auxiliary pose estimation branch.

## Architectural Innovations: How PFLD Differs from HRNet, FAN, and DAN

### Lightweight MobileNet-V2 Backbone vs. Heavyweight Alternatives

According to the `guoqiangqi/pfld` source code, the feature extraction backbone is defined in [`model2.py`](https://github.com/guoqiangqi/pfld/blob/main/model2.py) within the `mobilenet_v2` function. This implementation uses **depth-wise separable convolutions** to drastically reduce parameter count compared to standard convolutions.

This contrasts sharply with competing approaches:
- **HRNet** maintains high-resolution parallel branches throughout the network, resulting in >20M parameters.
- **FAN (Face Alignment Network)** uses stacked Hourglass modules with heavy skip connections, exceeding 10M parameters.
- **DAN (Deep Alignment Network)** employs dilated convolutions and VGG-style backbones, typically requiring >14M parameters.

The PFLD backbone achieves a **~2M parameter count** (approximately 0.8 MB model size), enabling deployment on resource-constrained devices.

### Multi-Scale Feature Fusion Strategy

In [`model2.py`](https://github.com/guoqiangqi/pfld/blob/main/model2.py), the `pfld_inference` function implements a distinctive **multi-scale feature concatenation** mechanism. After processing through the auxiliary branch features (`auxiliary_input`), the network aggregates three parallel feature maps:
1. Two average-pooled feature maps at different scales
2. One 7×7 convolutional feature map

These are concatenated before the final fully-connected layer that outputs 196 values (98 facial landmarks × 2 coordinates).

Most competing methods either maintain a single-scale representation or rely on deep stacks of hourglass modules to aggregate multi-scale context, significantly increasing computational cost and inference latency.

### Integrated Pose-Aware Auxiliary Head

The `create_model` function in [`model2.py`](https://github.com/guoqiangqi/pfld/blob/main/model2.py) defines an **auxiliary regression head** that predicts 3 Euler angles (yaw, pitch, roll) jointly with the landmark coordinates. This architectural choice improves robustness to extreme pose variations by explicitly modeling head orientation during training.

Many alternative detectors ignore pose estimation entirely or treat it as a separate post-processing step, requiring additional network stages or external models. PFLD's integrated approach ensures that pose awareness directly influences feature learning through shared backbone representations.

## Performance and Resource Efficiency Comparison

The following table summarizes the practical differences between PFLD and typical competing methods:

| Aspect | PFLD (guoqiangqi/pfld) | Typical Competing Methods |
|--------|------------------------|---------------------------|
| **Backbone** | Custom MobileNet-V2-style with depth-wise separable convolutions (`mobilenet_v2` in [`model2.py`](https://github.com/guoqiangqi/pfld/blob/main/model2.py)) | HRNet uses high-resolution parallel branches; FAN uses stacked Hourglass networks; DAN uses dilated convolutions |
| **Parameter Count** | ~2M parameters (≈0.8 MB) | HRNet-W32: >20M; FAN-24: >10M; DAN-VGG: >14M |
| **Multi-Scale Fusion** | Concatenates three pooled feature maps (two average-pooled, one 7×7 conv) in `pfld_inference` | Single-scale representation or deep hourglass stacks |
| **Pose Estimation** | Auxiliary head predicts 3 Euler angles (yaw, pitch, roll) jointly with landmarks (`create_model`) | Often ignored or treated as separate post-processing |
| **Loss Function** | Landmark L2 loss weighted by pose-aware term (`_sum_k`) and attribute weighting ([`train_model.py`](https://github.com/guoqiangqi/pfld/blob/main/train_model.py) lines 96-100) | Standard L2 or smooth-L1 without pose weighting |
| **Training Data** | WFLW dataset (98 landmarks) via [`generate_data.py`](https://github.com/guoqiangqi/pfld/blob/main/generate_data.py) | HRNet/FAN often use 300-W, 300-WLP, or COFW |
| **Inference Speed** | Real-time on CPU (~100 fps on mid-range laptop) | HRNet and FAN require GPU for real-time; CPU usually <10 fps |
| **Implementation** | Single Python file ([`model2.py`](https://github.com/guoqiangqi/pfld/blob/main/model2.py)) defines network; training script ~200 LOC | Often split across many modules with custom C++/CUDA ops |

## Implementation Details in the guoqiangqi/pfld Repository

### Model Definition in model2.py

The network architecture is consolidated in [`model2.py`](https://github.com/guoqiangqi/pfld/blob/main/model2.py), where the `mobilenet_v2` function implements the depth-wise separable backbone, and `pfld_inference` constructs the multi-scale head:

```python

# Conceptual structure based on model2.py

def mobilenet_v2(inputs, training):
    # Depth-wise separable convolutions

    # ...

    return feature_map

def pfld_inference(inputs, landmark, phase_train, args):
    # Auxiliary features

    auxiliary_input = ...
    # Multi-scale pooling and concatenation

    # ...

    return landmarks_pred, loss, pose_pred

```

### Loss Function and Training Configuration

The training script [`train_model.py`](https://github.com/guoqiangqi/pfld/blob/main/train_model.py) (lines 96-100) implements a **pose-aware weighted loss** that adjusts landmark penalties based on head pose difficulty:

```python

# From train_model.py - conceptual representation of lines 96-100

# _sum_k represents pose-aware weighting

loss = tf.reduce_mean(tf.reduce_sum(
    tf.square(landmark_pred - landmark_gt) * _sum_k, 
    axis=1
))

```

This contrasts with standard L2 losses used in HRNet or FAN implementations.

### Data Pipeline for WFLW

The [`generate_data.py`](https://github.com/guoqiangqi/pfld/blob/main/generate_data.py) file handles the WFLW dataset (98 landmarks), providing data augmentation tailored for the lightweight network:

```python

# Conceptual usage from generate_data.py

# Loads WFLW with 98 landmark points

# Applies augmentation suitable for mobile deployment

```

## Summary

- **PFLD** achieves **~100 fps CPU inference** through a **MobileNet-V2 backbone** with depth-wise separable convolutions, compared to GPU-dependent heavyweights like HRNet or FAN.
- The architecture uniquely combines **multi-scale feature concatenation** (three pooled branches in `pfld_inference`) with an **auxiliary pose head** predicting Euler angles, improving robustness without external modules.
- At **~2M parameters** (0.8 MB), PFLD trades marginal accuracy (≈4% NME on WFLW) for dramatic resource efficiency, using pose-aware weighted loss (`_sum_k` in [`train_model.py`](https://github.com/guoqiangqi/pfld/blob/main/train_model.py)) and the WFLW 98-landmark format.

## Frequently Asked Questions

### How does PFLD achieve real-time performance on CPUs while HRNet requires a GPU?

PFLD utilizes a **MobileNet-V2-style backbone** built with depth-wise separable convolutions (defined in [`model2.py`](https://github.com/guoqiangqi/pfld/blob/main/model2.py)), reducing parameters to ~2M compared to HRNet's >20M. This lightweight design, combined with a simple fully-connected head rather than stacked hourglass modules, enables **~100 fps inference** on mid-range laptop CPUs, whereas HRNet's high-resolution parallel branches and dense connections necessitate GPU acceleration for real-time speeds.

### What is the purpose of the auxiliary pose head in PFLD?

The auxiliary head, implemented in the `create_model` function of [`model2.py`](https://github.com/guoqiangqi/pfld/blob/main/model2.py), predicts **3 Euler angles (yaw, pitch, roll)** jointly with the 98 facial landmarks. This design explicitly models head pose during training, allowing the network to learn **pose-aware features** that improve landmark localization accuracy under extreme angles. Unlike competing methods that treat pose estimation as a separate post-processing step or ignore it entirely, PFLD's integrated approach ensures pose robustness without additional inference cost.

### Why does PFLD use the WFLW dataset instead of the more common 300-W dataset?

The [`generate_data.py`](https://github.com/guoqiangqi/pfld/blob/main/generate_data.py) script in the repository specifically targets the **WFLW (Wider Facial Landmarks in-the-wild)** dataset, which provides **98 landmark points** and greater diversity in pose, expression, and occlusion compared to 300-W's 68 landmarks. This choice aligns with PFLD's design philosophy of **practical robustness**; training on WFLW's challenging "in-the-wild" distribution ensures the lightweight network generalizes better to real-world mobile deployment scenarios than models trained on more constrained laboratory datasets like 300-W.

### Can PFLD be converted to TensorFlow Lite for mobile deployment?

Yes, the architecture is explicitly designed for mobile conversion. With only **~2M parameters** stored in standard TensorFlow 1.x variables (defined in [`model2.py`](https://github.com/guoqiangqi/pfld/blob/main/model2.py)) and operations limited to depth-wise separable convolutions, average pooling, and fully-connected layers, the model contains **no custom C++ or CUDA operations** that would block TFLite conversion. The single-file implementation structure simplifies graph freezing and quantization, making PFLD suitable for deployment on ARM-based mobile devices and embedded systems.