How to Perform Inference Using the PFLD Model for Facial Landmark Detection

To perform inference with the PFLD model, restore the TensorFlow 1.x checkpoint, feed a pre-processed 112×112 RGB image into the image_batch placeholder with phase_train set to False, and extract the 196-dimensional landmark vector from the landmark_L1 tensor, reshaping it into 98 (x, y) pairs scaled to the original image dimensions.

The guoqiangqi/pfld repository implements a TensorFlow 1.x version of the Progressive Face Localization and Detection (PFLD) network. This architecture predicts 98 facial landmarks (196 coordinate values) from a single RGB face image, utilizing a MobileNet-V2-style backbone for efficient feature extraction.

Understanding the PFLD Inference Pipeline

Performing inference requires three distinct operations: restoring the serialized computation graph from checkpoint files, locating the specific input and output tensors by name, and executing a forward pass with properly normalized image data. The model expects fixed-size inputs and produces normalized landmark coordinates that must be mapped back to the original image resolution.

Step-by-Step Implementation

Restore the Trained TensorFlow Graph

The pretrained model is stored as a collection of checkpoint files (model.meta, model.ckpt-*). In test_model.py (lines 28‑31), the graph is restored using tf.train.import_meta_graph followed by saver.restore:

import tensorflow as tf

# Reset default graph to avoid conflicts

tf.reset_default_graph()

# Import and restore

saver = tf.train.import_meta_graph(meta_file)
saver.restore(tf.get_default_session(), ckpt_file)

Locate Input and Output Tensors

After restoration, you must extract tensor references by their scoped names. The graph contains an image batch placeholder (image_batch:0), a phase flag (phase_train:0), and five intermediate landmark tensors (landmark_L1:0 through landmark_L5:0). As shown in test_model.py (lines 33‑41), the code typically uses landmark_L1 for final predictions:

graph = tf.get_default_graph()

# Input tensors

images_placeholder = graph.get_tensor_by_name('image_batch:0')
phase_train_placeholder = graph.get_tensor_by_name('phase_train:0')

# Output tensor (landmark_L1 contains the 196-dim vector)

landmark_L1 = graph.get_tensor_by_name('landmark_L1:0')

Pre-process the Input Image

The model expects a 112×112 RGB image with pixel values scaled to [0, 1]. Using OpenCV, convert the BGR image to RGB, resize it, and normalize by dividing by 256.0:

import cv2
import numpy as np

# Load image

image = cv2.imread('face_image.jpg')
h, w, _ = image.shape

# Pre-processing pipeline

input_img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
input_img = cv2.resize(input_img, (112, 112)).astype(np.float32) / 256.0
input_img = np.expand_dims(input_img, 0)  # Add batch dimension

Execute Inference and Decode Results

Feed the pre-processed tensor into the graph with phase_train=False to disable dropout and batch normalization updates. The raw output is a flat vector of 196 normalized values (range 0‑1). Reshape this into 98 (x, y) pairs and scale by the original image dimensions to obtain pixel coordinates:


# Run inference

feed_dict = {
    images_placeholder: input_img,
    phase_train_placeholder: False
}
landmarks_norm = sess.run(landmark_L1, feed_dict=feed_dict)

# Convert to pixel coordinates

landmarks = landmarks_norm.reshape(-1, 2) * np.array([h, w])

Complete Inference Script

Below is a consolidated, runnable script that loads a pretrained PFLD checkpoint and visualizes the detected landmarks on an input image:

import os
import cv2
import numpy as np
import tensorflow as tf

# Configuration

meta_file = './models2/model0/model.meta'
ckpt_file = './models2/model0/model.ckpt-0'
image_path = 'data/test_data/example.jpg'

