optimazed camera controll again
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
<title>Svelte app</title>
|
<title>Svelte app</title>
|
||||||
|
|
||||||
<link rel='icon' type='image/png' href='/favicon.png'>
|
<link rel='icon' type='image/png' href='/assets/nubzuki.png'>
|
||||||
<link rel='stylesheet' href='/global.css'>
|
<link rel='stylesheet' href='/global.css'>
|
||||||
<link rel='stylesheet' href='/build/bundle.css'>
|
<link rel='stylesheet' href='/build/bundle.css'>
|
||||||
|
|
||||||
|
|||||||
@@ -1,91 +1,89 @@
|
|||||||
import { FaceDetector, FilesetResolver } from '@mediapipe/tasks-vision';
|
import { FaceLandmarker, FilesetResolver } from '@mediapipe/tasks-vision';
|
||||||
|
|
||||||
export const cameraInput = {
|
export const cameraInput = {
|
||||||
left: false,
|
left: false,
|
||||||
right: false,
|
right: false,
|
||||||
active: false,
|
active: false,
|
||||||
zone: 'center',
|
zone: 'center'
|
||||||
x: 0.5
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let video;
|
let video;
|
||||||
let faceDetector;
|
let faceLandmarker;
|
||||||
|
let lastTime = 0;
|
||||||
|
|
||||||
// 🔥 NARROW CENTER ZONE:
|
const DETECTION_INTERVAL = 60;
|
||||||
// 0.48 and 0.52 means the center is only 4% of the screen width.
|
|
||||||
// Adjust these if it's still too hard to trigger movement.
|
|
||||||
const LEFT_THRESHOLD = 0.48;
|
|
||||||
const RIGHT_THRESHOLD = 0.52;
|
|
||||||
|
|
||||||
export async function initCameraControl() {
|
export async function initCameraControl() {
|
||||||
if (cameraInput.active) return video;
|
if (cameraInput.active)
|
||||||
|
return video;
|
||||||
|
|
||||||
video = document.createElement('video');
|
video = document.createElement('video');
|
||||||
video.autoplay = true;
|
video.autoplay = true;
|
||||||
video.playsInline = true;
|
video.playsInline = true;
|
||||||
video.muted = true;
|
video.muted = true;
|
||||||
|
|
||||||
|
// Set low resolution and frame rate for better performance
|
||||||
const stream = await navigator.mediaDevices.getUserMedia({
|
const stream = await navigator.mediaDevices.getUserMedia({
|
||||||
video: {
|
video: {
|
||||||
width: 160,
|
width: { ideal: 160 },
|
||||||
height: 120,
|
height: { ideal: 120 },
|
||||||
frameRate: { ideal: 20 } // Slightly higher for faster reaction
|
frameRate: { ideal: 30 }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
video.srcObject = stream;
|
video.srcObject = stream;
|
||||||
|
|
||||||
|
//load models for face landmark detection
|
||||||
const vision = await FilesetResolver.forVisionTasks(
|
const vision = await FilesetResolver.forVisionTasks(
|
||||||
'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision/wasm'
|
'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision/wasm'
|
||||||
);
|
);
|
||||||
|
faceLandmarker = await FaceLandmarker.createFromOptions(vision, {
|
||||||
faceDetector = await FaceDetector.createFromOptions(vision, {
|
|
||||||
baseOptions: {
|
baseOptions: {
|
||||||
modelAssetPath: `https://storage.googleapis.com/mediapipe-models/face_detector/blaze_face_short_range/float16/1/blaze_face_short_range.task`,
|
modelAssetPath:
|
||||||
delegate: 'GPU'
|
'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task',
|
||||||
},
|
},
|
||||||
runningMode: 'VIDEO'
|
runningMode: 'VIDEO',
|
||||||
});
|
numFaces: 1
|
||||||
|
|
||||||
video.addEventListener('loadeddata', () => {
|
|
||||||
cameraInput.active = true;
|
|
||||||
startDetectionLoop();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
cameraInput.active = true;
|
||||||
return video;
|
return video;
|
||||||
}
|
}
|
||||||
|
|
||||||
function startDetectionLoop() {
|
export function updateCameraControl() {
|
||||||
const detect = (now) => {
|
const now = performance.now();
|
||||||
const result = faceDetector.detectForVideo(video, now);
|
|
||||||
|
|
||||||
if (result.detections.length > 0) {
|
// Limit detection frequency to improve performance
|
||||||
const box = result.detections[0].boundingBox;
|
if (now - lastTime < DETECTION_INTERVAL)
|
||||||
// Calculate raw center point (0 to 1)
|
return;
|
||||||
const rawX = (box.originX + box.width / 2) / 160;
|
lastTime = now;
|
||||||
|
|
||||||
cameraInput.x = rawX;
|
|
||||||
|
|
||||||
// INSTANT LOGIC (No smoothing)
|
if (!faceLandmarker || !video || video.readyState < 2)
|
||||||
if (rawX < LEFT_THRESHOLD) {
|
return;
|
||||||
updateZones(false, true, 'right'); // Mirrored: Face on left of cam = move right
|
|
||||||
} else if (rawX > RIGHT_THRESHOLD) {
|
|
||||||
updateZones(true, false, 'left'); // Mirrored: Face on right of cam = move left
|
|
||||||
} else {
|
|
||||||
updateZones(false, false, 'center');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
updateZones(false, false, 'center');
|
|
||||||
}
|
|
||||||
|
|
||||||
video.requestVideoFrameCallback(detect);
|
const result = faceLandmarker.detectForVideo(video, now);
|
||||||
};
|
|
||||||
|
|
||||||
video.requestVideoFrameCallback(detect);
|
// If no face is detected, reset to center
|
||||||
}
|
if (!result.faceLandmarks.length) {
|
||||||
|
cameraInput.left = false;
|
||||||
|
cameraInput.right = false;
|
||||||
|
cameraInput.zone = 'center';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
function updateZones(l, r, z) {
|
// look at the nose and determine zone
|
||||||
cameraInput.left = l;
|
const x = result.faceLandmarks[0][1].x;
|
||||||
cameraInput.right = r;
|
if (x < 0.4) {
|
||||||
cameraInput.zone = z;
|
cameraInput.left = false;
|
||||||
|
cameraInput.right = true;
|
||||||
|
cameraInput.zone = 'right';
|
||||||
|
} else if (x > 0.6) {
|
||||||
|
cameraInput.left = true;
|
||||||
|
cameraInput.right = false;
|
||||||
|
cameraInput.zone = 'left';
|
||||||
|
} else {
|
||||||
|
cameraInput.left = false;
|
||||||
|
cameraInput.right = false;
|
||||||
|
cameraInput.zone = 'center';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -16,4 +16,4 @@ export const PLAT_TYPE = {
|
|||||||
MOVING: 'moving',
|
MOVING: 'moving',
|
||||||
SPRING: 'spring',
|
SPRING: 'spring',
|
||||||
ONE_TIME: 'one-time'
|
ONE_TIME: 'one-time'
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user