optimazed camera controll again

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
2026-04-29 23:09:07 +09:00
parent 708fa4e9ab
commit 73130dd8ce
4 changed files with 52 additions and 54 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -7,7 +7,7 @@
<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='/build/bundle.css'>

View File

@@ -1,91 +1,89 @@
import { FaceDetector, FilesetResolver } from '@mediapipe/tasks-vision';
import { FaceLandmarker, FilesetResolver } from '@mediapipe/tasks-vision';
export const cameraInput = {
left: false,
right: false,
active: false,
zone: 'center',
x: 0.5
zone: 'center'
};
let video;
let faceDetector;
let faceLandmarker;
let lastTime = 0;
// 🔥 NARROW CENTER ZONE:
// 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;
const DETECTION_INTERVAL = 60;
export async function initCameraControl() {
if (cameraInput.active) return video;
if (cameraInput.active)
return video;
video = document.createElement('video');
video.autoplay = true;
video.playsInline = true;
video.muted = true;
// Set low resolution and frame rate for better performance
const stream = await navigator.mediaDevices.getUserMedia({
video: {
width: 160,
height: 120,
frameRate: { ideal: 20 } // Slightly higher for faster reaction
video: {
width: { ideal: 160 },
height: { ideal: 120 },
frameRate: { ideal: 30 }
}
});
video.srcObject = stream;
//load models for face landmark detection
const vision = await FilesetResolver.forVisionTasks(
'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision/wasm'
);
faceDetector = await FaceDetector.createFromOptions(vision, {
faceLandmarker = await FaceLandmarker.createFromOptions(vision, {
baseOptions: {
modelAssetPath: `https://storage.googleapis.com/mediapipe-models/face_detector/blaze_face_short_range/float16/1/blaze_face_short_range.task`,
delegate: 'GPU'
modelAssetPath:
'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task',
},
runningMode: 'VIDEO'
});
video.addEventListener('loadeddata', () => {
cameraInput.active = true;
startDetectionLoop();
runningMode: 'VIDEO',
numFaces: 1
});
cameraInput.active = true;
return video;
}
function startDetectionLoop() {
const detect = (now) => {
const result = faceDetector.detectForVideo(video, now);
export function updateCameraControl() {
const now = performance.now();
if (result.detections.length > 0) {
const box = result.detections[0].boundingBox;
// Calculate raw center point (0 to 1)
const rawX = (box.originX + box.width / 2) / 160;
cameraInput.x = rawX;
// Limit detection frequency to improve performance
if (now - lastTime < DETECTION_INTERVAL)
return;
lastTime = now;
// INSTANT LOGIC (No smoothing)
if (rawX < LEFT_THRESHOLD) {
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');
}
if (!faceLandmarker || !video || video.readyState < 2)
return;
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) {
cameraInput.left = l;
cameraInput.right = r;
cameraInput.zone = z;
// look at the nose and determine zone
const x = result.faceLandmarks[0][1].x;
if (x < 0.4) {
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';
}
}

View File

@@ -16,4 +16,4 @@ export const PLAT_TYPE = {
MOVING: 'moving',
SPRING: 'spring',
ONE_TIME: 'one-time'
};
};