Python >> Tutoriel Python >  >> Python

Comment transcrire des enregistrements vocaux en texte avec Python

Lorsque vous avez un enregistrement où une ou plusieurs personnes parlent, il est utile d'avoir un moyen très précis et automatisé d'extraire les mots prononcés dans le texte. Une fois que vous avez le texte, vous pouvez l'utiliser pour une analyse plus approfondie ou comme fonctionnalité d'accessibilité.

Dans ce didacticiel, nous utiliserons une interface de programmation d'application Web de synthèse vocale de haute précision appelée AssemblyAI pour extraire le texte d'un enregistrement MP3 (de nombreux autres formats sont également pris en charge).

Avec le code de ce didacticiel, vous pourrez prendre un fichier audio contenant de la parole comme cet exemple que j'ai enregistré et produire une transcription de texte très précise comme celle-ci :

An object relational mapper is a code library that automates the transfer of 
data stored in relational, databases into objects that are more commonly used
in application code or EMS are useful because they provide a high level 
abstraction upon a relational database that allows developers to write Python 
code instead of sequel to create read update and delete, data and schemas in 
their database. Developers can use the programming language. They are 
comfortable with to work with a database instead of writing SQL...

(the text goes on from here but I abbreviated it at this point)

Exigences du didacticiel

Tout au long de ce didacticiel, nous allons utiliser les dépendances suivantes, que nous allons installer dans un instant. Assurez-vous également que Python 3, de préférence 3.6 ou une version plus récente, est installé dans votre environnement :

Nous utiliserons les dépendances suivantes pour terminer ce didacticiel :

  • demande la version 2.24.0 pour envoyer des requêtes HTTP à l'API de synthèse vocale AssemblyAI
  • Un compte AssemblyAI, auquel vous pouvez vous inscrire pour obtenir une clé d'accès API gratuite ici

Tout le code de cet article de blog est disponible en open source sous la licence MIT sur GitHub sous le répertoire transcribe-speech-text-script du référentiel blog-code-examples. Utilisez le code source comme vous le souhaitez pour vos propres projets.

Configuration de l'environnement de développement

Changez dans le répertoire où vous gardez vos environnements virtuels Python.Je garde le mien dans un sous-répertoire nommé venvs dans le répertoire personnel de mon utilisateur. Créez un nouvel environnement virtuel pour ce projet à l'aide de la commande suivante.

python3 -m venv ~/venvs/pytranscribe

Activez le virtualenv avec le activate script shell :

source ~/venvs/pytranscribe/bin/activate

Une fois la commande ci-dessus exécutée, l'invite de commande changera de sorte que le nom de virtualenv soit ajouté au format d'invite de commande d'origine, donc si votre invite est simplement $ , il ressemblera maintenant à ceci :

(pytranscribe) $

N'oubliez pas que vous devez activer votre environnement virtuel dans chaque nouvelle fenêtre de terminal où vous souhaitez utiliser des dépendances dans l'environnement virtuel.

Nous pouvons maintenant installer le requests package dans le virtualenv activé mais autrement vide.

pip install requests==2.24.0

Recherchez une sortie similaire à la suivante pour confirmer que les packages appropriés ont été correctement installés à partir de PyPI.

(pytranscribe) $ pip install requests==2.24.0
Collecting requests==2.24.0
  Using cached https://files.pythonhosted.org/packages/45/1e/0c169c6a5381e241ba7404532c16a21d86ab872c9bed8bdcd4c423954103/requests-2.24.0-py2.py3-none-any.whl
Collecting certifi>=2017.4.17 (from requests==2.24.0)
  Using cached https://files.pythonhosted.org/packages/5e/c4/6c4fe722df5343c33226f0b4e0bb042e4dc13483228b4718baf286f86d87/certifi-2020.6.20-py2.py3-none-any.whl
Collecting urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 (from requests==2.24.0)
  Using cached https://files.pythonhosted.org/packages/9f/f0/a391d1463ebb1b233795cabfc0ef38d3db4442339de68f847026199e69d7/urllib3-1.25.10-py2.py3-none-any.whl
Collecting chardet<4,>=3.0.2 (from requests==2.24.0)
  Using cached https://files.pythonhosted.org/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/chardet-3.0.4-py2.py3-none-any.whl
Collecting idna<3,>=2.5 (from requests==2.24.0)
  Using cached https://files.pythonhosted.org/packages/a2/38/928ddce2273eaa564f6f50de919327bf3a00f091b5baba8dfa9460f3a8a8/idna-2.10-py2.py3-none-any.whl
