CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-insightface

A comprehensive 2D and 3D face analysis toolkit with state-of-the-art algorithms for face recognition, detection, and alignment.

Pending
Overview
Eval results
Files

3d-models.mddocs/

3D Morphable Face Models

Advanced 3D face modeling capabilities using morphable models for face reconstruction, pose estimation, expression analysis, and realistic 3D face rendering. Built on the Basel Face Model (BFM) and other morphable model formats.

Capabilities

MorphabelModel Class

Core 3D morphable face model for generating and manipulating 3D face geometry, texture, and expressions.

class MorphabelModel:
    def __init__(self, model_path, model_type='BFM'):
        """
        Initialize 3D morphable face model.
        
        Parameters:
        - model_path: str, path to morphable model file (.mat, .pkl, etc.)
        - model_type: str, model type ('BFM', '3DMM', etc.)
        """
    
    def get_shape_para(self, type='random') -> np.ndarray:
        """
        Generate or retrieve shape parameters.
        
        Parameters:
        - type: str, parameter generation type ('random', 'zero', 'mean')
        
        Returns:
        np.ndarray: shape parameters vector
        """
    
    def get_exp_para(self, type='random') -> np.ndarray:
        """
        Generate or retrieve expression parameters.
        
        Parameters:
        - type: str, parameter generation type ('random', 'zero', 'mean')
        
        Returns:
        np.ndarray: expression parameters vector
        """
    
    def generate_vertices(self, shape_para, exp_para) -> np.ndarray:
        """
        Generate 3D face vertices from shape and expression parameters.
        
        Parameters:
        - shape_para: np.ndarray, shape parameters
        - exp_para: np.ndarray, expression parameters
        
        Returns:
        np.ndarray: 3D vertices, shape (n_vertices, 3)
        """
    
    def get_tex_para(self, type='random') -> np.ndarray:
        """
        Generate or retrieve texture parameters.
        
        Parameters:
        - type: str, parameter generation type
        
        Returns:
        np.ndarray: texture parameters vector
        """
    
    def generate_colors(self, tex_para) -> np.ndarray:
        """
        Generate face colors/textures from texture parameters.
        
        Parameters:
        - tex_para: np.ndarray, texture parameters
        
        Returns:
        np.ndarray: vertex colors, shape (n_vertices, 3)
        """
    
    def rotate(self, vertices, angles) -> np.ndarray:
        """
        Rotate 3D face vertices.
        
        Parameters:
        - vertices: np.ndarray, 3D vertices to rotate
        - angles: np.ndarray, rotation angles [pitch, yaw, roll] in radians
        
        Returns:
        np.ndarray: rotated vertices
        """
    
    def transform(self, vertices, s, angles, t3d) -> np.ndarray:
        """
        Apply full 3D transformation to vertices.
        
        Parameters:
        - vertices: np.ndarray, input vertices
        - s: float, scaling factor
        - angles: np.ndarray, rotation angles [pitch, yaw, roll]
        - t3d: np.ndarray, 3D translation vector
        
        Returns:
        np.ndarray: transformed vertices
        """
    
    def transform_3ddfa(self, vertices, s, angles, t3d) -> np.ndarray:
        """
        Apply 3DDFA-style transformation to vertices.
        
        Parameters: same as transform()
        
        Returns:
        np.ndarray: transformed vertices using 3DDFA convention
        """
    
    def fit(self, x, X_ind, max_iter=4, isShow=False) -> Tuple[np.ndarray, ...]:
        """
        Fit morphable model to 2D/3D landmarks.
        
        Parameters:
        - x: np.ndarray, target landmark coordinates
        - X_ind: np.ndarray, indices of model vertices corresponding to landmarks
        - max_iter: int, maximum fitting iterations
        - isShow: bool, show fitting progress visualization
        
        Returns:
        tuple: (fitted_vertices, shape_params, exp_params, pose_params, ...)
        """

Model Attributes

Key attributes of the morphable model providing model metadata and mesh information.

# Model dimensions and parameters
nver: float                    # Number of vertices in the model
ntri: float                    # Number of triangles in the mesh
n_shape_para: int             # Number of shape parameters (typically 199)
n_exp_para: int               # Number of expression parameters (typically 29)
n_tex_para: int               # Number of texture parameters (typically 199)

# Mesh topology
kpt_ind: np.ndarray           # Indices of keypoint vertices
triangles: np.ndarray         # Triangle mesh connectivity
full_triangles: np.ndarray    # Complete triangle mesh for rendering

3D Mesh Processing Modules

Comprehensive 3D mesh processing capabilities for visualization and manipulation.

# Mesh I/O operations
def read_obj(filename) -> Tuple[np.ndarray, np.ndarray]: ...
def write_obj(filename, vertices, triangles): ...

# Mesh visualization
def render_mesh(vertices, triangles, colors=None) -> np.ndarray: ...
def plot_mesh(vertices, triangles): ...

# 3D transformations
def apply_transform(vertices, transform_matrix) -> np.ndarray: ...
def compute_normal(vertices, triangles) -> np.ndarray: ...

