feat: criado repositório jhonny-editor no Gitea
criado repositório jhonny-editor no Gitea adicionado script admin/deploy.command com commit automático atualizado admin/DEV-NOTES.md com template limpo Resumo: - 48 arquivos alterados - 26 novos - 19 modificados - 3 removidos 22 files changed, 658 insertions(+), 568 deletions(-) Arquivos: - AGENTS.md - admin/DEV-NOTES.md - admin/OpenCut.command - admin/commit.command - admin/deploy.command - admin/update.command - code/cep-plugin/index.html - code/cep-plugin/main.js - code/cep-plugin/styles.css - code/engine/ARQUITETURA.md - code/engine/arquitetura/README.md - code/engine/gerar_relatorio_timeline.py - code/engine/integracoes/apple_speech/apple_speech_transcriber.swift - code/engine/integracoes/apple_speech/provider_de_transcricao_apple.py - code/engine/integracoes/midia/__init__.py - code/engine/integracoes/whisper/provider_de_transcricao_local.py - code/engine/scanner/__init__.py - code/engine/scanner/coordenacao/__init__.py - code/engine/scanner/descoberta/__init__.py - code/engine/scanner/modelos.py - code/engine/scanner/transcricao_da_timeline.py - code/src/tools/discovery.ts - :memory:.ses - admin/Jhonny.command - admin/inativos/OpenCut.command - admin/inativos/update.command - code/docs/glossario-analise-emocional.md - code/docs/levantamento-apple-vision-e-apple-intelligence.md - code/docs/levantamento-ferramentas-analise-visual-local.md - code/engine/arquitetura/biblioteca-inteligente-de-videos.md - code/engine/executar_scanner.py - code/engine/integracoes/huggingface/ - code/engine/integracoes/midia/extracao_de_metadados.py - code/engine/integracoes/visual/ - code/engine/requirements-visual.txt - code/engine/scanner/configuracao_visual.py - code/engine/scanner/descoberta/descoberta_de_arquivos.py - code/engine/scanner/metadados.py - code/engine/scanner/relatorio_visual.py - code/engine/scanner/retakes/ - code/engine/scanner/visual.py - code/engine/testes/test_analise_visual_local.py - code/engine/testes/test_arquivos_e_metadados.py - code/engine/testes/test_provider_de_transcricao_apple.py - code/relatorios/analise-brools/ - code/relatorios/analise-visual/ - code/relatorios/audio/arquivos/ - code/relatorios/transcricao-timeline.md
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
from .provider_de_emocao_local import ProviderDeEmocaoHuggingFace
|
||||
from .provider_de_diarizacao_local import ProviderDeDiarizacaoHuggingFace
|
||||
|
||||
__all__ = ["ProviderDeDiarizacaoHuggingFace", "ProviderDeEmocaoHuggingFace"]
|
||||
@@ -0,0 +1,21 @@
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class ProviderDeDiarizacaoHuggingFace:
|
||||
"""Identifica intervalos de falas por participante em áudio local."""
|
||||
|
||||
def __init__(self, modelo: str) -> None:
|
||||
from pyannote.audio import Pipeline
|
||||
self.pipeline = Pipeline.from_pretrained(modelo)
|
||||
|
||||
def analisar(self, arquivo: str | Path) -> list[tuple[float, float, str]]:
|
||||
import soundfile as sf
|
||||
import torch
|
||||
audio, taxa = sf.read(str(arquivo), dtype="float32")
|
||||
if getattr(audio, "ndim", 1) > 1:
|
||||
audio = audio.mean(axis=1)
|
||||
saida = self.pipeline({"waveform": torch.from_numpy(audio).unsqueeze(0),
|
||||
"sample_rate": taxa})
|
||||
diarizacao = getattr(saida, "exclusive_speaker_diarization", saida)
|
||||
return [(float(turno.start), float(turno.end), str(falante))
|
||||
for turno, _, falante in diarizacao.itertracks(yield_label=True)]
|
||||
@@ -0,0 +1,35 @@
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
class ProviderDeEmocaoHuggingFace:
|
||||
"""Classifica a emoção de uma fala localmente com Transformers."""
|
||||
|
||||
def __init__(self, modelo: str = "superb/wav2vec2-base-superb-er") -> None:
|
||||
from transformers import AutoFeatureExtractor, AutoModelForAudioClassification
|
||||
self.modelo = modelo
|
||||
self.processador = AutoFeatureExtractor.from_pretrained(modelo, local_files_only=True)
|
||||
self.classificador = AutoModelForAudioClassification.from_pretrained(
|
||||
modelo, local_files_only=True
|
||||
)
|
||||
self.classificador.eval()
|
||||
|
||||
def analisar(self, arquivo: str | Path, inicio: float, fim: float) -> dict[str, Any]:
|
||||
import soundfile as sf
|
||||
import torch
|
||||
audio, taxa = sf.read(str(arquivo), start=max(0, int(inicio * 16000)),
|
||||
stop=max(0, int(fim * 16000)), dtype="float32")
|
||||
if getattr(audio, "ndim", 1) > 1:
|
||||
audio = audio.mean(axis=1)
|
||||
entradas = self.processador(audio, sampling_rate=taxa, return_tensors="pt")
|
||||
with torch.no_grad():
|
||||
logits = self.classificador(**entradas).logits
|
||||
probabilidades = torch.softmax(logits[0], dim=-1)
|
||||
indice = int(torch.argmax(probabilidades))
|
||||
rotulo = self.classificador.config.id2label[indice]
|
||||
voz_aparente = {"non-neutral-female": "feminina",
|
||||
"non-neutral-male": "masculina"}.get(rotulo)
|
||||
return {"emocao": self.classificador.config.id2label[indice],
|
||||
"confianca": float(probabilidades[indice]),
|
||||
"voz_aparente": voz_aparente,
|
||||
"confianca_voz": float(probabilidades[indice]) if voz_aparente else None}
|
||||
Reference in New Issue
Block a user