Installing collected packages: certifi, urllib3, chardet, idna, requests
Successfully installed certifi-2020.6.20 chardet-3.0.4 idna-2.10 requests-2.24.0 urllib3-1.25.10

Toutes nos dépendances requises sont installées afin que nous puissions commencer à coder l'application.

Mise en ligne, lancement et transcription audio

Nous avons tout ce dont nous avons besoin pour commencer à créer notre application qui transcrira l'audio en texte. Nous allons compiler cette application en trois fichiers :

  1. upload_audio_file.py :télécharge votre fichier audio dans un endroit sécurisé sur le service d'AssemblyAI afin qu'il puisse y accéder pour le traitement. Si votre fichier audio est déjà accessible avec une URL publique, vous n'avez pas besoin de faire cette étape, vous pouvez simplement suivre ce guide de démarrage rapide
  2. initiate_transcription.py :indique à l'API quel fichier transcrire et démarrer immédiatement
  3. get_transcription.py :imprime l'état de la transcription si elle est toujours en cours de traitement ou affiche les résultats de la transcription lorsque le processus est terminé

Créez un nouveau répertoire nommé pytranscribe pour stocker ces fichiers au fur et à mesure que nous les écrivons. Passez ensuite au nouveau répertoire du projet.

mkdir pytranscribe
cd pytranscribe

Nous devons également exporter notre clé API AssemblyAI en tant que variable d'environnement. Créez un compte AssemblyAI et connectez-vous au tableau de bord AssemblyAI, puis copiez "Votre jeton API" comme indiqué dans cette capture d'écran :

export ASSEMBLYAI_KEY=your-api-key-here

Notez que vous devez utiliser le export commande dans chaque fenêtre de ligne de commande pour laquelle vous souhaitez que cette clé soit accessible. Les scripts que nous écrivons ne pourront pas accéder à l'API si vous n'avez pas le jeton exporté en tant que ASSEMBLYAI_KEY dans l'environnement dans lequel vous exécutez le script.

Maintenant que notre répertoire de projet est créé et que la clé API est définie comme variable d'environnement, passons à l'écriture du code du premier fichier qui téléchargera les fichiers audio vers le service AssemblyAI.

Téléchargement du fichier audio pour la transcription

Créez un nouveau fichier nommé upload_audio_file.py et placez-y le code suivant :

import argparse
import os
import requests


API_URL = "https://api.assemblyai.com/v2/"


def upload_file_to_api(filename):
    """Checks for a valid file and then uploads it to AssemblyAI
    so it can be saved to a secure URL that only that service can access.
    When the upload is complete we can then initiate the transcription
    API call.
    Returns the API JSON if successful, or None if file does not exist.
    """
    if not os.path.exists(filename):
        return None

    def read_file(filename, chunk_size=5242880):
        with open(filename, 'rb') as _file:
            while True:
                data = _file.read(chunk_size)
                if not data:
                    break
                yield data

    headers = {'authorization': os.getenv("ASSEMBLYAI_KEY")}
    response = requests.post("".join([API_URL, "upload"]), headers=headers,
                             data=read_file(filename))
    return response.json()

Le code ci-dessus importe le argparse , os et requests packagesafin que nous puissions les utiliser dans ce script. Le API_URL est une constante qui a l'URL de base du service AssemblyAI. Nous définissons leupload_file_to_api fonction avec un seul argument, filename qui devrait être une chaîne avec le chemin absolu vers un fichier et son nom de fichier.

Dans la fonction, nous vérifions que le fichier existe, puis utilisons l'encodage de transfert schunked de Request pour diffuser des fichiers volumineux vers l'API AssemblyAI.

Le os getenv du module la fonction lit l'API qui a été définie sur la ligne de commande en utilisant le export commande avec le getenv . Assurez-vous d'utiliser ce export commande dans le terminal où vous exécutez ce script sinon que ASSEMBLYAI_KEY la valeur sera vide. En cas de doute, utilisez echo $ASSEMBLY_AI pour voir si la valeur correspond à votre clé API.

Pour utiliser le upload_file_to_api fonction, ajoutez les lignes de code suivantes dans le upload_audio_file.py fichier afin que nous puissions exécuter correctement ce code en tant que script appelé avec le python commande :

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("filename")
    args = parser.parse_args()
    upload_filename = args.filename
    response_json = upload_file_to_api(upload_filename)
    if not response_json:
        print("file does not exist")
    else:
        print("File uploaded to URL: {}".format(response_json['upload_url']))

