pyfaceau 1.0.3__cp310-cp310-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. pyfaceau/__init__.py +19 -0
  2. pyfaceau/alignment/__init__.py +0 -0
  3. pyfaceau/alignment/calc_params.py +671 -0
  4. pyfaceau/alignment/face_aligner.py +352 -0
  5. pyfaceau/alignment/numba_calcparams_accelerator.py +244 -0
  6. pyfaceau/cython_histogram_median.cp310-win_amd64.pyd +0 -0
  7. pyfaceau/cython_rotation_update.cp310-win_amd64.pyd +0 -0
  8. pyfaceau/detectors/__init__.py +0 -0
  9. pyfaceau/detectors/pfld.py +128 -0
  10. pyfaceau/detectors/retinaface.py +352 -0
  11. pyfaceau/download_weights.py +134 -0
  12. pyfaceau/features/__init__.py +0 -0
  13. pyfaceau/features/histogram_median_tracker.py +335 -0
  14. pyfaceau/features/pdm.py +269 -0
  15. pyfaceau/features/triangulation.py +64 -0
  16. pyfaceau/parallel_pipeline.py +462 -0
  17. pyfaceau/pipeline.py +1083 -0
  18. pyfaceau/prediction/__init__.py +0 -0
  19. pyfaceau/prediction/au_predictor.py +434 -0
  20. pyfaceau/prediction/batched_au_predictor.py +269 -0
  21. pyfaceau/prediction/model_parser.py +337 -0
  22. pyfaceau/prediction/running_median.py +318 -0
  23. pyfaceau/prediction/running_median_fallback.py +200 -0
  24. pyfaceau/processor.py +270 -0
  25. pyfaceau/refinement/__init__.py +12 -0
  26. pyfaceau/refinement/svr_patch_expert.py +361 -0
  27. pyfaceau/refinement/targeted_refiner.py +362 -0
  28. pyfaceau/utils/__init__.py +0 -0
  29. pyfaceau/utils/cython_extensions/cython_histogram_median.c +35391 -0
  30. pyfaceau/utils/cython_extensions/cython_histogram_median.pyx +316 -0
  31. pyfaceau/utils/cython_extensions/cython_rotation_update.c +32262 -0
  32. pyfaceau/utils/cython_extensions/cython_rotation_update.pyx +211 -0
  33. pyfaceau/utils/cython_extensions/setup.py +47 -0
  34. pyfaceau-1.0.3.data/scripts/pyfaceau_gui.py +302 -0
  35. pyfaceau-1.0.3.dist-info/METADATA +466 -0
  36. pyfaceau-1.0.3.dist-info/RECORD +40 -0
  37. pyfaceau-1.0.3.dist-info/WHEEL +5 -0
  38. pyfaceau-1.0.3.dist-info/entry_points.txt +3 -0
  39. pyfaceau-1.0.3.dist-info/licenses/LICENSE +40 -0
  40. pyfaceau-1.0.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,361 @@
