Mendapatkan posisi wajah dengan viseme

Catatan

Untuk menjelajahi lokal yang didukung untuk ID viseme dan bentuk campuran, lihat daftar semua lokal yang didukung. Scalable Vector Graphics (SVG) hanya didukung untuk en-US lokal.

Viseme adalah deskripsi visual dari sebuah fonem dalam bahasa lisan. Ini menentukan posisi wajah dan mulut ketika seseorang berbicara. Setiap viseme menggambarkan pose kunci wajah untuk seperangkat fonem tertentu.

Anda dapat menggunakan viseme untuk mengontrol pergerakan model avatar 2D dan 3D, sehingga posisi wajah sesuai dengan ucapan sintetis. Misalnya, Anda dapat:

  • Membuat asisten suara virtual animasi untuk kios cerdas, membangun layanan terintegrasi multi-mode untuk pelanggan Anda.
  • Membangun siaran berita imersif dan menyempurnakan pengalaman audiens dengan gerakan wajah dan mulut yang alami.
  • Menghasilkan lebih banyak avatar game interaktif dan karakter kartun yang dapat berbicara dengan konten dinamis.
  • Buat video pengajaran bahasa yang lebih efektif yang membantu pelajar bahasa untuk memahami perilaku mulut dari setiap kata dan fonem.
  • Orang dengan gangguan pendengaran juga dapat mendengar suara secara visual dan konten ucapan "membaca bibir" yang menunjukkan viseme pada wajah animasi.

Untuk informasi selengkapnya tentang viseme, lihat video pengantar ini.

Alur kerja keseluruhan memproduksi viseme dengan ucapan

Teks Neural ke ucapan (Neural TTS) mengubah teks input atau SSML (Speech Synthesis Markup Language) menjadi ucapan yang disintesis seperti hidup. Output audio ucapan dapat disertai dengan ID viseme, Scalable Vector Graphics (SVG), atau bentuk campuran. Menggunakan mesin rendering 2D atau 3D, Anda dapat menggunakan peristiwa viseme ini untuk menghidupkan avatar Anda.

Alur kerja keseluruhan viseme digambarkan dalam diagram alur berikut:

Diagram of the overall workflow of viseme.

ID Visem

ID Viseme mengacu pada bilangan bulat yang menentukan viseme. Kami menawarkan 22 viseme yang berbeda, masing-masingnya menggambarkan posisi mulut untuk satu set fonem tertentu. Tidak ada korespondensi satu-ke-satu antara viseme dan fonem. Seringkali, beberapa fonem sesuai dengan satu viseme, karena terlihat sama pada wajah speaker ketika diproduksi, seperti s dan z. Untuk informasi lebih spesifik, lihat tabel untuk memetakan fonem untuk ID viseme.

Output audio ucapan dapat disertai dengan ID viseme dan Audio offset. Audio offset menunjukkan tanda waktu offset yang mewakili waktu mulai setiap viseme, dalam tanda waktu (100 nanodetik).

Petakan fonem ke visem

Viseme bervariasi menurut bahasa dan lokal. Setiap lokal memiliki serangkaian visem yang sesuai dengan fonem spesifiknya. Dokumentasi alfabet fonetik SSML memetakan ID viseme ke fonem Alfabet Fonetik Internasional (IPA) yang sesuai. Tabel di bagian ini menunjukkan hubungan pemetaan antara ID viseme dan posisi mulut, mencantumkan fonem IPA umum untuk setiap ID viseme.

ID Visem IPA Posisi mulut
0 Hening 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, , 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

Animasi SVG 2D

Untuk karakter 2D, Anda dapat merancang karakter yang sesuai dengan skenario Anda dan menggunakan Scalable Vector Graphics (SVG) untuk setiap ID viseme untuk mendapatkan posisi wajah berbasis waktu.

Dengan tag temporal yang disediakan dalam acara viseme, SVG yang dirancang dengan baik ini diproses dengan modifikasi smoothing, dan memberikan animasi yang kuat kepada pengguna. Misalnya, ilustrasi berikut menunjukkan karakter berlip merah yang dirancang untuk pembelajaran bahasa.

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

Animasi bentuk campuran 3D

Anda dapat menggunakan bentuk campuran untuk mendorong gerakan wajah dari karakter 3D yang Anda rancang.

String JSON bentuk campuran direpresentasikan sebagai matriks 2 dimensi. Setiap baris merepresentasikan bingkai. Setiap bingkai (dalam 60 FPS) berisi array 55 posisi wajah.

Dapatkan peristiwa viseme dengan Speech SDK

Untuk mendapatkan viseme dengan ucapan yang disintesis, berlangganan ke peristiwa VisemeReceived di SDK Ucapan.

Catatan

Untuk meminta output SVG atau bentuk campuran, Anda harus menggunakan elemen mstts:viseme di SSML. Untuk detailnya, lihat cara menggunakan elemen viseme di SSML.

Cuplikan berikut menunjukkan cara berlangganan peristiwa 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];

Berikut adalah contoh output viseme.

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

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

……

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

Setelah mendapatkan output viseme, Anda dapat menggunakan peristiwa ini untuk menggerakkan animasi karakter. Anda dapat membangun karakter Anda sendiri dan secara otomatis menganimasikannya.

Langkah berikutnya