Le code ci-dessus crée un ArgumentParser objet qui permet à l'application d'obtenir un seul argument de la ligne de commande pour spécifier le fichier auquel nous voulons accéder, lire et télécharger sur le service AssmeblyAI.

Si le fichier n'existe pas, le script affichera un message indiquant que le fichier est introuvable. Dans le chemin heureux où nous trouvons le fichier correct à ce chemin, le fichier est téléchargé en utilisant le code dans upload_file_to_api fonction.

Exécutez le upload_audio_file.py terminé script en l'exécutant sur la ligne de commande avec le python commande. Remplacer FULL_PATH_TO_FILE avec un chemin absolu vers le fichier que vous souhaitez télécharger, tel que/Users/matt/devel/audio.mp3 .

python upload_audio_file.py FULL_PATH_TO_FILE

En supposant que le fichier se trouve à l'emplacement que vous avez spécifié, lorsque le script aura fini de télécharger le fichier, il imprimera un message comme celui-ci avec une URL unique :

File uploaded to URL: https://cdn.assemblyai.com/upload/463ce27f-0922-4ea9-9ce4-3353d84b5638

Cette URL n'est pas publique, elle ne peut être utilisée que par le service AssemblyAI, donc personne d'autre ne pourra accéder à votre fichier et à son contenu à part vous et leur API de transcription.

La partie qui est importante est la dernière section de l'URL, dans cet exemple c'est 463ce27f-0922-4ea9-9ce4-3353d84b5638 . Enregistrez cet identifiant unique car nous devons le transmettre au prochain script qui lance le service de transcription.

Lancer la transcription

Ensuite, nous allons écrire du code pour lancer la transcription. Créez un nouveau fichier nommé initiate_transcription.py . Ajoutez le code suivant au nouveau fichier.

import argparse
import os
import requests


API_URL = "https://api.assemblyai.com/v2/"
CDN_URL = "https://cdn.assemblyai.com/"


def initiate_transcription(file_id):
    """Sends a request to the API to transcribe a specific
    file that was previously uploaded to the API. This will
    not immediately return the transcription because it takes
    a moment for the service to analyze and perform the
    transcription, so there is a different function to retrieve
    the results.
    """
    endpoint = "".join([API_URL, "transcript"])
    json = {"audio_url": "".join([CDN_URL, "upload/{}".format(file_id)])}
    headers = {
        "authorization": os.getenv("ASSEMBLYAI_KEY"),
        "content-type": "application/json"
    }
    response = requests.post(endpoint, json=json, headers=headers)
    return response.json()

Nous avons les mêmes importations que le script précédent et nous avons ajouté une nouvelle constante, CDN_URL qui correspond à l'URL distincte où AssemblyAIstocke les fichiers audio téléchargés.

Le initiate_transcription La fonction configure essentiellement une seule requête HTTP à l'API AssemblyAI pour démarrer le processus de transcription sur le fichier audio à l'URL spécifique transmise. C'est pourquoi transmettre le file_id est important :cela complète l'URL du fichier audio que nous demandons à AssemblyAI de récupérer.

Terminez le fichier en ajoutant ce code afin qu'il puisse être facilement invoqué à partir de la ligne de commande avec des arguments.

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("file_id")
    args = parser.parse_args()
    file_id = args.file_id
    response_json = initiate_transcription(file_id)
    print(response_json)

Démarrez le script en exécutant le python commande sur le initiate_transcription fichier et transmettez l'identifiant de fichier unique que vous avez enregistré à l'étape précédente.

# the FILE_IDENTIFIER is returned in the previous step and will
# look something like this: 463ce27f-0922-4ea9-9ce4-3353d84b5638
python initiate_transcription.py FILE_IDENTIFIER

L'API renverra une réponse JSON que ce script imprimera sur la ligne de commande.

{'audio_end_at': None, 'acoustic_model': 'assemblyai_default', 'text': None, 
 'audio_url': 'https://cdn.assemblyai.com/upload/463ce27f-0922-4ea9-9ce4-3353d84b5638', 
 'speed_boost': False, 'language_model': 'assemblyai_default', 'redact_pii': False, 
 'confidence': None, 'webhook_status_code': None, 
 'id': 'gkuu2krb1-8c7f-4fe3-bb69-6b14a2cac067', 'status': 'queued', 'boost_param': None, 
 'words': None, 'format_text': True, 'webhook_url': None, 'punctuate': True, 
 'utterances': None, 'audio_duration': None, 'auto_highlights': False, 
 'word_boost': [], 'dual_channel': None, 'audio_start_from': None}

