Obter posição facial com viseme

Nota

Para explorar as localidades suportadas para viseme ID e formas de mistura, consulte a lista de todas as localidades suportadas. SVG (Scalable Vetor Graphics) só é suportado para a en-US localidade.

Um visema é a descrição visual de um fonema na linguagem falada. Define a posição do rosto e da boca enquanto uma pessoa está falando. Cada viseme representa as principais poses faciais para um conjunto específico de fonemas.

Você pode usar visemes para controlar o movimento de modelos de avatar 2D e 3D, para que as posições faciais estejam melhor alinhadas com a fala sintética. Por exemplo, pode:

  • Crie um assistente de voz virtual animado para quiosques inteligentes, criando serviços integrados multimodo para os seus clientes.
  • Crie transmissões de notícias imersivas e melhore as experiências do público com movimentos naturais do rosto e da boca.
  • Gere avatares de jogos mais interativos e personagens de desenhos animados que podem falar com conteúdo dinâmico.
  • Crie vídeos de ensino de línguas mais eficazes que ajudem os alunos de línguas a compreender o comportamento oral de cada palavra e fonema.
  • Pessoas com deficiência auditiva também podem captar sons visualmente e conteúdo de fala "lip-read" que mostra visemes em um rosto animado.

Para obter mais informações sobre visemes, veja este vídeo introdutório.

Fluxo de trabalho geral de produção de viseme com fala

Neural Text to speech (Neural TTS) transforma texto de entrada ou SSML (Speech Synthesis Markup Language) em fala sintetizada realista. A saída de áudio de fala pode ser acompanhada por viseme ID, SVG (Scalable Vetor Graphics) ou formas de mistura. Usando um mecanismo de renderização 2D ou 3D, você pode usar esses eventos viseme para animar seu avatar.

O fluxo de trabalho geral do viseme é representado no seguinte fluxograma:

Diagram of the overall workflow of viseme.

Viseme ID

Viseme ID refere-se a um número inteiro que especifica um viseme. Oferecemos 22 visemes diferentes, cada um representando a posição da boca para um conjunto específico de fonemas. Não há correspondência um-para-um entre visemes e fonemas. Muitas vezes, vários fonemas correspondem a um único viseme, porque eles pareciam o mesmo no rosto do alto-falante quando são produzidos, como s e z. Para obter informações mais específicas, consulte a tabela para mapear fonemas para IDs viseme.

A saída de áudio de fala pode ser acompanhada por IDs viseme e Audio offset. O Audio offset indica o carimbo de data/hora de deslocamento que representa a hora de início de cada viseme, em ticks (100 nanossegundos).

Mapear fonemas para visemes

Visemes variam de acordo com o idioma e a localidade. Cada localidade tem um conjunto de visemes que correspondem aos seus fonemas específicos. A documentação dos alfabetos fonéticos SSML mapeia os IDs viseme para os fonemas correspondentes do Alfabeto Fonético Internacional (IPA). A tabela nesta seção mostra uma relação de mapeamento entre IDs de viseme e posições de boca, listando fonemas IPA típicos para cada ID de viseme.

Viseme ID IPA Posição da boca
0 Silêncio The mouth position when viseme ID is 0
5 æ, , əʌ The mouth position when viseme ID is 1
2 ɑ The mouth position when viseme ID is 2
3 ɔ The mouth position when viseme ID is 3
4 ɛ, ʊ The mouth position when viseme ID is 4
5 ɝ The mouth position when viseme ID is 5
6 j, , iɪ The mouth position when viseme ID is 6
7 w, u The mouth position when viseme ID is 7
8 o The mouth position when viseme ID is 8
9 The mouth position when viseme ID is 9
10 ɔɪ The mouth position when viseme ID is 10
11 The mouth position when viseme ID is 11
12 h The mouth position when viseme ID is 12
13 ɹ The mouth position when viseme ID is 13
14 l The mouth position when viseme ID is 14
15 s, z The mouth position when viseme ID is 15
16 ʃ, , , ʒ The mouth position when viseme ID is 16
17 ð The mouth position when viseme ID is 17
18 f, v The mouth position when viseme ID is 18
19 d, , , tnθ The mouth position when viseme ID is 19
20 k, , gŋ The mouth position when viseme ID is 20
21 p, , bm The mouth position when viseme ID is 21

Animação 2D SVG

Para caracteres 2D, você pode criar um personagem que se adapte ao seu cenário e usar gráficos vetoriais escaláveis (SVG) para cada ID de viseme para obter uma posição de rosto baseada no tempo.

Com tags temporais que são fornecidas em um evento viseme, esses SVGs bem projetados são processados com modificações de suavização e fornecem animação robusta aos usuários. Por exemplo, a ilustração a seguir mostra um caractere vermelho projetado para a aprendizagem de idiomas.

Screenshot showing a 2D rendering example of four red-lipped mouths, each representing a different viseme ID that corresponds to a phoneme.

Animação de formas de mistura 3D

Você pode usar formas de mesclagem para impulsionar os movimentos faciais de um personagem 3D que você criou.

A cadeia de caracteres JSON de formas de mesclagem é representada como uma matriz de 2 dimensões. Cada linha representa um quadro. Cada quadro (em 60 FPS) contém uma matriz de 55 posições faciais.

Obter eventos viseme com o SDK de Fala

Para obter viseme com seu discurso sintetizado, inscreva-se no VisemeReceived evento no SDK de fala.

Nota

Para solicitar a saída SVG ou misturar formas, você deve usar o mstts:viseme elemento no SSML. Para obter detalhes, consulte como usar o elemento viseme no SSML.

O trecho a seguir mostra como se inscrever no evento viseme:

using (var synthesizer = new SpeechSynthesizer(speechConfig, audioConfig))
{
    // Subscribes to viseme received event
    synthesizer.VisemeReceived += (s, e) =>
    {
        Console.WriteLine($"Viseme event received. Audio offset: " +
            $"{e.AudioOffset / 10000}ms, viseme id: {e.VisemeId}.");

        // `Animation` is an xml string for SVG or a json string for blend shapes
        var animation = e.Animation;
    };

    // If VisemeID is the only thing you want, you can also use `SpeakTextAsync()`
    var result = await synthesizer.SpeakSsmlAsync(ssml);
}

auto synthesizer = SpeechSynthesizer::FromConfig(speechConfig, audioConfig);

// Subscribes to viseme received event
synthesizer->VisemeReceived += [](const SpeechSynthesisVisemeEventArgs& e)
{
    cout << "viseme event received. "
        // The unit of e.AudioOffset is tick (1 tick = 100 nanoseconds), divide by 10,000 to convert to milliseconds.
        << "Audio offset: " << e.AudioOffset / 10000 << "ms, "
        << "viseme id: " << e.VisemeId << "." << endl;

    // `Animation` is an xml string for SVG or a json string for blend shapes
    auto animation = e.Animation;
};

// If VisemeID is the only thing you want, you can also use `SpeakTextAsync()`
auto result = synthesizer->SpeakSsmlAsync(ssml).get();
SpeechSynthesizer synthesizer = new SpeechSynthesizer(speechConfig, audioConfig);

// Subscribes to viseme received event
synthesizer.VisemeReceived.addEventListener((o, e) -> {
    // The unit of e.AudioOffset is tick (1 tick = 100 nanoseconds), divide by 10,000 to convert to milliseconds.
    System.out.print("Viseme event received. Audio offset: " + e.getAudioOffset() / 10000 + "ms, ");
    System.out.println("viseme id: " + e.getVisemeId() + ".");

    // `Animation` is an xml string for SVG or a json string for blend shapes
    String animation = e.getAnimation();
});

// If VisemeID is the only thing you want, you can also use `SpeakTextAsync()`
SpeechSynthesisResult result = synthesizer.SpeakSsmlAsync(ssml).get();
speech_synthesizer = speechsdk.SpeechSynthesizer(speech_config=speech_config, audio_config=audio_config)

def viseme_cb(evt):
    print("Viseme event received: audio offset: {}ms, viseme id: {}.".format(
        evt.audio_offset / 10000, evt.viseme_id))

    # `Animation` is an xml string for SVG or a json string for blend shapes
    animation = evt.animation

# Subscribes to viseme received event
speech_synthesizer.viseme_received.connect(viseme_cb)

# If VisemeID is the only thing you want, you can also use `speak_text_async()`
result = speech_synthesizer.speak_ssml_async(ssml).get()
var synthesizer = new SpeechSDK.SpeechSynthesizer(speechConfig, audioConfig);

// Subscribes to viseme received event
synthesizer.visemeReceived = function (s, e) {
    window.console.log("(Viseme), Audio offset: " + e.audioOffset / 10000 + "ms. Viseme ID: " + e.visemeId);

    // `Animation` is an xml string for SVG or a json string for blend shapes
    var animation = e.animation;
}

// If VisemeID is the only thing you want, you can also use `speakTextAsync()`
synthesizer.speakSsmlAsync(ssml);
SPXSpeechSynthesizer *synthesizer =
    [[SPXSpeechSynthesizer alloc] initWithSpeechConfiguration:speechConfig
                                           audioConfiguration:audioConfig];

// Subscribes to viseme received event
[synthesizer addVisemeReceivedEventHandler: ^ (SPXSpeechSynthesizer *synthesizer, SPXSpeechSynthesisVisemeEventArgs *eventArgs) {
    NSLog(@"Viseme event received. Audio offset: %fms, viseme id: %lu.", eventArgs.audioOffset/10000., eventArgs.visemeId);

    // `Animation` is an xml string for SVG or a json string for blend shapes
    NSString *animation = eventArgs.Animation;
}];

// If VisemeID is the only thing you want, you can also use `SpeakText`
[synthesizer speakSsml:ssml];

Aqui está um exemplo da saída viseme.

(Viseme), Viseme ID: 1, Audio offset: 200ms.

(Viseme), Viseme ID: 5, Audio offset: 850ms.

……

(Viseme), Viseme ID: 13, Audio offset: 2350ms.

Depois de obter a saída viseme, você pode usar esses eventos para direcionar a animação de caracteres. Você pode construir seus próprios personagens e animá-los automaticamente.

Próximos passos