pyfaceau 1.0.3__cp313-cp313-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.cp313-win_amd64.pyd +0 -0
  7. pyfaceau/cython_rotation_update.cp313-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,316 @@
1
+ # cython: language_level=3
2
+ # cython: boundscheck=False
3
+ # cython: wraparound=False
4
+ # cython: cdivision=True
5
+ """
6
+ Cython-optimized Histogram-Based Running Median Tracker
7
+
8
+ High-performance implementation of OpenFace 2.2's running median algorithm.
9
+
10
+ Performance improvements:
11
+ - Histogram update: ~10-20x faster (4464 features × 200 bins per frame)
12
+ - Median computation: ~15-30x faster (nested loops with early termination)
13
+ - Memory layout: C-contiguous for cache efficiency
14
+
15
+ Expected speedup: 10-20x for running median operations (major bottleneck!)
16
+ """
17
+
18
+ import numpy as np
19
+ cimport numpy as cnp
20
+ from libc.math cimport floor
21
+ cimport cython
22
+
23
+ # Initialize NumPy C API
24
+ cnp.import_array()
25
+
26
+ # Type definitions
27
+ ctypedef cnp.float32_t FLOAT32
28
+ ctypedef cnp.float64_t FLOAT64
29
+ ctypedef cnp.int32_t INT32
30
+
31
+
32
+ @cython.boundscheck(False)
33
+ @cython.wraparound(False)
34
+ @cython.cdivision(True)
35
+ cdef void update_histogram_c(float[:] features,
36
+ int[:, :] histogram,
37
+ int feature_dim,
38
+ int num_bins,
39
+ float min_val,
40
+ float bin_width,
41
+ float length) nogil:
42
+ """
43
+ Update histogram with new feature values (C implementation)
44
+
45
+ This is the critical tight loop - called every 2nd frame on 4702 features.
46
+
47
+ Args:
48
+ features: Input feature vector (feature_dim,)
49
+ histogram: Histogram array (feature_dim, num_bins) - modified in-place
50
+ feature_dim: Number of features
51
+ num_bins: Number of histogram bins
52
+ min_val: Minimum value for histogram range
53
+ bin_width: Width of each bin
54
+ length: Total range (max_val - min_val)
55
+ """
56
+ cdef int i, bin_idx
57
+ cdef float converted
58
+
59
+ for i in range(feature_dim):
60
+ # Convert feature value to bin index
61
+ # Formula: (value - min_val) * num_bins / length
62
+ converted = (features[i] - min_val) * num_bins / length
63
+
64
+ # Clamp to [0, num_bins-1]
65
+ if converted < 0.0:
66
+ bin_idx = 0
67
+ elif converted >= <float>(num_bins - 1):
68
+ bin_idx = num_bins - 1
69
+ else:
70
+ bin_idx = <int>converted # Truncation (matches C++ cast)
71
+
72
+ # Increment histogram bin
73
+ histogram[i, bin_idx] += 1
74
+
75
+
76
+ @cython.boundscheck(False)
77
+ @cython.wraparound(False)
78
+ @cython.cdivision(True)
79
+ cdef void compute_median_c(int[:, :] histogram,
80
+ double[:] median,
81
+ int feature_dim,
82
+ int num_bins,
83
+ int hist_count,
84
+ float min_val,
85
+ float bin_width) nogil:
86
+ """
87
+ Compute median from histogram using cumulative sum (C implementation)
88
+
89
+ This is performance-critical: nested loops with early termination.
90
+
91
+ Args:
92
+ histogram: Histogram array (feature_dim, num_bins)
93
+ median: Output median vector (feature_dim,) - modified in-place
94
+ feature_dim: Number of features
95
+ num_bins: Number of histogram bins
96
+ hist_count: Total number of histogram updates
97
+ min_val: Minimum value for histogram range
98
+ bin_width: Width of each bin
99
+ """
100
+ cdef int i, j, cutoff_point, cumulative_sum
101
+
102
+ cutoff_point = (hist_count + 1) / 2 # Integer division
103
+
104
+ for i in range(feature_dim):
105
+ cumulative_sum = 0
106
+
107
+ for j in range(num_bins):
108
+ cumulative_sum += histogram[i, j]
109
+
110
+ if cumulative_sum >= cutoff_point:
111
+ # Convert bin index back to value
112
+ # Formula: min_val + bin_idx * bin_width + 0.5 * bin_width
113
+ median[i] = min_val + <double>j * bin_width + 0.5 * bin_width
114
+ break # Early termination (critical for performance!)
115
+
116
+
117
+ cdef class HistogramMedianTrackerCython:
118
+ """
119
+ Cython-optimized histogram-based running median tracker
120
+
121
+ Direct replacement for Python HistogramBasedMedianTracker with
122
+ 10-20x performance improvement.
123
+ """
124
+
125
+ # C-level attributes (no Python overhead)
126
+ cdef int feature_dim
127
+ cdef int num_bins
128
+ cdef int hist_count
129
+ cdef float min_val
130
+ cdef float max_val
131
+ cdef float length
132
+ cdef float bin_width
133
+
134
+ # NumPy arrays (typed memoryviews for C-level access)
135
+ cdef cnp.ndarray histogram_array
136
+ cdef cnp.ndarray median_array
137
+
138
+ def __init__(self, int feature_dim, int num_bins=200,
139
+ float min_val=-3.0, float max_val=5.0):
140
+ """
141
+ Initialize histogram-based median tracker
142
+
143
+ Args:
144
+ feature_dim: Dimensionality of feature vectors
145
+ num_bins: Number of histogram bins (default: 200)
146
+ min_val: Minimum value for histogram range
147
+ max_val: Maximum value for histogram range
148
+ """
149
+ self.feature_dim = feature_dim
150
+ self.num_bins = num_bins
151
+ self.min_val = min_val
152
+ self.max_val = max_val
153
+ self.hist_count = 0
154
+
155
+ # Precompute constants
156
+ self.length = max_val - min_val
157
+ self.bin_width = self.length / num_bins
158
+
159
+ # Allocate arrays (C-contiguous for cache efficiency)
160
+ self.histogram_array = np.zeros((feature_dim, num_bins), dtype=np.int32, order='C')
161
+ self.median_array = np.zeros(feature_dim, dtype=np.float64, order='C')
162
+
163
+ def update(self, cnp.ndarray[FLOAT32, ndim=1] features, bint update_histogram=True):
164
+ """
165
+ Update tracker with new feature vector
166
+
167
+ Args:
168
+ features: Feature vector (1D float32 array)
169
+ update_histogram: Whether to update histogram (every 2nd frame in OF2.2)
170
+ """
171
+ # Declare all cdef variables at the beginning
172
+ cdef int[:, :] hist_view
173
+ cdef float[:] feat_view
174
+ cdef double[:] median_view
175
+
176
+ if features.shape[0] != self.feature_dim:
177
+ raise ValueError(f"Expected {self.feature_dim} features, got {features.shape[0]}")
178
+
179
+ if update_histogram:
180
+ # Update histogram using C function
181
+ hist_view = self.histogram_array
182
+ feat_view = features
183
+
184
+ with nogil:
185
+ update_histogram_c(feat_view, hist_view, self.feature_dim,
186
+ self.num_bins, self.min_val, self.bin_width,
187
+ self.length)
188
+
189
+ self.hist_count += 1
190
+
191
+ # Compute median
192
+ if self.hist_count == 0:
193
+ # Frame 0: use features directly
194
+ self.median_array[:] = features
195
+ elif self.hist_count == 1:
196
+ # Frame 1: still use features directly (matches C++)
197
+ self.median_array[:] = features
198
+ else:
199
+ # Frame 2+: compute from histogram
200
+ hist_view = self.histogram_array
201
+ median_view = self.median_array
202
+
203
+ with nogil:
204
+ compute_median_c(hist_view, median_view, self.feature_dim,
205
+ self.num_bins, self.hist_count,
206
+ self.min_val, self.bin_width)
207
+
208
+ def get_median(self):
209
+ """Get current running median"""
210
+ return self.median_array.copy()
211
+
212
+ def reset(self):
213
+ """Reset tracker"""
214
+ self.histogram_array.fill(0)
215
+ self.median_array.fill(0.0)
216
+ self.hist_count = 0
217
+
218
+ @property
219
+ def count(self):
220
+ """Get histogram update count"""
221
+ return self.hist_count
222
+
223
+
224
+ cdef class DualHistogramMedianTrackerCython:
225
+ """
226
+ Cython-optimized dual histogram median tracker
227
+
228
+ Manages separate trackers for HOG and geometric features.
229
+ Drop-in replacement for Python DualHistogramMedianTracker.
230
+ """
231
+
232
+ cdef HistogramMedianTrackerCython hog_tracker
233
+ cdef HistogramMedianTrackerCython geom_tracker
234
+ cdef int hog_dim
235
+ cdef int geom_dim
236
+
237
+ def __init__(self,
238
+ int hog_dim=4464,
239
+ int geom_dim=238,
240
+ int hog_bins=200,
241
+ float hog_min=-3.0,
242
+ float hog_max=5.0,
243
+ int geom_bins=200,
244
+ float geom_min=-3.0,
245
+ float geom_max=5.0):
246
+ """
247
+ Initialize dual histogram median tracker
248
+
249
+ Args:
250
+ hog_dim: HOG feature dimensionality
251
+ geom_dim: Geometric feature dimensionality
252
+ hog_bins, hog_min, hog_max: HOG histogram parameters
253
+ geom_bins, geom_min, geom_max: Geometric histogram parameters
254
+ """
255
+ self.hog_dim = hog_dim
256
+ self.geom_dim = geom_dim
257
+
258
+ self.hog_tracker = HistogramMedianTrackerCython(
259
+ hog_dim, hog_bins, hog_min, hog_max
260
+ )
261
+ self.geom_tracker = HistogramMedianTrackerCython(
262
+ geom_dim, geom_bins, geom_min, geom_max
263
+ )
264
+
265
+ def update(self,
266
+ cnp.ndarray[FLOAT32, ndim=1] hog_features,
267
+ cnp.ndarray[FLOAT32, ndim=1] geom_features,
268
+ bint update_histogram=True):
269
+ """
270
+ Update both trackers
271
+
272
+ Args:
273
+ hog_features: HOG feature vector (hog_dim,)
274
+ geom_features: Geometric feature vector (geom_dim,)
275
+ update_histogram: Whether to update histograms
276
+ """
277
+ self.hog_tracker.update(hog_features, update_histogram)
278
+
279
+ # CRITICAL: OpenFace clamps HOG median to >= 0 after update
280
+ # (FaceAnalyser.cpp line 405: this->hog_desc_median.setTo(0, this->hog_desc_median < 0);)
281
+ cdef double[:] hog_median_view = self.hog_tracker.median_array
282
+ cdef int i
283
+ for i in range(self.hog_dim):
284
+ if hog_median_view[i] < 0.0:
285
+ hog_median_view[i] = 0.0
286
+
287
+ self.geom_tracker.update(geom_features, update_histogram)
288
+
289
+ def get_combined_median(self):
290
+ """
291
+ Get concatenated [HOG_median, geom_median]
292
+
293
+ Returns:
294
+ Combined median vector (hog_dim + geom_dim,)
295
+ """
296
+ hog_median = self.hog_tracker.get_median()
297
+ geom_median = self.geom_tracker.get_median()
298
+ return np.concatenate([hog_median, geom_median])
299
+
300
+ def get_hog_median(self):
301
+ """Get HOG running median"""
302
+ return self.hog_tracker.get_median()
303
+
304
+ def get_geom_median(self):
305
+ """Get geometric running median"""
306
+ return self.geom_tracker.get_median()
307
+
308
+ def reset(self):
309
+ """Reset both trackers"""
310
+ self.hog_tracker.reset()
311
+ self.geom_tracker.reset()
312
+
313
+ @property
314
+ def count(self):
315
+ """Get histogram update count"""
316
+ return self.hog_tracker.count