# Lighting calculations
def phong_shading(vertices, normals, light_pos, light_color) -> np.ndarray: ...
def lambert_shading(vertices, normals, light_dir) -> np.ndarray: ...

Usage Examples

Basic 3D Face Generation

from insightface.thirdparty.face3d.morphable_model import MorphabelModel
import numpy as np

# Load morphable model (requires BFM model file)
model_path = 'path/to/BFM.mat'  # Basel Face Model file
bfm = MorphabelModel(model_path, model_type='BFM')

print(f"Model info:")
print(f"  Vertices: {int(bfm.nver)}")
print(f"  Triangles: {int(bfm.ntri)}")
print(f"  Shape parameters: {bfm.n_shape_para}")
print(f"  Expression parameters: {bfm.n_exp_para}")

# Generate random face
shape_params = bfm.get_shape_para('random')
exp_params = bfm.get_exp_para('zero')  # Neutral expression

# Generate 3D face vertices
vertices = bfm.generate_vertices(shape_params, exp_params)
print(f"Generated vertices shape: {vertices.shape}")

# Generate face colors/texture
tex_params = bfm.get_tex_para('random')
colors = bfm.generate_colors(tex_params)
print(f"Generated colors shape: {colors.shape}")

3D Face Pose Manipulation

import numpy as np

# Start with a neutral face
shape_params = bfm.get_shape_para('mean')  # Average face shape
exp_params = bfm.get_exp_para('zero')      # Neutral expression
vertices = bfm.generate_vertices(shape_params, exp_params)

# Apply different poses
poses = [
    {'name': 'frontal', 'angles': [0, 0, 0]},
    {'name': 'left_profile', 'angles': [0, np.pi/3, 0]},
    {'name': 'right_profile', 'angles': [0, -np.pi/3, 0]},
    {'name': 'looking_up', 'angles': [np.pi/6, 0, 0]},
    {'name': 'looking_down', 'angles': [-np.pi/6, 0, 0]}
]

posed_faces = {}
for pose in poses:
    # Apply rotation
    rotated = bfm.rotate(vertices, pose['angles'])
    
    # Apply full transformation with scaling and translation
    s = 1.0  # No scaling
    t3d = np.array([0, 0, 0])  # No translation
    transformed = bfm.transform(rotated, s, pose['angles'], t3d)
    
    posed_faces[pose['name']] = transformed

print(f"Generated {len(posed_faces)} different poses")

3D Model Fitting to Landmarks

# Fit model to detected facial landmarks
from insightface.app import FaceAnalysis
import cv2

# Setup face analysis for landmark detection
app = FaceAnalysis()
app.prepare(ctx_id=0)

# Load image and detect landmarks
img = cv2.imread('face_image.jpg')
faces = app.get(img)

if faces:
    face = faces[0]
    
    # Use 3D landmarks if available
    if hasattr(face, 'landmark_3d_68') and face.landmark_3d_68 is not None:
        target_landmarks = face.landmark_3d_68
        
        # Define corresponding model vertex indices
        # (This would typically be predefined based on the model)
        landmark_indices = np.array([
            # Indices of BFM vertices that correspond to facial landmarks
            # These need to be established based on the specific model
            8163, 8174, 8213, 8194, 8227,  # Chin area
            2435, 2452, 2463, 2441, 2456,  # Left eyebrow
            # ... more landmark correspondences
        ])
        
        # Fit model to landmarks
        fitted_results = bfm.fit(
            x=target_landmarks,
            X_ind=landmark_indices,
            max_iter=10,
            isShow=False
        )
        
        fitted_vertices, fitted_shape, fitted_exp, fitted_pose = fitted_results[:4]
        
        print(f"Fitting completed:")
        print(f"  Shape parameters: {fitted_shape.shape}")
        print(f"  Expression parameters: {fitted_exp.shape}")
        print(f"  Pose parameters: {fitted_pose.shape}")

Expression Manipulation

# Create faces with different expressions
base_shape = bfm.get_shape_para('mean')

# Define expression variations
expressions = {
    'neutral': bfm.get_exp_para('zero'),
    'smile': bfm.get_exp_para('random') * 0.5,  # Scaled random expression
    'surprise': np.zeros(bfm.n_exp_para),
    'frown': np.zeros(bfm.n_exp_para)
}

# Manually adjust specific expression parameters if known
# (These indices depend on the specific morphable model)
expressions['smile'][12] = 2.0    # Smile-related parameter
expressions['surprise'][5] = 1.5  # Eyebrow raise
expressions['frown'][15] = -1.0   # Mouth corner down

expression_faces = {}
for exp_name, exp_params in expressions.items():
    vertices = bfm.generate_vertices(base_shape, exp_params)
    expression_faces[exp_name] = vertices

print(f"Generated {len(expression_faces)} expression variations")

3D Face Rendering Pipeline

from insightface.thirdparty.face3d.mesh import render, light

