feat(pessoas): reconhecimento de quem chega, só na placa
- reconhecimento.py: cada foto de evento passa pelo YuNet (acha rostos)
e pelo SFace (impressão de 128 números) no OpenCV da placa (~0,3 s por
foto); compara com as amostras marcadas (cosseno >= 0,40). Rostos sem
nome ficam agrupados por semelhança para marcar de uma vez; marcar uma
pessoa reconhece também os rostos antigos parecidos. Procura nas fotos
guardadas, renomear, apagar (apaga recortes e impressões), sem nome
somem em 7 dias, liga/desliga.
- modo vida: espera até 8 s a foto para cumprimentar pelo nome ("Ana!
Senti sua falta!"), saudade de cada pessoa, "Oi, Bia!" para quem chega
com alguém já por perto, afinidade com quem dá carinho logo depois.
- painel: tela Pessoas e tópico no guia; console mostra "Reconheceu Ana".
- sistema/reconhecimento/instalar.sh: python3-opencv e os modelos do
opencv_zoo.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -124,6 +124,8 @@ GESTOS_TRACO = {'brincadeira': {'rir', 'comemorar', 'alegre', 'musica', 'piscadi
|
||||
AUSENCIA_ACENO, AUSENCIA_OI, AUSENCIA_SAUDADE = 10 * 60, 60 * 60, 4 * 3600
|
||||
INTERVALO_PEDIDO = 20 * 60 # no mínimo 20 min entre um pedido ("brinca comigo?") e outro
|
||||
DESISTE = 0.12 # chance de largar uma atividade no meio porque ficou chato
|
||||
ESPERA_NOME = 8 # s esperando o reconhecimento antes de cumprimentar sem o nome
|
||||
OI_DE_NOVO = 10 * 60 # reconheceu alguém que não via há esse tempo: "Oi, Ana!"
|
||||
GESTOS_VIDA = {g for atos in ATOS.values() for g, _, _ in atos}
|
||||
|
||||
|
||||
@@ -172,6 +174,11 @@ class Vida:
|
||||
self.pediu_em = 0.0
|
||||
self.desistencia = None # (quando, o que) se largou a atividade no meio
|
||||
self.figurinha_mostrada = 0.0
|
||||
# reconhecimento de pessoas (reconhecimento.py): o main liga
|
||||
self.reconhece = lambda: False # True: vale esperar a foto para cumprimentar pelo nome
|
||||
self.gostou_de = lambda pessoa, quanto=1.0: None
|
||||
self.reencontro_pendente = None # (desde, ausência) esperando saber quem chegou
|
||||
self.ultimo_reconhecido = None # (id, nome, quando)
|
||||
|
||||
agora = time.time()
|
||||
self.config = dict(PADRAO)
|
||||
@@ -359,6 +366,9 @@ class Vida:
|
||||
if self.aprendizado:
|
||||
self.aprendizado.resposta(tipo, agora)
|
||||
self.amadurecer(tipo)
|
||||
rec = self.ultimo_reconhecido
|
||||
if rec and agora - rec[2] < 120:
|
||||
self.gostou_de(rec[0], 1.0 if tipo in ('carinho', 'brincar', 'conversa') else 0.5)
|
||||
self.console('sentidos', CUIDADOS.get(tipo) or NOME_ESTIMULO.get(tipo, tipo),
|
||||
', '.join('%s %+d' % (k, v) for k, v in efeito.items()),
|
||||
'detalhe' if tipo in ('som', 'movimento') else 'info', agora)
|
||||
@@ -453,10 +463,12 @@ class Vida:
|
||||
def tempo(seg):
|
||||
return '%d min' % round(seg / 60) if seg < 3600 else '%dh%02d' % (seg // 3600, seg % 3600 // 60)
|
||||
|
||||
def saudacao(self, agora):
|
||||
def saudacao(self, agora, nome=None):
|
||||
"""Bom dia com uma lembrança de ontem (do que ele viveu, sem IA)."""
|
||||
hora = time.localtime(agora).tm_hour
|
||||
prefixo = 'Bom dia' if 5 <= hora < 12 else 'Boa tarde' if hora < 18 else 'Boa noite'
|
||||
if nome:
|
||||
prefixo += ', ' + nome
|
||||
fatos = []
|
||||
ontem = None
|
||||
try:
|
||||
@@ -478,29 +490,35 @@ class Vida:
|
||||
fatos.append('Acordei cheio de energia!' if self.necessidades['energia'] >= 80 else 'Que bom te ver!')
|
||||
return '%s! %s' % (prefixo, self.acaso.choice(fatos))
|
||||
|
||||
def reencontro(self, agora, ausencia):
|
||||
def reencontro(self, agora, ausencia, nome=None, afinidade=0.0):
|
||||
"""Alguém apareceu: a reação depende de quanto tempo ficou fora (e de quem ele é)."""
|
||||
if self.dormindo() or self.madrugada(agora) or self.bloqueado():
|
||||
return None
|
||||
n, tim = self.necessidades, self.personalidade['timidez']
|
||||
dia = time.strftime('%Y-%m-%d', time.localtime(agora))
|
||||
motivo = 'ninguém por perto havia ' + self.tempo(ausencia)
|
||||
motivo = ('%s não aparecia havia ' % nome if nome else 'ninguém por perto havia ') + self.tempo(ausencia)
|
||||
querido = afinidade >= 20 # muito carinho dessa pessoa: festa com coração
|
||||
if self.saudou_dia != dia and time.localtime(agora).tm_hour >= 5 and ausencia >= AUSENCIA_ACENO:
|
||||
self.saudou_dia = dia
|
||||
frase = self.saudacao(agora)
|
||||
frase = self.saudacao(agora, nome)
|
||||
self.dizer(frase, 'timido' if tim > 0.65 else 'acenar')
|
||||
ganho, texto, motivo = 10, 'Deu bom dia: "%s"' % frase, 'primeiro encontro do dia; ' + motivo
|
||||
elif ausencia >= AUSENCIA_SAUDADE:
|
||||
self.dizer('Senti sua falta!', 'comemorar')
|
||||
self.dizer('%s! Senti sua falta!' % nome if nome else 'Senti sua falta!', 'comemorar')
|
||||
self.enfileirar('coracao')
|
||||
ganho, texto = 20, 'Fez festa: estava com saudade'
|
||||
ganho, texto = 20, 'Fez festa: estava com saudade' + (' de ' + nome if nome else '')
|
||||
n['alegria'] = _limitar(n['alegria'] + 10)
|
||||
elif ausencia >= AUSENCIA_OI:
|
||||
self.dizer('Oi! Você voltou!', 'timido' if tim > 0.65 else 'comemorar')
|
||||
ganho, texto = 10, 'Comemorou que alguém voltou'
|
||||
self.dizer('Oi, %s! Você voltou!' % nome if nome else 'Oi! Você voltou!', 'timido' if tim > 0.65 else 'comemorar')
|
||||
if querido:
|
||||
self.enfileirar('coracao')
|
||||
ganho, texto = 10, 'Comemorou que %s voltou' % (nome or 'alguém')
|
||||
elif ausencia >= AUSENCIA_ACENO:
|
||||
self.enfileirar('acenar')
|
||||
ganho, texto = 4, 'Acenou para quem voltou'
|
||||
if nome:
|
||||
self.dizer('Oi, %s!' % nome, 'acenar')
|
||||
else:
|
||||
self.enfileirar('acenar')
|
||||
ganho, texto = 4, 'Acenou para %s' % (nome or 'quem voltou')
|
||||
else:
|
||||
return None
|
||||
with self.lock:
|
||||
@@ -510,6 +528,21 @@ class Vida:
|
||||
self.avisar()
|
||||
return texto
|
||||
|
||||
def reconhecido(self, pessoa, confianca, agora=None):
|
||||
"""O reconhecimento disse quem está aí (vem da thread do reconhecimento)."""
|
||||
agora = agora or time.time()
|
||||
nome = pessoa['nome']
|
||||
self.ultimo_reconhecido = (pessoa['id'], nome, agora)
|
||||
antes = pessoa.get('visto_antes')
|
||||
pendente = self.reencontro_pendente
|
||||
if pendente: # chegou alguém e agora sabe quem é
|
||||
self.reencontro_pendente = None
|
||||
ausencia = agora - antes if antes else pendente[1]
|
||||
return self.reencontro(agora, ausencia, nome, pessoa.get('afinidade', 0))
|
||||
if antes and agora - antes > OI_DE_NOVO: # chegou outra pessoa com alguém já por perto
|
||||
return self.reencontro(agora, agora - antes, nome, pessoa.get('afinidade', 0))
|
||||
return None
|
||||
|
||||
def pedir_algo(self, agora):
|
||||
"""Iniciativa com motivo: pede o que está faltando (ou mostra o que fez)."""
|
||||
n = self.necessidades
|
||||
@@ -769,9 +802,16 @@ class Vida:
|
||||
perto = self.alguem_perto()
|
||||
if perto:
|
||||
if not self.perto_antes:
|
||||
self.reencontro(agora, agora - self.ultimo_perto)
|
||||
if self.reconhece():
|
||||
self.reencontro_pendente = (agora, agora - self.ultimo_perto) # espera a foto dizer quem é
|
||||
else:
|
||||
self.reencontro(agora, agora - self.ultimo_perto)
|
||||
self.ultimo_perto = agora
|
||||
self.perto_antes = perto
|
||||
pendente = self.reencontro_pendente
|
||||
if pendente and agora - pendente[0] >= ESPERA_NOME:
|
||||
self.reencontro_pendente = None
|
||||
self.reencontro(agora, pendente[1])
|
||||
if self.desistencia and agora >= self.desistencia[0]:
|
||||
_, oque = self.desistencia
|
||||
self.desistencia = None
|
||||
|
||||
Reference in New Issue
Block a user