or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

3d-models.mdcli.mdface-analysis.mdface-processing.mdindex.mdmask-rendering.mdmodel-management.mdmodel-zoo.mdsample-data.md

3d-models.mddocs/

0

# 3D Morphable Face Models

1

2

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.

3

4

## Capabilities

5

6

### MorphabelModel Class

7

8

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

9

10

```python { .api }

11

class MorphabelModel:

12

def __init__(self, model_path, model_type='BFM'):

13

"""

14

Initialize 3D morphable face model.

15

16

Parameters:

17

- model_path: str, path to morphable model file (.mat, .pkl, etc.)

18

- model_type: str, model type ('BFM', '3DMM', etc.)

19

"""

20

21

def get_shape_para(self, type='random') -> np.ndarray:

22

"""

23

Generate or retrieve shape parameters.

24

25

Parameters:

26

- type: str, parameter generation type ('random', 'zero', 'mean')

27

28

Returns:

29

np.ndarray: shape parameters vector

30

"""

31

32

def get_exp_para(self, type='random') -> np.ndarray:

33

"""

34

Generate or retrieve expression parameters.

35

36

Parameters:

37

- type: str, parameter generation type ('random', 'zero', 'mean')

38

39

Returns:

40

np.ndarray: expression parameters vector

41

"""

42

43

def generate_vertices(self, shape_para, exp_para) -> np.ndarray:

44

"""

45

Generate 3D face vertices from shape and expression parameters.

46

47

Parameters:

48

- shape_para: np.ndarray, shape parameters

49

- exp_para: np.ndarray, expression parameters

50

51

Returns:

52

np.ndarray: 3D vertices, shape (n_vertices, 3)

53

"""

54

55

def get_tex_para(self, type='random') -> np.ndarray:

56

"""

57

Generate or retrieve texture parameters.

58

59

Parameters:

60

- type: str, parameter generation type

61

62

Returns:

63

np.ndarray: texture parameters vector

64

"""

65

66

def generate_colors(self, tex_para) -> np.ndarray:

67

"""

68

Generate face colors/textures from texture parameters.

69

70

Parameters:

71

- tex_para: np.ndarray, texture parameters

72

73

Returns:

74

np.ndarray: vertex colors, shape (n_vertices, 3)

75

"""

76

77

def rotate(self, vertices, angles) -> np.ndarray:

78

"""

79

Rotate 3D face vertices.

80

81

Parameters:

82

- vertices: np.ndarray, 3D vertices to rotate

83

- angles: np.ndarray, rotation angles [pitch, yaw, roll] in radians

84

85

Returns:

86

np.ndarray: rotated vertices

87

"""

88

89

def transform(self, vertices, s, angles, t3d) -> np.ndarray:

90

"""

91

Apply full 3D transformation to vertices.

92

93

Parameters:

94

- vertices: np.ndarray, input vertices

95

- s: float, scaling factor

96

- angles: np.ndarray, rotation angles [pitch, yaw, roll]

97

- t3d: np.ndarray, 3D translation vector

98

99

Returns:

100

np.ndarray: transformed vertices

101

"""

102

103

def transform_3ddfa(self, vertices, s, angles, t3d) -> np.ndarray:

104

"""

105

Apply 3DDFA-style transformation to vertices.

106

107

Parameters: same as transform()

108

109

Returns:

110

np.ndarray: transformed vertices using 3DDFA convention

111

"""

112

113

def fit(self, x, X_ind, max_iter=4, isShow=False) -> Tuple[np.ndarray, ...]:

114

"""

115

Fit morphable model to 2D/3D landmarks.

116

117

Parameters:

118

- x: np.ndarray, target landmark coordinates

119

- X_ind: np.ndarray, indices of model vertices corresponding to landmarks

120

- max_iter: int, maximum fitting iterations

121

- isShow: bool, show fitting progress visualization

122

123

Returns:

124

tuple: (fitted_vertices, shape_params, exp_params, pose_params, ...)

125

"""

126

```

127

128

### Model Attributes

129

130

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