Prenez note de la valeur du id clé dans la réponse JSON. Il s'agit de l'identifiant de transcription que nous devons utiliser pour récupérer le résultat de la transcription. Dans cet exemple, il s'agit de gkuu2krb1-8c7f-4fe3-bb69-6b14a2cac067 . Copiez l'identifiant de transcription dans votre propre réponse, car nous en aurons besoin pour vérifier la fin du processus de transcription à l'étape suivante.

Récupérer le résultat de la transcription

Nous avons téléchargé et commencé le processus de transcription, alors obtenons le résultat dès qu'il est prêt.

Le temps nécessaire pour obtenir les résultats peut dépendre de la taille du fichier, donc ce prochain script enverra une requête HTTP à l'API et rendra compte de l'état de la transcription, ou imprimera la sortie si elle est terminée.

Créez un troisième fichier Python nommé get_transcription.py et mettez-y le code suivant.

import argparse
import os
import requests


API_URL = "https://api.assemblyai.com/v2/"


def get_transcription(transcription_id):
    """Requests the transcription from the API and returns the JSON
    response."""
    endpoint = "".join([API_URL, "transcript/{}".format(transcription_id)])
    headers = {"authorization": os.getenv('ASSEMBLYAI_KEY')}
    response = requests.get(endpoint, headers=headers)
    return response.json()


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("transcription_id")
    args = parser.parse_args()
    transcription_id = args.transcription_id
    response_json = get_transcription(transcription_id)
    if response_json['status'] == "completed":
        for word in response_json['words']:
            print(word['text'], end=" ")
    else:
        print("current status of transcription request: {}".format(
              response_json['status']))

Le code ci-dessus a les mêmes importations que les autres scripts. Dans ce nouveau get_transcription fonction, nous appelons simplement l'API AssemblyAI avec notre clé API et l'identifiant de transcription de l'étape précédente (pas l'identifiant du fichier). Nous récupérons la réponse JSON et la renvoyons.

Dans la fonction principale, nous traitons l'identifiant de transcription qui est transmis en tant qu'argument de ligne de commande et le transmettons au get_transcription fonction. Si la réponse JSON du get_transcription la fonction contient un completed status puis nous imprimons les résultats de la transcription. Sinon, imprimez l'état actuel qui est soit queued ou processing avant c'est completed .

Appelez le script à l'aide de la ligne de commande et de l'identifiant de transcription de la section précédente :

python get_transcription.py TRANSCRIPTION_ID

Si le service n'a pas encore commencé à travailler sur la transcription, il renverra queued comme ceci :

current status of transcription request: queued

Lorsque le service travaille actuellement sur le fichier audio, il renverra processing :

current status of transcription request: processing

Une fois le processus terminé, notre script renverra le texte de la transcription, comme vous le voyez ici :

An object relational mapper is a code library that automates the transfer of 
data stored in relational, databases into objects that are more commonly used
in application code or EMS are useful because they provide a high level 

...(output abbreviated)

Ça y est, nous avons notre transcription !

Vous vous demandez peut-être quoi faire si la précision n'est pas là où vous en avez besoin pour votre situation. C'est là qu'intervient l'amélioration de la précision des mots clés ou des expressions. Vous pouvez utiliser l'une ou l'autre de ces deux méthodes pour augmenter la précision de vos enregistrements à un niveau acceptable pour votre situation.

Quelle est la prochaine ?

Nous venons de finir d'écrire des scripts qui appellent l'API AssemblyAI pour transcrire des enregistrements avec parole en sortie texte.

Ensuite, jetez un œil à certaines de leurs documentations plus avancées qui vont au-delà des bases dans ce didacticiel :

  • Formats de fichiers pris en charge
  • Transcrire des enregistrements double canal/stéréo
  • Obtenir des étiquettes de haut-parleur (diarisation du haut-parleur)

Des questions? Faites-le moi savoir via un ticket de problème sur le référentiel Full Stack Python, sur Twitter@fullstackpythonou @mattmakai.Vous voyez quelque chose qui ne va pas avec ce message ? La source de cette page sur GitHuband soumet une pull request.