def render_3d_face(vertices, triangles, colors, pose_angles, image_size=(256, 256)):
    """Complete 3D face rendering pipeline."""
    
    # Apply pose transformation
    transformed_vertices = bfm.rotate(vertices, pose_angles)
    
    # Set up lighting
    light_positions = np.array([
        [0, 0, 1],    # Front light
        [-1, 1, 1],   # Left-top light
        [1, 1, 1]     # Right-top light
    ])
    
    light_intensities = np.array([0.6, 0.3, 0.3])
    
    # Compute normals for lighting
    from insightface.thirdparty.face3d.mesh.transform import compute_normal
    normals = compute_normal(transformed_vertices, triangles)
    
    # Apply lighting
    lit_colors = colors.copy()
    for light_pos, intensity in zip(light_positions, light_intensities):
        lighting = light.lambert_shading(normals, light_pos) * intensity
        lit_colors = lit_colors * lighting[:, np.newaxis]
    
    # Render to image
    rendered_image = render.render_mesh(
        transformed_vertices,
        triangles,
        lit_colors,
        image_size
    )
    
    return rendered_image

# Render faces with different poses and lighting
shape_params = bfm.get_shape_para('mean')
exp_params = bfm.get_exp_para('zero')
tex_params = bfm.get_tex_para('mean')

vertices = bfm.generate_vertices(shape_params, exp_params)
colors = bfm.generate_colors(tex_params)

# Render with different poses
for angle in [0, np.pi/6, np.pi/3]:
    pose = [0, angle, 0]  # Yaw rotation
    rendered = render_3d_face(vertices, bfm.triangles, colors, pose)
    
    # Save rendered image
    cv2.imwrite(f'rendered_face_yaw_{int(np.degrees(angle))}.jpg', rendered)

Face Morphing and Interpolation

# Create morphing between two faces
def morph_faces(shape1, exp1, shape2, exp2, n_steps=10):
    """Create smooth morphing between two face configurations."""
    morphed_faces = []
    
    for i in range(n_steps):
        alpha = i / (n_steps - 1)  # Interpolation weight
        
        # Linear interpolation of parameters
        morphed_shape = (1 - alpha) * shape1 + alpha * shape2
        morphed_exp = (1 - alpha) * exp1 + alpha * exp2
        
        # Generate intermediate face
        vertices = bfm.generate_vertices(morphed_shape, morphed_exp)
        morphed_faces.append(vertices)
    
    return morphed_faces

# Create two different faces
face1_shape = bfm.get_shape_para('random')
face1_exp = bfm.get_exp_para('zero')

face2_shape = bfm.get_shape_para('random')
face2_exp = bfm.get_exp_para('random')

# Generate morphing sequence
morph_sequence = morph_faces(face1_shape, face1_exp, face2_shape, face2_exp)

print(f"Generated morphing sequence with {len(morph_sequence)} frames")

# Save morphing frames
for i, vertices in enumerate(morph_sequence):
    # Render each frame (using rendering function from above)
    colors = bfm.generate_colors(bfm.get_tex_para('mean'))
    rendered = render_3d_face(vertices, bfm.triangles, colors, [0, 0, 0])
    cv2.imwrite(f'morph_frame_{i:03d}.jpg', rendered)

Advanced Model Analysis

def analyze_morphable_model(bfm):
    """Analyze morphable model properties and statistics."""
    
    print("=== Morphable Model Analysis ===")
    
    print(f"Model Dimensions:")
    print(f"  Vertices: {int(bfm.nver)}")
    print(f"  Triangles: {int(bfm.ntri)}")
    print(f"  Keypoints: {len(bfm.kpt_ind)}")
    
    print(f"\nParameter Dimensions:")
    print(f"  Shape: {bfm.n_shape_para}")
    print(f"  Expression: {bfm.n_exp_para}")
    print(f"  Texture: {bfm.n_tex_para}")
    
    # Analyze parameter ranges
    test_shapes = [bfm.get_shape_para('random') for _ in range(100)]
    shape_stats = np.array(test_shapes)
    
    print(f"\nShape Parameter Statistics:")
    print(f"  Mean range: [{shape_stats.mean(axis=0).min():.3f}, {shape_stats.mean(axis=0).max():.3f}]")
    print(f"  Std range: [{shape_stats.std(axis=0).min():.3f}, {shape_stats.std(axis=0).max():.3f}]")
    
    # Analyze mesh properties
    mean_shape = bfm.get_shape_para('mean')
    mean_exp = bfm.get_exp_para('zero')
    mean_vertices = bfm.generate_vertices(mean_shape, mean_exp)
    
    print(f"\nMesh Properties:")
    print(f"  Vertex bounds: X[{mean_vertices[:, 0].min():.2f}, {mean_vertices[:, 0].max():.2f}]")
    print(f"                 Y[{mean_vertices[:, 1].min():.2f}, {mean_vertices[:, 1].max():.2f}]")
    print(f"                 Z[{mean_vertices[:, 2].min():.2f}, {mean_vertices[:, 2].max():.2f}]")

# Run analysis
analyze_morphable_model(bfm)

Install with Tessl CLI

npx tessl i tessl/pypi-insightface

docs

3d-models.md

cli.md

face-analysis.md

face-processing.md

index.md

mask-rendering.md

model-management.md

model-zoo.md

sample-data.md

tile.json