face-track 0.1.3
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.
- package/README.md +76 -0
- package/dist/face-track.js +271 -0
- package/dist/face-track.js.map +1 -0
- package/models/face_landmark_68_model-shard1 +0 -0
- package/models/face_landmark_68_model-weights_manifest.json +1 -0
- package/models/tiny_face_detector_model-shard1 +0 -0
- package/models/tiny_face_detector_model-weights_manifest.json +1 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# face-track
|
|
2
|
+
|
|
3
|
+
Real-time face detection and 68-point landmark tracking using TensorFlow.js. Ships with pre-trained model weights — no external downloads needed.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install face-track @tensorflow/tfjs
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`@tensorflow/tfjs` is a peer dependency.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import * as tf from '@tensorflow/tfjs'
|
|
17
|
+
import { FaceTracker, calculateFaceOrientation, drawResults } from 'face-track'
|
|
18
|
+
|
|
19
|
+
const tracker = new FaceTracker()
|
|
20
|
+
await tracker.loadModels('/models/')
|
|
21
|
+
|
|
22
|
+
// Detect faces + landmarks from a video element
|
|
23
|
+
const results = await tracker.detectFacesWithLandmarks(videoElement)
|
|
24
|
+
|
|
25
|
+
for (const { detection, landmarks } of results) {
|
|
26
|
+
console.log('Box:', detection.box) // { x, y, width, height }
|
|
27
|
+
console.log('Landmarks:', landmarks) // 68 points with { x, y }
|
|
28
|
+
|
|
29
|
+
const orientation = calculateFaceOrientation(landmarks)
|
|
30
|
+
console.log('Yaw/Pitch/Roll:', orientation) // { yaw, pitch, roll } in degrees
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Draw results on a canvas
|
|
34
|
+
const ctx = canvas.getContext('2d')
|
|
35
|
+
drawResults(ctx, results, canvas.width, canvas.height)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## API
|
|
39
|
+
|
|
40
|
+
| Export | Description |
|
|
41
|
+
|---|---|
|
|
42
|
+
| `FaceTracker` | High-level class: load models, detect faces + landmarks |
|
|
43
|
+
| `calculateFaceOrientation(landmarks)` | Estimate yaw/pitch/roll from 68 landmarks |
|
|
44
|
+
| `drawResults(ctx, results, w, h)` | Draw bounding boxes + landmarks on canvas |
|
|
45
|
+
| `drawLandmarkPoints(ctx, landmarks)` | Draw landmark dots |
|
|
46
|
+
| `drawLandmarkConnections(ctx, landmarks)` | Draw landmark group connections |
|
|
47
|
+
| `FACE_LANDMARKS` | Named index groups (JAW, LEFT_EYE, NOSE_TIP, etc.) |
|
|
48
|
+
| `TinyFaceDetector` | Low-level face detection network |
|
|
49
|
+
| `FaceLandmark68Net` | Low-level landmark network |
|
|
50
|
+
| `postProcessLandmarks(raw, box, w, h)` | Convert raw network output to pixel coordinates |
|
|
51
|
+
|
|
52
|
+
## Models
|
|
53
|
+
|
|
54
|
+
Pre-trained weights are in the `models/` directory. Pass the serving URL to `loadModels()`:
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
// Serve models from your public directory
|
|
58
|
+
await tracker.loadModels('/models/')
|
|
59
|
+
|
|
60
|
+
// Or from a CDN/custom path
|
|
61
|
+
await tracker.loadModels('https://cdn.example.com/face-track-models/')
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Demo
|
|
65
|
+
|
|
66
|
+
See `examples/react-demo/` for a working React app:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
cd examples/react-demo
|
|
70
|
+
npm install
|
|
71
|
+
npm run dev
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## License
|
|
75
|
+
|
|
76
|
+
MIT
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import * as s from "@tensorflow/tfjs";
|
|
2
|
+
const T = [
|
|
3
|
+
{ x: 1.603231, y: 2.094468 },
|
|
4
|
+
{ x: 6.041143, y: 7.080126 },
|
|
5
|
+
{ x: 2.882459, y: 3.518061 },
|
|
6
|
+
{ x: 4.266906, y: 5.178857 },
|
|
7
|
+
{ x: 9.041765, y: 10.66308 }
|
|
8
|
+
], S = [117.001, 114.697, 97.404], B = [122.782, 117.001, 104.298], p = 112, _ = {
|
|
9
|
+
JAW: Array.from({ length: 17 }, (n, e) => e),
|
|
10
|
+
RIGHT_EYEBROW: Array.from({ length: 5 }, (n, e) => e + 17),
|
|
11
|
+
LEFT_EYEBROW: Array.from({ length: 5 }, (n, e) => e + 22),
|
|
12
|
+
NOSE_BRIDGE: Array.from({ length: 4 }, (n, e) => e + 27),
|
|
13
|
+
NOSE_TIP: Array.from({ length: 5 }, (n, e) => e + 31),
|
|
14
|
+
RIGHT_EYE: Array.from({ length: 6 }, (n, e) => e + 36),
|
|
15
|
+
LEFT_EYE: Array.from({ length: 6 }, (n, e) => e + 42),
|
|
16
|
+
OUTER_LIPS: Array.from({ length: 12 }, (n, e) => e + 48),
|
|
17
|
+
INNER_LIPS: Array.from({ length: 8 }, (n, e) => e + 60)
|
|
18
|
+
};
|
|
19
|
+
function A(n) {
|
|
20
|
+
return 1 / (1 + Math.exp(-n));
|
|
21
|
+
}
|
|
22
|
+
function F(n, e) {
|
|
23
|
+
const t = Math.max(n.x, e.x), o = Math.max(n.y, e.y), a = Math.min(n.x + n.width, e.x + e.width), r = Math.min(n.y + n.height, e.y + e.height), i = Math.max(0, a - t) * Math.max(0, r - o), c = n.width * n.height + e.width * e.height - i;
|
|
24
|
+
return i / c;
|
|
25
|
+
}
|
|
26
|
+
function Y(n, e) {
|
|
27
|
+
if (n.length === 0) return [];
|
|
28
|
+
const t = [...n].sort((a, r) => r.score - a.score), o = [];
|
|
29
|
+
for (; t.length > 0; ) {
|
|
30
|
+
const a = t.shift();
|
|
31
|
+
o.push(a);
|
|
32
|
+
for (let r = t.length - 1; r >= 0; r--)
|
|
33
|
+
F(a.box, t[r].box) > e && t.splice(r, 1);
|
|
34
|
+
}
|
|
35
|
+
return o;
|
|
36
|
+
}
|
|
37
|
+
function N(n) {
|
|
38
|
+
return s.tidy(() => {
|
|
39
|
+
const e = s.scalar(0.10000000149011612), t = s.mul(n, e);
|
|
40
|
+
return s.add(s.relu(s.sub(n, t)), t);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
class G {
|
|
44
|
+
constructor() {
|
|
45
|
+
this.weights = null;
|
|
46
|
+
}
|
|
47
|
+
async load(e) {
|
|
48
|
+
const o = await (await fetch(e + "tiny_face_detector_model-weights_manifest.json")).json();
|
|
49
|
+
this.weights = await s.io.loadWeights(o, e);
|
|
50
|
+
}
|
|
51
|
+
forward(e) {
|
|
52
|
+
return s.tidy(() => {
|
|
53
|
+
const t = this.weights;
|
|
54
|
+
let o = s.pad(e, [[0, 0], [1, 1], [1, 1], [0, 0]]);
|
|
55
|
+
o = s.add(s.conv2d(o, t["conv0/filters"], 1, "valid"), t["conv0/bias"]), o = N(o), o = s.maxPool(o, 2, 2, "same");
|
|
56
|
+
for (let a = 1; a <= 5; a++)
|
|
57
|
+
o = s.pad(o, [[0, 0], [1, 1], [1, 1], [0, 0]]), o = s.separableConv2d(
|
|
58
|
+
o,
|
|
59
|
+
t[`conv${a}/depthwise_filter`],
|
|
60
|
+
t[`conv${a}/pointwise_filter`],
|
|
61
|
+
1,
|
|
62
|
+
"valid"
|
|
63
|
+
), o = s.add(o, t[`conv${a}/bias`]), o = N(o), o = s.maxPool(o, 2, a === 5 ? 1 : 2, "same");
|
|
64
|
+
return o = s.add(s.conv2d(o, t["conv8/filters"], 1, "valid"), t["conv8/bias"]), o;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
async detect(e, t = {}) {
|
|
68
|
+
const { inputSize: o = 320, scoreThreshold: a = 0.5 } = t, r = s.browser.fromPixels(e), [i, c] = r.shape, h = s.tidy(() => {
|
|
69
|
+
const l = Math.max(i, c), w = s.pad(r, [[0, l - i], [0, l - c], [0, 0]]), M = s.image.resizeBilinear(w, [o, o]).expandDims(0).toFloat(), g = s.tensor1d(S);
|
|
70
|
+
return s.div(s.sub(M, g), 256);
|
|
71
|
+
});
|
|
72
|
+
r.dispose();
|
|
73
|
+
const f = this.forward(h);
|
|
74
|
+
h.dispose();
|
|
75
|
+
const u = await f.data(), m = f.shape[1];
|
|
76
|
+
f.dispose();
|
|
77
|
+
const d = this.decodeOutput(u, m, c, i, a);
|
|
78
|
+
return Y(d, 0.4);
|
|
79
|
+
}
|
|
80
|
+
decodeOutput(e, t, o, a, r) {
|
|
81
|
+
const i = T.length, c = 5, h = [], f = Math.max(o, a), u = f / o, m = f / a;
|
|
82
|
+
for (let d = 0; d < t; d++)
|
|
83
|
+
for (let l = 0; l < t; l++)
|
|
84
|
+
for (let w = 0; w < i; w++) {
|
|
85
|
+
const y = ((d * t + l) * i + w) * c, M = e[y + 4], g = A(M);
|
|
86
|
+
if (g < r) continue;
|
|
87
|
+
const O = e[y], P = e[y + 1], I = e[y + 2], b = e[y + 3], k = (l + A(O)) / t * u, $ = (d + A(P)) / t * m, L = Math.exp(I) * T[w].x / t * u, D = Math.exp(b) * T[w].y / t * m, E = (k - L / 2) * o, v = ($ - D / 2) * a, x = L * o, R = D * a;
|
|
88
|
+
x > 0 && R > 0 && E + x > 0 && v + R > 0 && h.push({
|
|
89
|
+
box: {
|
|
90
|
+
x: Math.max(0, E),
|
|
91
|
+
y: Math.max(0, v),
|
|
92
|
+
width: Math.min(x, o - Math.max(0, E)),
|
|
93
|
+
height: Math.min(R, a - Math.max(0, v))
|
|
94
|
+
},
|
|
95
|
+
score: g
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return h;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function z(n, e, t, o) {
|
|
102
|
+
const a = Math.max(t, o), r = p / a, i = t * r, c = o * r, h = t < o ? Math.abs(t - o) / 2 : 0, f = o < t ? Math.abs(t - o) / 2 : 0, u = [];
|
|
103
|
+
for (let m = 0; m < 68; m++) {
|
|
104
|
+
let d = n[m * 2] * p, l = n[m * 2 + 1] * p;
|
|
105
|
+
d -= h * r, l -= f * r, d /= i, l /= c, u.push({
|
|
106
|
+
x: d * t + e.x,
|
|
107
|
+
y: l * o + e.y
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return u;
|
|
111
|
+
}
|
|
112
|
+
class C {
|
|
113
|
+
constructor() {
|
|
114
|
+
this.weights = null;
|
|
115
|
+
}
|
|
116
|
+
async load(e) {
|
|
117
|
+
const o = await (await fetch(e + "face_landmark_68_model-weights_manifest.json")).json();
|
|
118
|
+
this.weights = await s.io.loadWeights(o, e);
|
|
119
|
+
}
|
|
120
|
+
denseBlock(e, t, o) {
|
|
121
|
+
const a = this.weights;
|
|
122
|
+
let r;
|
|
123
|
+
o ? r = s.add(
|
|
124
|
+
s.conv2d(e, a[`${t}/conv0/filters`], 2, "same"),
|
|
125
|
+
a[`${t}/conv0/bias`]
|
|
126
|
+
) : r = s.add(
|
|
127
|
+
s.separableConv2d(
|
|
128
|
+
e,
|
|
129
|
+
a[`${t}/conv0/depthwise_filter`],
|
|
130
|
+
a[`${t}/conv0/pointwise_filter`],
|
|
131
|
+
2,
|
|
132
|
+
"same"
|
|
133
|
+
),
|
|
134
|
+
a[`${t}/conv0/bias`]
|
|
135
|
+
), r = s.relu(r);
|
|
136
|
+
const i = s.add(
|
|
137
|
+
s.separableConv2d(
|
|
138
|
+
r,
|
|
139
|
+
a[`${t}/conv1/depthwise_filter`],
|
|
140
|
+
a[`${t}/conv1/pointwise_filter`],
|
|
141
|
+
1,
|
|
142
|
+
"same"
|
|
143
|
+
),
|
|
144
|
+
a[`${t}/conv1/bias`]
|
|
145
|
+
), c = s.relu(s.add(r, i)), h = s.add(
|
|
146
|
+
s.separableConv2d(
|
|
147
|
+
c,
|
|
148
|
+
a[`${t}/conv2/depthwise_filter`],
|
|
149
|
+
a[`${t}/conv2/pointwise_filter`],
|
|
150
|
+
1,
|
|
151
|
+
"same"
|
|
152
|
+
),
|
|
153
|
+
a[`${t}/conv2/bias`]
|
|
154
|
+
), f = s.relu(s.add(s.add(r, i), h)), u = s.add(
|
|
155
|
+
s.separableConv2d(
|
|
156
|
+
f,
|
|
157
|
+
a[`${t}/conv3/depthwise_filter`],
|
|
158
|
+
a[`${t}/conv3/pointwise_filter`],
|
|
159
|
+
1,
|
|
160
|
+
"same"
|
|
161
|
+
),
|
|
162
|
+
a[`${t}/conv3/bias`]
|
|
163
|
+
);
|
|
164
|
+
return s.relu(s.add(s.add(s.add(r, i), h), u));
|
|
165
|
+
}
|
|
166
|
+
forward(e) {
|
|
167
|
+
return s.tidy(() => {
|
|
168
|
+
let t = this.denseBlock(e, "dense0", !0);
|
|
169
|
+
t = this.denseBlock(t, "dense1", !1), t = this.denseBlock(t, "dense2", !1), t = this.denseBlock(t, "dense3", !1), t = s.avgPool(t, [7, 7], [2, 2], "valid"), t = s.reshape(t, [t.shape[0], -1]);
|
|
170
|
+
const o = this.weights;
|
|
171
|
+
return t = s.add(s.matMul(t, o["fc/weights"]), o["fc/bias"]), t;
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
async detectLandmarks(e, t) {
|
|
175
|
+
const [o, a] = e.shape, r = Math.max(0, Math.floor(t.x)), i = Math.max(0, Math.floor(t.y)), c = Math.min(Math.floor(t.width), a - r), h = Math.min(Math.floor(t.height), o - i);
|
|
176
|
+
if (c <= 0 || h <= 0) return [];
|
|
177
|
+
const f = s.tidy(() => {
|
|
178
|
+
let d = s.slice(e, [i, r, 0], [h, c, 3]);
|
|
179
|
+
const l = Math.max(c, h), w = Math.floor((l - c) / 2), y = Math.floor((l - h) / 2);
|
|
180
|
+
d = s.pad(d, [
|
|
181
|
+
[y, l - h - y],
|
|
182
|
+
[w, l - c - w],
|
|
183
|
+
[0, 0]
|
|
184
|
+
]), d = s.image.resizeBilinear(d, [p, p]);
|
|
185
|
+
const M = d.expandDims(0).toFloat(), g = s.tensor1d(B);
|
|
186
|
+
return s.div(s.sub(M, g), 255);
|
|
187
|
+
}), u = this.forward(f);
|
|
188
|
+
f.dispose();
|
|
189
|
+
const m = await u.data();
|
|
190
|
+
return u.dispose(), z(m, t, c, h);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function j(n, e) {
|
|
194
|
+
n.fillStyle = "#00ff00";
|
|
195
|
+
for (const t of e)
|
|
196
|
+
n.beginPath(), n.arc(t.x, t.y, 2, 0, 2 * Math.PI), n.fill();
|
|
197
|
+
}
|
|
198
|
+
function W(n, e) {
|
|
199
|
+
n.strokeStyle = "#ff0000", n.lineWidth = 1;
|
|
200
|
+
const t = (o, a = !1) => {
|
|
201
|
+
if (!o.some((r) => r >= e.length)) {
|
|
202
|
+
n.beginPath(), n.moveTo(e[o[0]].x, e[o[0]].y);
|
|
203
|
+
for (let r = 1; r < o.length; r++)
|
|
204
|
+
n.lineTo(e[o[r]].x, e[o[r]].y);
|
|
205
|
+
a && n.closePath(), n.stroke();
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
t(_.JAW), t(_.RIGHT_EYEBROW), t(_.LEFT_EYEBROW), t(_.NOSE_BRIDGE), t(_.NOSE_TIP), t(_.RIGHT_EYE, !0), t(_.LEFT_EYE, !0), t(_.OUTER_LIPS, !0), t(_.INNER_LIPS, !0);
|
|
209
|
+
}
|
|
210
|
+
function X(n, e, t, o) {
|
|
211
|
+
if (n.clearRect(0, 0, t, o), !(!e || e.length === 0))
|
|
212
|
+
for (const { detection: a, landmarks: r } of e) {
|
|
213
|
+
const { box: i } = a;
|
|
214
|
+
n.strokeStyle = "#00ff00", n.lineWidth = 2, n.strokeRect(i.x, i.y, i.width, i.height), r.length > 0 && (W(n, r), j(n, r));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
function K(n) {
|
|
218
|
+
if (!n || n.length < 68)
|
|
219
|
+
return { yaw: 0, pitch: 0, roll: 0 };
|
|
220
|
+
const e = n[36], t = n[45], o = n[30], a = Math.sqrt(
|
|
221
|
+
(t.x - e.x) ** 2 + (t.y - e.y) ** 2
|
|
222
|
+
);
|
|
223
|
+
if (a === 0) return { yaw: 0, pitch: 0, roll: 0 };
|
|
224
|
+
const r = (e.x + t.x) / 2 - o.x, i = o.y - (e.y + t.y) / 2;
|
|
225
|
+
return {
|
|
226
|
+
yaw: r / a * 30,
|
|
227
|
+
pitch: i / a * 30,
|
|
228
|
+
roll: Math.atan2(t.y - e.y, t.x - e.x) * (180 / Math.PI)
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
class J {
|
|
232
|
+
constructor() {
|
|
233
|
+
this.faceDetector = new G(), this.landmarkNet = new C(), this.isLoaded = !1;
|
|
234
|
+
}
|
|
235
|
+
async loadModels(e = "/models/") {
|
|
236
|
+
try {
|
|
237
|
+
return await this.faceDetector.load(e), await this.landmarkNet.load(e), this.isLoaded = !0, !0;
|
|
238
|
+
} catch (t) {
|
|
239
|
+
return console.error("Failed to load models:", t), !1;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
async detectFacesWithLandmarks(e, t) {
|
|
243
|
+
if (!this.isLoaded) throw new Error("Models not loaded");
|
|
244
|
+
const o = await this.faceDetector.detect(e, t);
|
|
245
|
+
if (o.length === 0) return [];
|
|
246
|
+
const a = s.browser.fromPixels(e), r = [];
|
|
247
|
+
for (const i of o) {
|
|
248
|
+
const c = await this.landmarkNet.detectLandmarks(a, i.box);
|
|
249
|
+
r.push({ detection: i, landmarks: c });
|
|
250
|
+
}
|
|
251
|
+
return a.dispose(), r;
|
|
252
|
+
}
|
|
253
|
+
calculateFaceOrientation(e) {
|
|
254
|
+
return K(e);
|
|
255
|
+
}
|
|
256
|
+
drawResults(e, t, o, a) {
|
|
257
|
+
return X(e, t, o, a);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
export {
|
|
261
|
+
_ as FACE_LANDMARKS,
|
|
262
|
+
C as FaceLandmark68Net,
|
|
263
|
+
J as FaceTracker,
|
|
264
|
+
G as TinyFaceDetector,
|
|
265
|
+
K as calculateFaceOrientation,
|
|
266
|
+
W as drawLandmarkConnections,
|
|
267
|
+
j as drawLandmarkPoints,
|
|
268
|
+
X as drawResults,
|
|
269
|
+
z as postProcessLandmarks
|
|
270
|
+
};
|
|
271
|
+
//# sourceMappingURL=face-track.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"face-track.js","sources":["../src/constants.js","../src/utils.js","../src/TinyFaceDetector.js","../src/FaceLandmark68Net.js","../src/drawing.js","../src/FaceTracker.js"],"sourcesContent":["export const DETECTOR_ANCHORS = [\n { x: 1.603231, y: 2.094468 },\n { x: 6.041143, y: 7.080126 },\n { x: 2.882459, y: 3.518061 },\n { x: 4.266906, y: 5.178857 },\n { x: 9.041765, y: 10.66308 },\n]\n\nexport const DETECTOR_MEAN_RGB = [117.001, 114.697, 97.404]\nexport const LANDMARK_MEAN_RGB = [122.782, 117.001, 104.298]\nexport const LANDMARK_INPUT_SIZE = 112\n\nexport const FACE_LANDMARKS = {\n JAW: Array.from({ length: 17 }, (_, i) => i),\n RIGHT_EYEBROW: Array.from({ length: 5 }, (_, i) => i + 17),\n LEFT_EYEBROW: Array.from({ length: 5 }, (_, i) => i + 22),\n NOSE_BRIDGE: Array.from({ length: 4 }, (_, i) => i + 27),\n NOSE_TIP: Array.from({ length: 5 }, (_, i) => i + 31),\n RIGHT_EYE: Array.from({ length: 6 }, (_, i) => i + 36),\n LEFT_EYE: Array.from({ length: 6 }, (_, i) => i + 42),\n OUTER_LIPS: Array.from({ length: 12 }, (_, i) => i + 48),\n INNER_LIPS: Array.from({ length: 8 }, (_, i) => i + 60),\n}\n","export function sigmoid(x) {\n return 1 / (1 + Math.exp(-x))\n}\n\nexport function iou(a, b) {\n const x1 = Math.max(a.x, b.x)\n const y1 = Math.max(a.y, b.y)\n const x2 = Math.min(a.x + a.width, b.x + b.width)\n const y2 = Math.min(a.y + a.height, b.y + b.height)\n const inter = Math.max(0, x2 - x1) * Math.max(0, y2 - y1)\n const union = a.width * a.height + b.width * b.height - inter\n return inter / union\n}\n\nexport function nms(boxes, iouThreshold) {\n if (boxes.length === 0) return []\n const sorted = [...boxes].sort((a, b) => b.score - a.score)\n const selected = []\n while (sorted.length > 0) {\n const current = sorted.shift()\n selected.push(current)\n for (let i = sorted.length - 1; i >= 0; i--) {\n if (iou(current.box, sorted[i].box) > iouThreshold) {\n sorted.splice(i, 1)\n }\n }\n }\n return selected\n}\n","import * as tf from '@tensorflow/tfjs'\nimport { sigmoid, nms } from './utils.js'\nimport { DETECTOR_ANCHORS, DETECTOR_MEAN_RGB } from './constants.js'\n\nexport function leakyRelu(x) {\n return tf.tidy(() => {\n const alpha = tf.scalar(0.10000000149011612)\n const min = tf.mul(x, alpha)\n return tf.add(tf.relu(tf.sub(x, min)), min)\n })\n}\n\nexport class TinyFaceDetector {\n constructor() {\n this.weights = null\n }\n\n async load(baseUrl) {\n const resp = await fetch(baseUrl + 'tiny_face_detector_model-weights_manifest.json')\n const manifest = await resp.json()\n this.weights = await tf.io.loadWeights(manifest, baseUrl)\n }\n\n forward(x) {\n return tf.tidy(() => {\n const w = this.weights\n\n let out = tf.pad(x, [[0, 0], [1, 1], [1, 1], [0, 0]])\n out = tf.add(tf.conv2d(out, w['conv0/filters'], 1, 'valid'), w['conv0/bias'])\n out = leakyRelu(out)\n out = tf.maxPool(out, 2, 2, 'same')\n\n for (let i = 1; i <= 5; i++) {\n out = tf.pad(out, [[0, 0], [1, 1], [1, 1], [0, 0]])\n out = tf.separableConv2d(\n out,\n w[`conv${i}/depthwise_filter`],\n w[`conv${i}/pointwise_filter`],\n 1, 'valid'\n )\n out = tf.add(out, w[`conv${i}/bias`])\n out = leakyRelu(out)\n out = tf.maxPool(out, 2, i === 5 ? 1 : 2, 'same')\n }\n\n out = tf.add(tf.conv2d(out, w['conv8/filters'], 1, 'valid'), w['conv8/bias'])\n\n return out\n })\n }\n\n async detect(input, options = {}) {\n const { inputSize = 320, scoreThreshold = 0.5 } = options\n\n const imgTensor = tf.browser.fromPixels(input)\n const [origH, origW] = imgTensor.shape\n\n const preprocessed = tf.tidy(() => {\n const maxDim = Math.max(origH, origW)\n const padded = tf.pad(imgTensor, [[0, maxDim - origH], [0, maxDim - origW], [0, 0]])\n const resized = tf.image.resizeBilinear(padded, [inputSize, inputSize])\n const batched = resized.expandDims(0).toFloat()\n const mean = tf.tensor1d(DETECTOR_MEAN_RGB)\n return tf.div(tf.sub(batched, mean), 256)\n })\n\n imgTensor.dispose()\n\n const output = this.forward(preprocessed)\n preprocessed.dispose()\n\n const outputData = await output.data()\n const numCells = output.shape[1]\n output.dispose()\n\n const boxes = this.decodeOutput(outputData, numCells, origW, origH, scoreThreshold)\n return nms(boxes, 0.4)\n }\n\n decodeOutput(data, numCells, origW, origH, threshold) {\n const numAnchors = DETECTOR_ANCHORS.length\n const boxEncoding = 5\n const boxes = []\n\n const maxDim = Math.max(origW, origH)\n const corrX = maxDim / origW\n const corrY = maxDim / origH\n\n for (let row = 0; row < numCells; row++) {\n for (let col = 0; col < numCells; col++) {\n for (let a = 0; a < numAnchors; a++) {\n const offset = ((row * numCells + col) * numAnchors + a) * boxEncoding\n\n const rawScore = data[offset + 4]\n const score = sigmoid(rawScore)\n if (score < threshold) continue\n\n const tx = data[offset]\n const ty = data[offset + 1]\n const tw = data[offset + 2]\n const th = data[offset + 3]\n\n const ctX = ((col + sigmoid(tx)) / numCells) * corrX\n const ctY = ((row + sigmoid(ty)) / numCells) * corrY\n const w = (Math.exp(tw) * DETECTOR_ANCHORS[a].x / numCells) * corrX\n const h = (Math.exp(th) * DETECTOR_ANCHORS[a].y / numCells) * corrY\n\n const x = (ctX - w / 2) * origW\n const y = (ctY - h / 2) * origH\n const width = w * origW\n const height = h * origH\n\n if (width > 0 && height > 0 && x + width > 0 && y + height > 0) {\n boxes.push({\n box: {\n x: Math.max(0, x),\n y: Math.max(0, y),\n width: Math.min(width, origW - Math.max(0, x)),\n height: Math.min(height, origH - Math.max(0, y)),\n },\n score,\n })\n }\n }\n }\n }\n\n return boxes\n }\n}\n","import * as tf from '@tensorflow/tfjs'\nimport { LANDMARK_MEAN_RGB, LANDMARK_INPUT_SIZE } from './constants.js'\n\nexport function postProcessLandmarks(rawData, box, cropW, cropH) {\n const maxDim = Math.max(cropW, cropH)\n const scale = LANDMARK_INPUT_SIZE / maxDim\n const scaledW = cropW * scale\n const scaledH = cropH * scale\n const padX = cropW < cropH ? Math.abs(cropW - cropH) / 2 : 0\n const padY = cropH < cropW ? Math.abs(cropW - cropH) / 2 : 0\n\n const landmarks = []\n for (let i = 0; i < 68; i++) {\n let lx = rawData[i * 2] * LANDMARK_INPUT_SIZE\n let ly = rawData[i * 2 + 1] * LANDMARK_INPUT_SIZE\n\n lx -= padX * scale\n ly -= padY * scale\n\n lx /= scaledW\n ly /= scaledH\n\n landmarks.push({\n x: lx * cropW + box.x,\n y: ly * cropH + box.y,\n })\n }\n\n return landmarks\n}\n\nexport class FaceLandmark68Net {\n constructor() {\n this.weights = null\n }\n\n async load(baseUrl) {\n const resp = await fetch(baseUrl + 'face_landmark_68_model-weights_manifest.json')\n const manifest = await resp.json()\n this.weights = await tf.io.loadWeights(manifest, baseUrl)\n }\n\n denseBlock(x, prefix, isFirstLayer) {\n const w = this.weights\n\n let out1\n if (isFirstLayer) {\n out1 = tf.add(\n tf.conv2d(x, w[`${prefix}/conv0/filters`], 2, 'same'),\n w[`${prefix}/conv0/bias`]\n )\n } else {\n out1 = tf.add(\n tf.separableConv2d(\n x,\n w[`${prefix}/conv0/depthwise_filter`],\n w[`${prefix}/conv0/pointwise_filter`],\n 2, 'same'\n ),\n w[`${prefix}/conv0/bias`]\n )\n }\n out1 = tf.relu(out1)\n\n const out2 = tf.add(\n tf.separableConv2d(\n out1,\n w[`${prefix}/conv1/depthwise_filter`],\n w[`${prefix}/conv1/pointwise_filter`],\n 1, 'same'\n ),\n w[`${prefix}/conv1/bias`]\n )\n\n const in3 = tf.relu(tf.add(out1, out2))\n const out3 = tf.add(\n tf.separableConv2d(\n in3,\n w[`${prefix}/conv2/depthwise_filter`],\n w[`${prefix}/conv2/pointwise_filter`],\n 1, 'same'\n ),\n w[`${prefix}/conv2/bias`]\n )\n\n const in4 = tf.relu(tf.add(tf.add(out1, out2), out3))\n const out4 = tf.add(\n tf.separableConv2d(\n in4,\n w[`${prefix}/conv3/depthwise_filter`],\n w[`${prefix}/conv3/pointwise_filter`],\n 1, 'same'\n ),\n w[`${prefix}/conv3/bias`]\n )\n\n return tf.relu(tf.add(tf.add(tf.add(out1, out2), out3), out4))\n }\n\n forward(x) {\n return tf.tidy(() => {\n let out = this.denseBlock(x, 'dense0', true)\n out = this.denseBlock(out, 'dense1', false)\n out = this.denseBlock(out, 'dense2', false)\n out = this.denseBlock(out, 'dense3', false)\n\n out = tf.avgPool(out, [7, 7], [2, 2], 'valid')\n out = tf.reshape(out, [out.shape[0], -1])\n\n const w = this.weights\n out = tf.add(tf.matMul(out, w['fc/weights']), w['fc/bias'])\n\n return out\n })\n }\n\n async detectLandmarks(imageTensor, box) {\n const [imgH, imgW] = imageTensor.shape\n\n const x = Math.max(0, Math.floor(box.x))\n const y = Math.max(0, Math.floor(box.y))\n const w = Math.min(Math.floor(box.width), imgW - x)\n const h = Math.min(Math.floor(box.height), imgH - y)\n if (w <= 0 || h <= 0) return []\n\n const preprocessed = tf.tidy(() => {\n let face = tf.slice(imageTensor, [y, x, 0], [h, w, 3])\n\n const maxDim = Math.max(w, h)\n const padX = Math.floor((maxDim - w) / 2)\n const padY = Math.floor((maxDim - h) / 2)\n face = tf.pad(face, [\n [padY, maxDim - h - padY],\n [padX, maxDim - w - padX],\n [0, 0],\n ])\n\n face = tf.image.resizeBilinear(face, [LANDMARK_INPUT_SIZE, LANDMARK_INPUT_SIZE])\n\n const batched = face.expandDims(0).toFloat()\n const mean = tf.tensor1d(LANDMARK_MEAN_RGB)\n return tf.div(tf.sub(batched, mean), 255)\n })\n\n const output = this.forward(preprocessed)\n preprocessed.dispose()\n\n const rawData = await output.data()\n output.dispose()\n\n return postProcessLandmarks(rawData, box, w, h)\n }\n}\n","import { FACE_LANDMARKS } from './constants.js'\n\nexport function drawLandmarkPoints(ctx, landmarks) {\n ctx.fillStyle = '#00ff00'\n for (const pt of landmarks) {\n ctx.beginPath()\n ctx.arc(pt.x, pt.y, 2, 0, 2 * Math.PI)\n ctx.fill()\n }\n}\n\nexport function drawLandmarkConnections(ctx, landmarks) {\n ctx.strokeStyle = '#ff0000'\n ctx.lineWidth = 1\n\n const drawLine = (indices, close = false) => {\n if (indices.some(i => i >= landmarks.length)) return\n ctx.beginPath()\n ctx.moveTo(landmarks[indices[0]].x, landmarks[indices[0]].y)\n for (let i = 1; i < indices.length; i++) {\n ctx.lineTo(landmarks[indices[i]].x, landmarks[indices[i]].y)\n }\n if (close) ctx.closePath()\n ctx.stroke()\n }\n\n drawLine(FACE_LANDMARKS.JAW)\n drawLine(FACE_LANDMARKS.RIGHT_EYEBROW)\n drawLine(FACE_LANDMARKS.LEFT_EYEBROW)\n drawLine(FACE_LANDMARKS.NOSE_BRIDGE)\n drawLine(FACE_LANDMARKS.NOSE_TIP)\n drawLine(FACE_LANDMARKS.RIGHT_EYE, true)\n drawLine(FACE_LANDMARKS.LEFT_EYE, true)\n drawLine(FACE_LANDMARKS.OUTER_LIPS, true)\n drawLine(FACE_LANDMARKS.INNER_LIPS, true)\n}\n\nexport function drawResults(ctx, results, canvasWidth, canvasHeight) {\n ctx.clearRect(0, 0, canvasWidth, canvasHeight)\n if (!results || results.length === 0) return\n\n for (const { detection, landmarks } of results) {\n const { box } = detection\n ctx.strokeStyle = '#00ff00'\n ctx.lineWidth = 2\n ctx.strokeRect(box.x, box.y, box.width, box.height)\n\n if (landmarks.length > 0) {\n drawLandmarkConnections(ctx, landmarks)\n drawLandmarkPoints(ctx, landmarks)\n }\n }\n}\n","import * as tf from '@tensorflow/tfjs'\nimport { TinyFaceDetector } from './TinyFaceDetector.js'\nimport { FaceLandmark68Net } from './FaceLandmark68Net.js'\nimport { drawResults } from './drawing.js'\n\nexport function calculateFaceOrientation(landmarks) {\n if (!landmarks || landmarks.length < 68) {\n return { yaw: 0, pitch: 0, roll: 0 }\n }\n\n const leftEye = landmarks[36]\n const rightEye = landmarks[45]\n const noseTip = landmarks[30]\n\n const eyeDist = Math.sqrt(\n (rightEye.x - leftEye.x) ** 2 + (rightEye.y - leftEye.y) ** 2\n )\n if (eyeDist === 0) return { yaw: 0, pitch: 0, roll: 0 }\n\n const hOffset = ((leftEye.x + rightEye.x) / 2) - noseTip.x\n const vOffset = noseTip.y - ((leftEye.y + rightEye.y) / 2)\n\n return {\n yaw: (hOffset / eyeDist) * 30,\n pitch: (vOffset / eyeDist) * 30,\n roll: Math.atan2(rightEye.y - leftEye.y, rightEye.x - leftEye.x) * (180 / Math.PI),\n }\n}\n\nexport class FaceTracker {\n constructor() {\n this.faceDetector = new TinyFaceDetector()\n this.landmarkNet = new FaceLandmark68Net()\n this.isLoaded = false\n }\n\n async loadModels(baseUrl = '/models/') {\n try {\n await this.faceDetector.load(baseUrl)\n await this.landmarkNet.load(baseUrl)\n this.isLoaded = true\n return true\n } catch (error) {\n console.error('Failed to load models:', error)\n return false\n }\n }\n\n async detectFacesWithLandmarks(input, options) {\n if (!this.isLoaded) throw new Error('Models not loaded')\n\n const faces = await this.faceDetector.detect(input, options)\n if (faces.length === 0) return []\n\n const imgTensor = tf.browser.fromPixels(input)\n const results = []\n\n for (const face of faces) {\n const landmarks = await this.landmarkNet.detectLandmarks(imgTensor, face.box)\n results.push({ detection: face, landmarks })\n }\n\n imgTensor.dispose()\n return results\n }\n\n calculateFaceOrientation(landmarks) {\n return calculateFaceOrientation(landmarks)\n }\n\n drawResults(ctx, results, canvasWidth, canvasHeight) {\n return drawResults(ctx, results, canvasWidth, canvasHeight)\n }\n}\n"],"names":["DETECTOR_ANCHORS","DETECTOR_MEAN_RGB","LANDMARK_MEAN_RGB","LANDMARK_INPUT_SIZE","FACE_LANDMARKS","_","i","sigmoid","x","iou","a","b","x1","y1","x2","y2","inter","union","nms","boxes","iouThreshold","sorted","selected","current","leakyRelu","tf","alpha","min","TinyFaceDetector","baseUrl","manifest","w","out","input","options","inputSize","scoreThreshold","imgTensor","origH","origW","preprocessed","maxDim","padded","batched","mean","output","outputData","numCells","data","threshold","numAnchors","boxEncoding","corrX","corrY","row","col","offset","rawScore","score","tx","ty","tw","th","ctX","ctY","h","y","width","height","postProcessLandmarks","rawData","box","cropW","cropH","scale","scaledW","scaledH","padX","padY","landmarks","lx","ly","FaceLandmark68Net","prefix","isFirstLayer","out1","out2","in3","out3","in4","out4","imageTensor","imgH","imgW","face","drawLandmarkPoints","ctx","pt","drawLandmarkConnections","drawLine","indices","close","drawResults","results","canvasWidth","canvasHeight","detection","calculateFaceOrientation","leftEye","rightEye","noseTip","eyeDist","hOffset","vOffset","FaceTracker","error","faces"],"mappings":"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"}
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"weights":[{"name":"dense0/conv0/filters","shape":[3,3,3,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.004853619781194949,"min":-0.5872879935245888}},{"name":"dense0/conv0/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.004396426443960153,"min":-0.7298067896973853}},{"name":"dense0/conv1/depthwise_filter","shape":[3,3,32,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00635151559231328,"min":-0.5589333721235686}},{"name":"dense0/conv1/pointwise_filter","shape":[1,1,32,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.009354315552057004,"min":-1.2628325995276957}},{"name":"dense0/conv1/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0029380727048013726,"min":-0.5846764682554731}},{"name":"dense0/conv2/depthwise_filter","shape":[3,3,32,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0049374802439820535,"min":-0.6171850304977566}},{"name":"dense0/conv2/pointwise_filter","shape":[1,1,32,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.009941946758943446,"min":-1.3421628124573652}},{"name":"dense0/conv2/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0030300481062309416,"min":-0.5272283704841838}},{"name":"dense0/conv3/depthwise_filter","shape":[3,3,32,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.005672684837790097,"min":-0.7431217137505026}},{"name":"dense0/conv3/pointwise_filter","shape":[1,1,32,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.010712201455060173,"min":-1.5639814124387852}},{"name":"dense0/conv3/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0030966934035806097,"min":-0.3839899820439956}},{"name":"dense1/conv0/depthwise_filter","shape":[3,3,32,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0039155554537679636,"min":-0.48161332081345953}},{"name":"dense1/conv0/pointwise_filter","shape":[1,1,32,64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.01023082966898002,"min":-1.094698774580862}},{"name":"dense1/conv0/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0027264176630506327,"min":-0.3871513081531898}},{"name":"dense1/conv1/depthwise_filter","shape":[3,3,64,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.004583378632863362,"min":-0.5454220573107401}},{"name":"dense1/conv1/pointwise_filter","shape":[1,1,64,64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00915846403907327,"min":-1.117332612766939}},{"name":"dense1/conv1/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.003091680419211294,"min":-0.5966943209077797}},{"name":"dense1/conv2/depthwise_filter","shape":[3,3,64,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.005407439727409214,"min":-0.708374604290607}},{"name":"dense1/conv2/pointwise_filter","shape":[1,1,64,64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00946493943532308,"min":-1.2399070660273235}},{"name":"dense1/conv2/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.004409168514550901,"min":-0.9788354102303}},{"name":"dense1/conv3/depthwise_filter","shape":[3,3,64,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.004478132958505668,"min":-0.6493292789833219}},{"name":"dense1/conv3/pointwise_filter","shape":[1,1,64,64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.011063695888893277,"min":-1.2501976354449402}},{"name":"dense1/conv3/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.003909627596537272,"min":-0.6646366914113363}},{"name":"dense2/conv0/depthwise_filter","shape":[3,3,64,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.003213915404151468,"min":-0.3374611174359041}},{"name":"dense2/conv0/pointwise_filter","shape":[1,1,64,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.010917326048308728,"min":-1.4520043644250609}},{"name":"dense2/conv0/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.002800439152063108,"min":-0.38085972468058266}},{"name":"dense2/conv1/depthwise_filter","shape":[3,3,128,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0050568851770139206,"min":-0.6927932692509071}},{"name":"dense2/conv1/pointwise_filter","shape":[1,1,128,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.01074961213504567,"min":-1.3222022926106174}},{"name":"dense2/conv1/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0030654204242369708,"min":-0.5487102559384177}},{"name":"dense2/conv2/depthwise_filter","shape":[3,3,128,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00591809165244009,"min":-0.917304206128214}},{"name":"dense2/conv2/pointwise_filter","shape":[1,1,128,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.01092823346455892,"min":-1.366029183069865}},{"name":"dense2/conv2/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.002681120470458386,"min":-0.36463238398234055}},{"name":"dense2/conv3/depthwise_filter","shape":[3,3,128,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0048311497650894465,"min":-0.5797379718107336}},{"name":"dense2/conv3/pointwise_filter","shape":[1,1,128,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.011227761062921263,"min":-1.4483811771168429}},{"name":"dense2/conv3/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0034643323982463162,"min":-0.3360402426298927}},{"name":"dense3/conv0/depthwise_filter","shape":[3,3,128,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.003394978887894574,"min":-0.49227193874471326}},{"name":"dense3/conv0/pointwise_filter","shape":[1,1,128,256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.010051267287310432,"min":-1.2765109454884247}},{"name":"dense3/conv0/bias","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.003142924752889895,"min":-0.4588670139219247}},{"name":"dense3/conv1/depthwise_filter","shape":[3,3,256,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00448304671867221,"min":-0.5872791201460595}},{"name":"dense3/conv1/pointwise_filter","shape":[1,1,256,256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.016063522357566685,"min":-2.3613377865623026}},{"name":"dense3/conv1/bias","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00287135781026354,"min":-0.47664539650374765}},{"name":"dense3/conv2/depthwise_filter","shape":[3,3,256,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.006002906724518421,"min":-0.7923836876364315}},{"name":"dense3/conv2/pointwise_filter","shape":[1,1,256,256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.017087187019048954,"min":-1.6061955797906016}},{"name":"dense3/conv2/bias","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.003124481205846749,"min":-0.46242321846531886}},{"name":"dense3/conv3/depthwise_filter","shape":[3,3,256,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.006576311588287353,"min":-1.0193282961845398}},{"name":"dense3/conv3/pointwise_filter","shape":[1,1,256,256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.015590153955945782,"min":-1.99553970636106}},{"name":"dense3/conv3/bias","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.004453541601405424,"min":-0.6546706154065973}},{"name":"fc/weights","shape":[256,136],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.010417488509533453,"min":-1.500118345372817}},{"name":"fc/bias","shape":[136],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0025084222648658005,"min":0.07683877646923065}}],"paths":["face_landmark_68_model-shard1"]}]
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"weights":[{"name":"conv0/filters","shape":[3,3,3,16],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.009007044399485869,"min":-1.2069439495311063}},{"name":"conv0/bias","shape":[16],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.005263455241334205,"min":-0.9211046672334858}},{"name":"conv1/depthwise_filter","shape":[3,3,16,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.004001977630690033,"min":-0.5042491814669441}},{"name":"conv1/pointwise_filter","shape":[1,1,16,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.013836609615999109,"min":-1.411334180831909}},{"name":"conv1/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0015159862590771096,"min":-0.30926119685173037}},{"name":"conv2/depthwise_filter","shape":[3,3,32,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.002666276225856706,"min":-0.317286870876948}},{"name":"conv2/pointwise_filter","shape":[1,1,32,64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.015265831292844286,"min":-1.6792414422128714}},{"name":"conv2/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0020280554598453,"min":-0.37113414915168985}},{"name":"conv3/depthwise_filter","shape":[3,3,64,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.006100742489683862,"min":-0.8907084034938438}},{"name":"conv3/pointwise_filter","shape":[1,1,64,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.016276211832083907,"min":-2.0508026908425725}},{"name":"conv3/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.003394414279975143,"min":-0.7637432129944072}},{"name":"conv4/depthwise_filter","shape":[3,3,128,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.006716050119961009,"min":-0.8059260143953211}},{"name":"conv4/pointwise_filter","shape":[1,1,128,256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.021875603993733724,"min":-2.8875797271728514}},{"name":"conv4/bias","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0041141652009066415,"min":-0.8187188749804216}},{"name":"conv5/depthwise_filter","shape":[3,3,256,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.008423839597141042,"min":-0.9013508368940915}},{"name":"conv5/pointwise_filter","shape":[1,1,256,512],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.030007277283014035,"min":-3.8709387695088107}},{"name":"conv5/bias","shape":[512],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.008402082966823203,"min":-1.4871686851277068}},{"name":"conv8/filters","shape":[1,1,512,25],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.028336129469030042,"min":-4.675461362389957}},{"name":"conv8/bias","shape":[25],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.002268134028303857,"min":-0.41053225912299807}}],"paths":["tiny_face_detector_model-shard1"]}]
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "face-track",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Real-time face landmark detection and tracking using TensorFlow.js",
|
|
6
|
+
"main": "dist/face-track.js",
|
|
7
|
+
"module": "dist/face-track.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/face-track.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"models"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "vite build",
|
|
19
|
+
"prepublishOnly": "npm test && npm run build",
|
|
20
|
+
"test": "vitest run",
|
|
21
|
+
"test:watch": "vitest",
|
|
22
|
+
"lint": "eslint ."
|
|
23
|
+
},
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/yogthos/face-track.git"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"face-detection",
|
|
30
|
+
"face-landmarks",
|
|
31
|
+
"tensorflow",
|
|
32
|
+
"tfjs",
|
|
33
|
+
"computer-vision"
|
|
34
|
+
],
|
|
35
|
+
"author": "yogthos",
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"@tensorflow/tfjs": "^4.0.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@eslint/js": "^9.36.0",
|
|
42
|
+
"@tensorflow/tfjs": "^4.22.0",
|
|
43
|
+
"eslint": "^9.36.0",
|
|
44
|
+
"globals": "^16.4.0",
|
|
45
|
+
"vite": "^7.1.7",
|
|
46
|
+
"vitest": "^4.0.18"
|
|
47
|
+
}
|
|
48
|
+
}
|