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 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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"}
@@ -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"]}]
@@ -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
+ }