1
+ """
2
+ SVR Patch Expert implementation for CLNF landmark refinement
3
+
4
+ This module loads and uses SVR patch experts from OpenFace to refine landmark positions.
5
+ The implementation is based on OpenFace 2.2's SVR_patch_expert.cpp.
6
+ """
7
+
8
+ import numpy as np
9
+ from typing import Dict, List, Optional
10
+
11
+
12
+ class SVRPatchExpert:
13
+ """
14
+ Single SVR patch expert for one landmark.
15
+
16
+ Similar to OpenFace's SVR_patch_expert class but in Python.
17
+ Each patch expert is a trained SVR model that evaluates local patches
18
+ around a landmark position to determine optimal placement.
19
+ """
20
+
21
+ def __init__(self):
22
+ self.type = 0 # 0 = raw pixels, 1 = gradient features
23
+ self.confidence = 0.0
24
+ self.scaling = 1.0 # Logistic regression slope
25
+ self.bias = 0.0 # Logistic regression bias
26
+ self.weights = None # SVR weights matrix (similar to AU SVR!)
27
+
28
+ def __repr__(self):
29
+ patch_type = "raw" if self.type == 0 else "gradient"
30
+ if self.weights is not None:
31
+ return f"SVRPatchExpert(type={patch_type}, weights_shape={self.weights.shape}, confidence={self.confidence:.3f})"
32
+ return f"SVRPatchExpert(type={patch_type}, not_loaded)"
33
+
34
+
35
+ class SVRPatchExpertLoader:
36
+ """
37
+ Loader for OpenFace SVR patch expert models.
38
+
39
+ Parses the svr_patches_*.txt format and loads patch experts for specified landmarks.
40
+ File format matches OpenFace 2.2's SVR patch expert format.
41
+ """
42
+
43
+ # OpenCV type constants (from OpenCV headers)
44
+ CV_8UC1 = 0
45
+ CV_32FC1 = 5
46
+ CV_64FC1 = 6
47
+ CV_32SC1 = 4
48
+
49
+ def __init__(self, filepath: str):
50
+ """
51
+ Initialize loader with patch expert model file.
52
+
53
+ Args:
54
+ filepath: Path to svr_patches_*.txt file
55
+ """
56
+ self.filepath = filepath
57
+ self.scale = None
58
+ self.num_views = None
59
+ self.view_centers = []
60
+ self.visibility_indices = []
61
+
62
+ def load(self, target_landmarks: Optional[List[int]] = None) -> Dict[int, SVRPatchExpert]:
63
+ """
64
+ Load patch experts from file.
65
+
66
+ Args:
67
+ target_landmarks: List of landmark indices to load (0-67).
68
+ If None, loads all 68 landmarks.
69
+ Example: [17, 18, 19, 20, 21, 22, 26, 48, 54]
70
+
71
+ Returns:
72
+ Dictionary mapping landmark index -> SVRPatchExpert
73
+ """
74
+ with open(self.filepath, 'r') as f:
75
+ # Parse header
76
+ self._parse_header(f)
77
+
78
+ # Load patch experts for each landmark
79
+ patch_experts = {}
80
+ for landmark_idx in range(68):
81
+ if target_landmarks is None or landmark_idx in target_landmarks:
82
+ expert = self._load_single_patch_expert(f, landmark_idx)
83
+ patch_experts[landmark_idx] = expert
84
+ else:
85
+ # Skip this patch expert to save memory
86
+ self._skip_patch_expert(f)
87
+
88
+ return patch_experts
89
+
90
+ def _parse_header(self, f):
91
+ """Parse file header (scale, views, visibility)"""
92
+ # Skip comment line "# scaling factor of training"
93
+ f.readline()
94
+
95
+ # Read scaling factor
96
+ self.scale = float(f.readline().strip())
97
+
98
+ # Skip comment "# number of views"
99
+ f.readline()
100
+
101
+ # Read number of views
102
+ self.num_views = int(f.readline().strip())
103
+
104
+ # Skip comment "# centers of the views"
105
+ f.readline()
106
+
107
+ # Read view centers
108
+ # Each view has 6 lines: 3 metadata lines + 3 rotation values
109
+ for _ in range(self.num_views):
110
+ f.readline() # 3
111
+ f.readline() # 1
112
+ f.readline() # 6
113
+ f.readline() # rx value
114
+ f.readline() # ry value
115
+ f.readline() # rz value
116
+
117
+ # Skip visibility comment "# visibility indices per view"
118
+ f.readline()
119
+
120
+ # Read visibility indices (one block per view)
121
+ for _ in range(self.num_views):
122
+ # Each visibility block has:
123
+ # - num_landmarks line (68)
124
+ # - format line (1)
125
+ # - format line (4)
126
+ # - 68 visibility values (all on separate lines)
127
+ num_landmarks = int(f.readline().strip())
128
+
129
+ # Skip format lines
130
+ f.readline() # "1"
131
+ f.readline() # "4"
132
+
133
+ # Skip all visibility values
134
+ for _ in range(num_landmarks):
135
+ f.readline()
136
+
137
+ # Skip comment "# Patches themselves (1 line patches of a vertex)"
138
+ f.readline()
139
+
140
+ def _load_single_patch_expert(self, f, landmark_idx: int) -> SVRPatchExpert:
141
+ """
142
+ Load a single patch expert from file.
143
+
144
+ Implements Multi_SVR_patch_expert::Read() and SVR_patch_expert::Read() from OpenFace.
145
+ All data is on a single line per view!
146
+
147
+ We only load view 0 (frontal) and skip the other 6 views.
148
+ """
149
+ expert = SVRPatchExpert()
150
+
151
+ # For each view (we'll just use the first/frontal view for now)
152
+ for view_idx in range(self.num_views):
153
+ # Read entire patch expert line
154
+ line = f.readline().strip().split()
155
+
156
+ # Only process view 0 (frontal)
157
+ if view_idx != 0:
158
+ continue
159
+
160
+ idx = 0
161
+ read_type = int(line[idx])
162
+ idx += 1
163
+
164
+ # Handle Multi_SVR_patch_expert format (read_type=3)
165
+ if read_type == 3:
166
+ # Read Multi_SVR header
167
+ width = int(line[idx])
168
+ idx += 1
169
+ height = int(line[idx])
170
+ idx += 1
171
+ num_modalities_declared = int(line[idx])
172
+ idx += 1
173
+
174
+ # Note: num_modalities_declared may say 2, but we only have data for 1 modality on this line
175
+ # We'll just read the one SVR expert that's present
176
+ svr_read_type = int(line[idx])
177
+ idx += 1
178
+
179
+ if svr_read_type != 2:
180
+ raise ValueError(f"Expected SVR read_type=2, got {svr_read_type}")
181
+
182
+ # Read SVR patch expert data
183
+ expert.type = int(line[idx])
184
+ idx += 1
185
+ expert.confidence = float(line[idx])
186
+ idx += 1
187
+ expert.scaling = float(line[idx])
188
+ idx += 1
189
+ expert.bias = float(line[idx])
190
+ idx += 1
191
+
192
+ # Read matrix dimensions and type
193
+ rows = int(line[idx])
194
+ idx += 1
195
+ cols = int(line[idx])
196
+ idx += 1
197
+ mat_type = int(line[idx])
198
+ idx += 1
199
+
200
+ # Determine dtype
201
+ if mat_type == self.CV_32FC1:
202
+ dtype = np.float32
203
+ elif mat_type == self.CV_64FC1:
204
+ dtype = np.float64
205
+ else:
206
+ raise ValueError(f"Unsupported matrix type: {mat_type}")
207
+
208
+ # Read matrix values
209
+ total_values = rows * cols
210
+ values = [dtype(line[idx + i]) for i in range(total_values)]
211
+ expert.weights = np.array(values, dtype=dtype).reshape(rows, cols)
212
+ # Transpose weights (OpenFace does this for Matlab compatibility)
213
+ expert.weights = expert.weights.T
214
+
215
+ elif read_type == 2:
216
+ # Simple SVR_patch_expert format (not wrapped in Multi_SVR)
217
+ # Read type, confidence, scaling, bias
218
+ expert.type = int(line[idx])
219
+ idx += 1
220
+ expert.confidence = float(line[idx])
221
+ idx += 1
222
+ expert.scaling = float(line[idx])
223
+ idx += 1
224
+ expert.bias = float(line[idx])
225
+ idx += 1
226
+
227
+ # Read matrix dimensions and type
228
+ rows = int(line[idx])
229
+ idx += 1
230
+ cols = int(line[idx])
231
+ idx += 1
232
+ mat_type = int(line[idx])
233
+ idx += 1
234
+
235
+ # Determine dtype
236
+ if mat_type == self.CV_32FC1:
237
+ dtype = np.float32
238
+ elif mat_type == self.CV_64FC1:
239
+ dtype = np.float64
240
+ else:
241
+ raise ValueError(f"Unsupported matrix type: {mat_type}")
242
+
243
+ # Read matrix values
244
+ total_values = rows * cols
245
+ values = [dtype(line[idx + i]) for i in range(total_values)]
246
+ expert.weights = np.array(values, dtype=dtype).reshape(rows, cols)
247
+
248
+ # Transpose weights (OpenFace does this for Matlab compatibility)
249
+ expert.weights = expert.weights.T
250
+ else:
251
+ raise ValueError(f"Unsupported read_type: {read_type}")
252
+
253
+ return expert
254
+
255
+ def _skip_patch_expert(self, f):
256
+ """Skip a patch expert without loading (to save memory)"""
257
+ for view_idx in range(self.num_views):
258
+ # Each view is a single line, just skip it
259
+ f.readline()
260
+
261
+ def _read_mat(self, f) -> np.ndarray:
262
+ """
263
+ Read matrix in OpenFace format.
264
+
265
+ Format:
266
+ rows cols type
267
+ value1 value2 value3 ...
268
+
269
+ This matches OpenFace's ReadMat() function.
270
+ """
271
+ # Read dimensions and type
272
+ line = f.readline().strip().split()
273
+ rows = int(line[0])
274
+ cols = int(line[1])
275
+ mat_type = int(line[2])
276
+
277
+ # Determine numpy dtype from OpenCV type
278
+ if mat_type == self.CV_32FC1:
279
+ dtype = np.float32
280
+ elif mat_type == self.CV_64FC1:
281
+ dtype = np.float64
282
+ elif mat_type == self.CV_32SC1:
283
+ dtype = np.int32
284
+ elif mat_type == self.CV_8UC1:
285
+ dtype = np.uint8
286
+ else:
287
+ raise ValueError(f"Unsupported matrix type: {mat_type}")
288
+
289
+ # Read all values (may span multiple lines)
290
+ total_values = rows * cols
291
+ values = []
292
+ while len(values) < total_values:
293
+ line = f.readline().strip()
294
+ if line:
295
+ values.extend([dtype(v) for v in line.split()])
296
+
297
+ # Create matrix and reshape
298
+ mat = np.array(values, dtype=dtype).reshape(rows, cols)
299
+ return mat
300
+
301
+ def _skip_mat(self, f):
302
+ """Skip matrix without reading values"""
303
+ # Read dimensions
304
+ line = f.readline().strip().split()
305
+ rows = int(line[0])
306
+ cols = int(line[1])
307
+ total_values = rows * cols
308
+
309
+ # Skip all values
310
+ values_read = 0
311
+ while values_read < total_values:
312
+ line = f.readline().strip()
313
+ if line:
314
+ values_read += len(line.split())
315
+
316
+
317
+ def test_loader():
318
+ """Quick test of the patch expert loader"""
319
+ import os
320
+
321
+ weights_dir = os.path.join(os.path.dirname(__file__), '..', '..', 'weights')
322
+ patch_expert_file = os.path.join(weights_dir, 'svr_patches_0.25_general.txt')
323
+
324
+ if not os.path.exists(patch_expert_file):
325
+ print(f"Patch expert file not found: {patch_expert_file}")
326
+ return
327
+
328
+ print("Testing SVR Patch Expert Loader...")
329
+ print(f"Loading from: {patch_expert_file}")
330
+
331
+ # Load only critical landmarks
332
+ critical_landmarks = [17, 18, 19, 20, 21, 22, 26, 48, 54]
333
+
334
+ loader = SVRPatchExpertLoader(patch_expert_file)
335
+ experts = loader.load(target_landmarks=critical_landmarks)
336
+
337
+ print(f"\nLoaded {len(experts)} patch experts")
338
+ print(f"Scale: {loader.scale}")
339
+ print(f"Num views: {loader.num_views}")
340
+
341
+ print("\nPatch expert details:")
342
+ for idx, expert in experts.items():
343
+ print(f" Landmark {idx:2d}: {expert}")
344
+
345
+ # Test a specific expert
346
+ if 48 in experts:
347
+ expert = experts[48]
348
+ print(f"\nDetailed info for landmark 48 (left lip corner):")
349
+ print(f" Type: {'raw pixels' if expert.type == 0 else 'gradient'}")
350
+ print(f" Confidence: {expert.confidence:.4f}")
351
+ print(f" Scaling: {expert.scaling:.4f}")
352
+ print(f" Bias: {expert.bias:.4f}")
353
+ print(f" Weights shape: {expert.weights.shape}")
354
+ print(f" Weights dtype: {expert.weights.dtype}")
355
+ print(f" Weights range: [{expert.weights.min():.4f}, {expert.weights.max():.4f}]")
356
+
357
+ print("\nPatch expert loader test complete!")
358
+
359
+
360
+ if __name__ == '__main__':
361
+ test_loader()