131

132

```python { .api }

133

# Model dimensions and parameters

134

nver: float # Number of vertices in the model

135

ntri: float # Number of triangles in the mesh

136

n_shape_para: int # Number of shape parameters (typically 199)

137

n_exp_para: int # Number of expression parameters (typically 29)

138

n_tex_para: int # Number of texture parameters (typically 199)

139

140

# Mesh topology

141

kpt_ind: np.ndarray # Indices of keypoint vertices

142

triangles: np.ndarray # Triangle mesh connectivity

143

full_triangles: np.ndarray # Complete triangle mesh for rendering

144

```

145

146

### 3D Mesh Processing Modules

147

148

Comprehensive 3D mesh processing capabilities for visualization and manipulation.

149

150

```python { .api }

151

# Mesh I/O operations

152

def read_obj(filename) -> Tuple[np.ndarray, np.ndarray]: ...

153

def write_obj(filename, vertices, triangles): ...

154

155

# Mesh visualization

156

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

157

def plot_mesh(vertices, triangles): ...

158

159

# 3D transformations

160

def apply_transform(vertices, transform_matrix) -> np.ndarray: ...

161

def compute_normal(vertices, triangles) -> np.ndarray: ...

162

163

# Lighting calculations

164

def phong_shading(vertices, normals, light_pos, light_color) -> np.ndarray: ...

165

def lambert_shading(vertices, normals, light_dir) -> np.ndarray: ...

166

```

167

168

## Usage Examples

169

170

### Basic 3D Face Generation

171

172

```python

173

from insightface.thirdparty.face3d.morphable_model import MorphabelModel

174

import numpy as np

175

176

# Load morphable model (requires BFM model file)

177

model_path = 'path/to/BFM.mat' # Basel Face Model file

178

bfm = MorphabelModel(model_path, model_type='BFM')

179

180

print(f"Model info:")

181

print(f" Vertices: {int(bfm.nver)}")

182

print(f" Triangles: {int(bfm.ntri)}")

183

print(f" Shape parameters: {bfm.n_shape_para}")

184

print(f" Expression parameters: {bfm.n_exp_para}")

185

186

# Generate random face

187

shape_params = bfm.get_shape_para('random')

188

exp_params = bfm.get_exp_para('zero') # Neutral expression

189

190

# Generate 3D face vertices

191

vertices = bfm.generate_vertices(shape_params, exp_params)

192

print(f"Generated vertices shape: {vertices.shape}")

193

194

# Generate face colors/texture

195

tex_params = bfm.get_tex_para('random')

196

colors = bfm.generate_colors(tex_params)

197

print(f"Generated colors shape: {colors.shape}")

198

```

199

200

### 3D Face Pose Manipulation

201

202

```python

203

import numpy as np

204

205

# Start with a neutral face

206

shape_params = bfm.get_shape_para('mean') # Average face shape

207

exp_params = bfm.get_exp_para('zero') # Neutral expression

208

vertices = bfm.generate_vertices(shape_params, exp_params)

209

210

# Apply different poses

211

poses = [

212

{'name': 'frontal', 'angles': [0, 0, 0]},

213

{'name': 'left_profile', 'angles': [0, np.pi/3, 0]},

214

{'name': 'right_profile', 'angles': [0, -np.pi/3, 0]},

215

{'name': 'looking_up', 'angles': [np.pi/6, 0, 0]},

216

{'name': 'looking_down', 'angles': [-np.pi/6, 0, 0]}

217

]

218

219

posed_faces = {}

220

for pose in poses:

221

# Apply rotation

222

rotated = bfm.rotate(vertices, pose['angles'])

223

224

# Apply full transformation with scaling and translation

225

s = 1.0 # No scaling

226

t3d = np.array([0, 0, 0]) # No translation

227

transformed = bfm.transform(rotated, s, pose['angles'], t3d)

228

229

posed_faces[pose['name']] = transformed

230

231

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

232

```

233

234

### 3D Model Fitting to Landmarks

235

236

```python

237

# Fit model to detected facial landmarks

238

from insightface.app import FaceAnalysis

239

import cv2

240

241

# Setup face analysis for landmark detection

242

app = FaceAnalysis()

243

app.prepare(ctx_id=0)

244

245

# Load image and detect landmarks

246

img = cv2.imread('face_image.jpg')

247

faces = app.get(img)

248

249

if faces:

250

face = faces[0]

251

252

# Use 3D landmarks if available

253

if hasattr(face, 'landmark_3d_68') and face.landmark_3d_68 is not None:

254

target_landmarks = face.landmark_3d_68

255

256

# Define corresponding model vertex indices

257

# (This would typically be predefined based on the model)

258

landmark_indices = np.array([

259

# Indices of BFM vertices that correspond to facial landmarks

260

# These need to be established based on the specific model

261

8163, 8174, 8213, 8194, 8227, # Chin area

262

2435, 2452, 2463, 2441, 2456, # Left eyebrow

263

# ... more landmark correspondences

264

])

265

266

# Fit model to landmarks

267

fitted_results = bfm.fit(

268

x=target_landmarks,

269

X_ind=landmark_indices,

270

max_iter=10,

271

isShow=False

272

)

273

274

fitted_vertices, fitted_shape, fitted_exp, fitted_pose = fitted_results[:4]

275

276

print(f"Fitting completed:")

277

print(f" Shape parameters: {fitted_shape.shape}")

278

print(f" Expression parameters: {fitted_exp.shape}")

279

print(f" Pose parameters: {fitted_pose.shape}")

280

```

281

282

### Expression Manipulation

283

284

```python

285

# Create faces with different expressions

286

base_shape = bfm.get_shape_para('mean')

287

288

# Define expression variations

289

expressions = {

290

'neutral': bfm.get_exp_para('zero'),

291

'smile': bfm.get_exp_para('random') * 0.5, # Scaled random expression

292

'surprise': np.zeros(bfm.n_exp_para),

293

'frown': np.zeros(bfm.n_exp_para)

294

}

295

296

# Manually adjust specific expression parameters if known

297

# (These indices depend on the specific morphable model)

298

expressions['smile'][12] = 2.0 # Smile-related parameter

299

expressions['surprise'][5] = 1.5 # Eyebrow raise

300

expressions['frown'][15] = -1.0 # Mouth corner down

301

302

expression_faces = {}

303

for exp_name, exp_params in expressions.items():

304

vertices = bfm.generate_vertices(base_shape, exp_params)

305

expression_faces[exp_name] = vertices

306

307

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

308

```

309

310

### 3D Face Rendering Pipeline

311

312

```python

313

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

314

315

def render_3d_face(vertices, triangles, colors, pose_angles, image_size=(256, 256)):

316

"""Complete 3D face rendering pipeline."""

317

318

# Apply pose transformation

319

transformed_vertices = bfm.rotate(vertices, pose_angles)

320

321

# Set up lighting

322

light_positions = np.array([

323

[0, 0, 1], # Front light

324

[-1, 1, 1], # Left-top light

325

[1, 1, 1] # Right-top light

326

])

327

328

light_intensities = np.array([0.6, 0.3, 0.3])

329

330

# Compute normals for lighting

331

from insightface.thirdparty.face3d.mesh.transform import compute_normal

332

normals = compute_normal(transformed_vertices, triangles)

333

334

# Apply lighting

335

lit_colors = colors.copy()

336

for light_pos, intensity in zip(light_positions, light_intensities):

337

lighting = light.lambert_shading(normals, light_pos) * intensity

338

lit_colors = lit_colors * lighting[:, np.newaxis]

339

340

# Render to image

341

rendered_image = render.render_mesh(

342

transformed_vertices,

343

triangles,

344

lit_colors,

345

image_size

346

)

347

348

return rendered_image

349

350

# Render faces with different poses and lighting

351

shape_params = bfm.get_shape_para('mean')

352

exp_params = bfm.get_exp_para('zero')

353

tex_params = bfm.get_tex_para('mean')

354

355

vertices = bfm.generate_vertices(shape_params, exp_params)

356

colors = bfm.generate_colors(tex_params)

357

358

# Render with different poses

359

for angle in [0, np.pi/6, np.pi/3]:

360

pose = [0, angle, 0] # Yaw rotation

361

rendered = render_3d_face(vertices, bfm.triangles, colors, pose)

362

363

# Save rendered image

364

cv2.imwrite(f'rendered_face_yaw_{int(np.degrees(angle))}.jpg', rendered)

365

```

366

367

### Face Morphing and Interpolation

368

369

```python

370

# Create morphing between two faces

371

def morph_faces(shape1, exp1, shape2, exp2, n_steps=10):

372

"""Create smooth morphing between two face configurations."""

373

morphed_faces = []

374

375

for i in range(n_steps):

376

alpha = i / (n_steps - 1) # Interpolation weight

377

378

# Linear interpolation of parameters

379

morphed_shape = (1 - alpha) * shape1 + alpha * shape2

380

morphed_exp = (1 - alpha) * exp1 + alpha * exp2

381

382

# Generate intermediate face

383

vertices = bfm.generate_vertices(morphed_shape, morphed_exp)

384

morphed_faces.append(vertices)

385

386

return morphed_faces

387

388

# Create two different faces

389

face1_shape = bfm.get_shape_para('random')

390

face1_exp = bfm.get_exp_para('zero')

391

392

face2_shape = bfm.get_shape_para('random')

393

face2_exp = bfm.get_exp_para('random')

394

395

# Generate morphing sequence

396

morph_sequence = morph_faces(face1_shape, face1_exp, face2_shape, face2_exp)

397

398

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

399

400

# Save morphing frames

401

for i, vertices in enumerate(morph_sequence):

402

# Render each frame (using rendering function from above)

403

colors = bfm.generate_colors(bfm.get_tex_para('mean'))

404

rendered = render_3d_face(vertices, bfm.triangles, colors, [0, 0, 0])

405

cv2.imwrite(f'morph_frame_{i:03d}.jpg', rendered)

406

```

407

408

### Advanced Model Analysis

409

410

```python

411

def analyze_morphable_model(bfm):

412

"""Analyze morphable model properties and statistics."""

413

414

print("=== Morphable Model Analysis ===")

415

416

print(f"Model Dimensions:")

417

print(f" Vertices: {int(bfm.nver)}")

418

print(f" Triangles: {int(bfm.ntri)}")

419

print(f" Keypoints: {len(bfm.kpt_ind)}")

420

421

print(f"\nParameter Dimensions:")

422

print(f" Shape: {bfm.n_shape_para}")

423

print(f" Expression: {bfm.n_exp_para}")

424

print(f" Texture: {bfm.n_tex_para}")

425

426

# Analyze parameter ranges

427

test_shapes = [bfm.get_shape_para('random') for _ in range(100)]

428

shape_stats = np.array(test_shapes)

429

430

print(f"\nShape Parameter Statistics:")

431

print(f" Mean range: [{shape_stats.mean(axis=0).min():.3f}, {shape_stats.mean(axis=0).max():.3f}]")

432

print(f" Std range: [{shape_stats.std(axis=0).min():.3f}, {shape_stats.std(axis=0).max():.3f}]")

433

434

# Analyze mesh properties

435

mean_shape = bfm.get_shape_para('mean')

436

mean_exp = bfm.get_exp_para('zero')

437

mean_vertices = bfm.generate_vertices(mean_shape, mean_exp)

438

439

print(f"\nMesh Properties:")

440

print(f" Vertex bounds: X[{mean_vertices[:, 0].min():.2f}, {mean_vertices[:, 0].max():.2f}]")

441

print(f" Y[{mean_vertices[:, 1].min():.2f}, {mean_vertices[:, 1].max():.2f}]")

442

print(f" Z[{mean_vertices[:, 2].min():.2f}, {mean_vertices[:, 2].max():.2f}]")

443

444

# Run analysis

445

analyze_morphable_model(bfm)

446

```