feat(vida): jeito de gente e console com o porquê de cada decisão
Console (console.py + tela Console no painel):
- cada parte registra o que fez e o motivo: modo vida (com necessidades
e humor), sentidos, celular (ordens e o que o rosto fez), IA local,
aprendizado e ideias do dia; últimas 400 linhas, ao vivo pelo SSE;
- tela com o "agora" (humor, necessidades, o que está fazendo, próxima
decisão, última decisão, jeito), filtros, detalhes, pausa e busca.
Jeito de gente:
- saudade: a festa cresce com o tempo fora (aceno, "Oi! Você voltou!",
"Senti sua falta!"); bom dia lembrando de ontem;
- pedidos com motivo ("Brinca comigo?", "Me faz um carinho?", mostrar a
figurinha nova), no máximo 1 a cada 20 min;
- larga atividade no meio quando fica chato; personalidade (curiosidade,
brincadeira, timidez) que pesa nos gestos e amadurece com o convívio;
- no rosto: acordar grogue, sonhar com algo do dia (balão no motor),
hesitar antes de gestos, errar na atividade e ficar sem graça, olhar
nos olhos e desviar, sorrir de volta (FaceLandmarker no rastreador do
painel) e reagir à luz (sensor de luz no app 0.10.0);
- ordem "dizer" com legenda e voz de robô.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+18
-1
@@ -1,5 +1,7 @@
|
||||
'use strict';
|
||||
let face = null, hands = null, vision = null;
|
||||
// expressão (sorriso) para o contágio emocional: modelo opcional, roda 1 quadro a cada 4
|
||||
let expressao = null, semExpressao = false, quadro = 0, sorriso = null;
|
||||
let lastTime = 0;
|
||||
async function prepare(c) {
|
||||
if (!vision) {
|
||||
@@ -9,6 +11,13 @@ async function prepare(c) {
|
||||
}
|
||||
if (c.rosto && !face) face = await vision.module.FaceDetector.createFromOptions(vision.files, {
|
||||
baseOptions:{modelAssetPath:'/vendor/mediapipe/face.tflite',delegate:'CPU'},runningMode:'VIDEO',minDetectionConfidence:.6});
|
||||
if (c.rosto && !expressao && !semExpressao) {
|
||||
try {
|
||||
expressao = await vision.module.FaceLandmarker.createFromOptions(vision.files, {
|
||||
baseOptions:{modelAssetPath:'/vendor/mediapipe/expressao.task',delegate:'CPU'},runningMode:'VIDEO',numFaces:1,
|
||||
outputFaceBlendshapes:true});
|
||||
} catch (e) { semExpressao = true; } // sem o modelo: segue só com a posição do rosto
|
||||
}
|
||||
if (c.maos && !hands) hands = await vision.module.HandLandmarker.createFromOptions(vision.files, {
|
||||
baseOptions:{modelAssetPath:'/vendor/mediapipe/hands.task',delegate:'CPU'},runningMode:'VIDEO',numHands:2,
|
||||
minHandDetectionConfidence:.6,minHandPresenceConfidence:.6,minTrackingConfidence:.5});
|
||||
@@ -25,9 +34,17 @@ self.onmessage = async ({data}) => {
|
||||
w:d.boundingBox.width/bitmap.width,h:d.boundingBox.height/bitmap.height,
|
||||
score:d.categories[0]?.score || 0,pontos:d.keypoints.map(p=>({x:p.x,y:p.y}))
|
||||
})) : [];
|
||||
if (!faces.length) sorriso = null;
|
||||
else if (expressao && quadro++ % 4 === 0) {
|
||||
const b = (expressao.detectForVideo(bitmap,stamp).faceBlendshapes || [])[0];
|
||||
if (b) {
|
||||
const s = {}; for (const c of b.categories) s[c.categoryName] = c.score;
|
||||
sorriso = ((s.mouthSmileLeft || 0) + (s.mouthSmileRight || 0)) / 2;
|
||||
}
|
||||
}
|
||||
const result = data.config.maos ? hands.detectForVideo(bitmap,stamp) : {landmarks:[]};
|
||||
self.postMessage({tipo:'resultado',faces,hands:result.landmarks.map(h=>h.map(p=>({x:p.x,y:p.y}))),
|
||||
ms:performance.now()-began,timestamp:data.timestamp});
|
||||
sorriso,ms:performance.now()-began,timestamp:data.timestamp});
|
||||
} catch(e) { self.postMessage({tipo:'erro',erro:e.message || String(e)}); }
|
||||
finally { if (bitmap) bitmap.close(); }
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user