def main():
    # Initialize session

    tf.reset_default_graph()
    with tf.Graph().as_default():
        with tf.Session() as sess:
            # Restore model

            saver = tf.train.import_meta_graph(meta_file)
            saver.restore(sess, ckpt_file)
            
            # Get tensors

            graph = tf.get_default_graph()
            img_ph = graph.get_tensor_by_name('image_batch:0')
            phase_ph = graph.get_tensor_by_name('phase_train:0')
            landmark_tensor = graph.get_tensor_by_name('landmark_L1:0')
            
            # Load and preprocess image

            img = cv2.imread(image_path)
            if img is None:
                raise ValueError(f"Could not load image: {image_path}")
            h, w, _ = img.shape
            
            input_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
            input_img = cv2.resize(input_img, (112, 112)).astype(np.float32) / 256.0
            input_img = np.expand_dims(input_img, 0)
            
            # Inference

            feed = {img_ph: input_img, phase_ph: False}
            landmarks = sess.run(landmark_tensor, feed_dict=feed)
            
            # Post-process

            points = landmarks.reshape(-1, 2) * np.array([h, w])
            
            # Visualize

            for x, y in points.astype(np.int32):
                cv2.circle(img, (x, y), 2, (0, 0, 255), -1)
            
            cv2.imshow('PFLD Landmarks', img)
            cv2.waitKey(0)
            cv2.destroyAllWindows()

if __name__ == '__main__':
    main()

Model Architecture and Key Source Files

Understanding the underlying implementation helps debug inference issues. The PFLD model in this repository uses a MobileNet-V2-style backbone for feature extraction, followed by multi-scale feature aggregation.

model2.py contains the core architecture definition. The pfld_inference() function constructs the network and outputs a 196-dimensional vector representing 98 (x, y) landmark coordinates:

def pfld_inference(input, weight_decay, batch_norm_params):
    # MobileNet-V2 backbone construction...

    multi_scale = tf.concat([s1, s2, s3], 1)
    landmarks = slim.fully_connected(multi_scale, num_outputs=196,
                                    activation_fn=None, scope='fc')
    return features, landmarks

test_model.py provides the reference implementation for inference, demonstrating how to restore checkpoints and feed data into the graph.

utils.py contains visualization helpers such as LandmarkImage for generating heatmaps, though these are optional for basic inference.

Summary

  • PFLD inference requires restoring a TensorFlow 1.x checkpoint and accessing specific tensor names (image_batch:0, phase_train:0, landmark_L1:0).
  • Input preprocessing involves resizing images to 112×112 pixels, converting BGR to RGB, and scaling values to [0, 1] by dividing by 256.0.
  • Output decoding requires reshaping the 196-dimensional vector into 98 (x, y) pairs and multiplying by the original image height and width to obtain pixel coordinates.
  • The underlying architecture in model2.py uses a MobileNet-V2 backbone with multi-scale feature concatenation to produce the final landmark predictions.

Frequently Asked Questions

What input image size does the PFLD model require?

The model expects a fixed input size of 112×112 pixels. During inference, you must resize your input image to these dimensions before normalizing pixel values. The repository uses OpenCV's resize function with bilinear interpolation, followed by scaling the uint8 values to the range [0, 1] by dividing by 256.0.

How do I convert normalized landmarks to pixel coordinates?

The model outputs a flat vector of 196 normalized values (range 0–1). First, reshape this array to (-1, 2) to create 98 (x, y) pairs. Then multiply the x-coordinates by the original image width and the y-coordinates by the original image height. This scaling maps the normalized predictions back to the original pixel space for visualization or further processing.

Which tensor contains the final landmark predictions?

During inference, retrieve the tensor named landmark_L1:0 from the restored graph. While the graph contains five intermediate landmark tensors (landmark_L1 through landmark_L5) representing different training stages, the first tensor (landmark_L1) contains the final 196-dimensional prediction vector suitable for deployment.

Can I use PFLD with TensorFlow 2.x?

The guoqiangqi/pfld repository is implemented for TensorFlow 1.x. To run inference in TensorFlow 2.x environments, you must enable compatibility mode using tf.compat.v1 and disable eager execution with tf.compat.v1.disable_eager_execution(). Alternatively, convert the checkpoint to TensorFlow 2.x format or use the TensorFlow 1.x runtime in a separate environment.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →