Ottenere la posizione del viso con viseme

Nota

Per esplorare le impostazioni locali supportate per le forme viseme ID e blend, vedere l'elenco di tutte le impostazioni locali supportate. La grafica vettoriale scalabile (SVG) è supportata solo per le en-US impostazioni locali.

Un viseme è la descrizione visiva di un fonemo nella lingua parlata. Definisce la posizione del viso e della bocca mentre una persona sta parlando. Ogni viseme illustra le pose del viso chiave per un set specifico di fonemi.

Puoi usare visemes per controllare il movimento dei modelli avatar 2D e 3D, in modo che le posizioni del viso siano meglio allineate al parlato sintetico. È ad esempio possibile:

  • Creare un assistente vocale virtuale animato per chioschi intelligenti, creando servizi integrati in più modalità per i clienti.
  • Creare trasmissioni di notizie immersive e migliorare le esperienze del pubblico con movimenti naturali di viso e bocca.
  • Genera avatar e personaggi animati di gioco più interattivi che possono parlare con contenuti dinamici.
  • Rendere più efficaci i video di insegnamento linguistico che aiutano gli studenti a comprendere il comportamento della bocca di ogni parola e fonema.
  • Persone con problemi di udito possono anche raccogliere suoni visivamente e "lip-read" contenuti vocali che mostrano visemi su un viso animato.

Per altre informazioni sui visemes, vedere questo video introduttivo.

Flusso di lavoro complessivo di produzione di viseme con riconoscimento vocale

Neural Text to Speech (Neural TTS) trasforma il testo di input o SSML (Speech Synthesis Markup Language) in parlato sintetizzato in modo realistico. L'output audio vocale può essere accompagnato da viseme ID, SVG (Scalable Vector Graphics) o forme blend. Usando un motore di rendering 2D o 3D, puoi usare questi eventi viseme per animare il tuo avatar.

Il flusso di lavoro complessivo di viseme è illustrato nel diagramma di flusso seguente:

Diagram of the overall workflow of viseme.

Viseme ID

L'ID Viseme fa riferimento a un numero intero che specifica un viseme. Offriamo 22 visemi diversi, ognuno che rappresenta la posizione della bocca per un set specifico di fonemi. Non c'è corrispondenza uno-a-uno tra visemes e fonemi. Spesso, diversi fonemi corrispondono a un singolo viseme, perché hanno guardato lo stesso sul viso dell'altoparlante quando vengono prodotti, ad esempio s e z. Per informazioni più specifiche, vedere la tabella per il mapping dei fonemi agli ID viseme.

L'output audio vocale può essere accompagnato da ID viseme e Audio offset. Indica Audio offset il timestamp di offset che rappresenta l'ora di inizio di ogni viseme, in tick (100 nanosecondi).

Mappare i fonemi ai visemes

Visemes varia in base alla lingua e alle impostazioni locali. Ogni impostazione locale ha un set di visemes che corrispondono ai relativi fonemi specifici. La documentazione degli alfabeti fonetici SSML esegue il mapping degli ID viseme ai corrispondenti fonemi ipA (International Telefono tic Alphabet). La tabella in questa sezione mostra una relazione di mapping tra ID viseme e posizioni della bocca, elencando i tipici fonemi IPA per ogni ID viseme.

Viseme ID IPA Posizione bocca
0 Silenzio The mouth position when viseme ID is 0
1 æ, ə, ʌ 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, t, nθ The mouth position when viseme ID is 19
20 k, g, ŋ The mouth position when viseme ID is 20
21 p, b, m The mouth position when viseme ID is 21

Animazione SVG 2D

Per i caratteri 2D, è possibile progettare un carattere adatto allo scenario e usare La grafica vettoriale scalabile (SVG) per ogni ID viseme per ottenere una posizione del viso basata sul tempo.

Con i tag temporali forniti in un evento viseme, questi svg ben progettati vengono elaborati con modifiche di smoothing e forniscono animazioni affidabili agli utenti. Ad esempio, la figura seguente mostra un carattere rosso-riquadro progettato per l'apprendimento linguistico.

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

Animazione delle forme di fusione 3D

È possibile usare forme di fusione per guidare i movimenti facciali di un carattere 3D progettato.

La stringa JSON delle forme di fusione è rappresentata come matrice bidimensionale. Ogni riga rappresenta una cornice. Ogni frame (in 60 FPS) contiene una matrice di 55 posizioni facciali.

Ottenere eventi viseme con Speech SDK

Per ottenere viseme con il parlato sintetizzato, sottoscrivere l'evento VisemeReceived in Speech SDK.

Nota

Per richiedere l'output delle forme SVG o blend, è necessario usare l'elemento mstts:viseme in SSML. Per informazioni dettagliate, vedere come usare l'elemento viseme in SSML.

Il frammento di codice seguente illustra come sottoscrivere l'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];

Ecco un esempio dell'output viseme.

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

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

……

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

Dopo aver ottenuto l'output viseme, puoi usare questi eventi per guidare l'animazione dei caratteri. È possibile creare personaggi personalizzati e animarli automaticamente.

Passaggi successivi