[PYTHON] Une histoire de lecture d'un livre d'images en synthétisant la voix avec l'API COTOHA et l'API Cloud Vision

Qu'est-ce que l'API COTOHA?

Il s'agit d'un groupe d'API pouvant être utilisées pour l'analyse du langage, etc. émises par NTT. Non seulement l'analyse syntaxique, mais aussi la reconnaissance vocale et la synthèse vocale (payante) sont incluses, vous pouvez donc faire la plupart des robots de conversation et de l'analyse vocale! Il regorge de fonctionnalités que vous pouvez atteindre là où cela vous démange, telles que l'extraction de mots-clés et la suppression de la stagnation, qui ont été implémentées de manière constante jusqu'à présent, et vous pouvez également obtenir le taux de correspondance des réponses des utilisateurs dans l'apprentissage en profondeur, donc si vous avez de la précision, japonais N'est-ce pas le plus fort à gérer?

Page API COTOHA

Il s'agit d'un service qui fournit diverses API de traitement du langage naturel / de la voix telles que l'analyse syntaxique, l'analyse de correspondance, l'extraction de mots-clés, la reconnaissance vocale et la synthèse. En utilisant les résultats de recherche de 40 ans du groupe NTT, tels que le dictionnaire japonais et la technologie de classification des significations de plus de 3000 types de mots, une analyse avancée peut être facilement utilisée avec l'API.

Ce produit

J'ai pensé que ce serait très intéressant si je pouvais extraire des personnages des images prises du livre d'images, analyser les phrases, ajouter une direction et une sortie en théâtre, alors j'ai fait un essai de ce proto. Je passe des jours où je ne peux pas rencontrer ma fille qui retourne chez les parents de ma femme à cause du virus corona, alors je l'ai fait parce que je voulais jouer avec ma fille quand elle s'est calmée. Si vous vous connectez à la reconnaissance de caractères → synthèse vocale normalement, vous deviendrez un homme de lecture de bâton hiragana, vous ne pourrez donc pas l'obtenir. Pas bon pour l'éducation de ma fille. C'est là que l'API COTOHA entre en jeu. Utilisez l'API COTOHA pour lire à haute voix un livre d'images avec émotion.

Comme un flux brutal,

  1. Extraire du texte d'images avec Cloud Vision OCR
  2. Convertissez hiragana en kanji avec google translate
  3. Corrigez les erreurs de conversion avec la détection d'erreur de reconnaissance vocale (β) de l'API COTOHA
  4. Reconnaître les émotions des phrases par l'analyse des émotions de l'API COTOHA
  5. Analysez les personnages avec l'estimation des attributs utilisateur de l'API COTOHA (β)
  6. Utilisez l'API HOYA VoiceText pour sélectionner le meilleur haut-parleur et le meilleur style de parole, et synthétiser la voix. Voici la procédure.

Le résultat est enregistré sous forme de fichier txt ou json pour chaque phase, et il est écrit pour être utilisé dans la phase suivante.

1. Extraire du texte d'images avec Cloud Vision OCR

Je n'entrerai pas dans les détails car ce n'est pas le principal cette fois. Si vous voulez en savoir plus, veuillez vous référer à ici écrit séparément. Le livre d'images utilisé pour ce test est "Shiroi Sagi et Kuroi Sagi" de Garth Williams. 500_Ehon_582.jpg

La raison pour laquelle j'ai choisi cela était parce qu'il semblait facile à reconnaître, et parce que c'était le premier livre d'images que j'avais acheté et que je l'avais lu à mort. Le code source est ci-dessous. Fondamentalement, l'anglais est supprimé sous l'hypothèse que seul le japonais apparaîtra.

<détails>

Code source </ summary>

import copy
from google.cloud import vision
from pathlib import Path
import re

def is_japanese(text):
    if re.search(r'[Ah-Hmm]', text):
        return True
    else:
        return False

client = vision.ImageAnnotatorClient()
row_list = []
res_list = []
text_path = "./ehon_text/text.txt"

with open(text_path, 'w') as f:
    for x in range(1, 15):
        p = Path(__file__).parent / "ehon_image/{}.png ".format(x)
        with p.open('rb') as image_file:
            content = image_file.read()
        image = vision.types.Image(content=content)
        response = client.text_detection(image=image)
        if len(response.text_annotations) == 0:
            row_list.append("-")
        for lines in response.text_annotations:
            if lines.locale != "ja":
                for text in str(lines.description).split("\n"):
                    if is_japanese(text):
                        print(text)
                        f.write(text + '\n')
            else:
                print(lines.description)
                f.write(lines.description)
            break
        f.write("\n")

Le résultat de l'exécution ressemble à ce qui suit (extrait partiel) Il n'est pas à 100% comme prévu, mais il est moyennement précis. La plupart des phrases sont des hiragana et peuvent être faciles à reconnaître. Il semble que "ki" et "sa" et "po" et "bo" sont difficiles, donc je fais souvent une erreur. Étant donné que les caractères de ce livre d'images sont plus petits que les images dans leur ensemble, il y a un gros problème de résolution. Je l'ai bien reconnu lorsque j'ai pris une grande photo des seuls personnages. Heureusement, le texte du résultat n'est pas affiché cette fois, et la voix est synthétisée, donc même si "pissenlit" devient "pissenlit", on a l'impression d'avoir été lu un instant, et il n'y a pas de forte sensation d'inconfort. Vous ne devriez pas être exposé à votre fille.

Au bout d'un moment, le lapin noir s'assit.
Et j'ai fait quelque chose qui avait l'air très bien.
"Qu'est-ce qui ne va pas?」
J'ai entendu une épée blanche.
"Ouais, j'ai réfléchi pendant un moment."
Répondit le sagi noir.

2. Convertissez hiragana en kanji avec google translate

Hiragana est plus apprécié en matière de reconnaissance à partir d'images, mais les opérations ultérieures utilisant du texte donneront de meilleurs résultats dans les phrases avec kanji et kana (devrait). Le japonais est la langue mendokusai, et le niveau de difficulté du programme change considérablement selon qu'il s'agit d'un mélange de kanji ou de kana ou seulement de hiragana. Il est difficile d'analyser le sens uniquement avec hiragana, qui ne possède que des informations sonores. L'intonation de la lecture à haute voix au moment de la synthèse vocale est également différente, et la précision lors de l'application de l'analyse doit également inclure les kanji.

Pour le moment, j'ai utilisé google translate cette fois. Le code source est ci-dessous.

<détails>

Code source </ summary>

import urllib
import json

kanji_text_path = "./ehon_text/kanji_text.txt"

with open('./ehon_text/text.txt', 'r') as f:
    lines = f.readlines()

url = "http://www.google.com/transliterate?"
kanji_text = ""

with open('./ehon_text/kanji_text.txt', 'w') as f:
    for line in lines:
        if line == "\n":
            f.write(line)
        else:
            param = {'langpair':'ja-Hira|ja','text':line.strip().replace(' ','').replace(' ','')}
            paramStr = urllib.parse.urlencode(param)
            readObj = urllib.request.urlopen(url + paramStr)
            response = readObj.read()
            data = json.loads(response)
            for text in data:
                kanji_text += text[1][0]
            print(kanji_text)
            f.write(kanji_text)
            kanji_text = ""

Le résultat de l'exécution ressemble à ceci. C'est douloureux que "Qu'est-ce qui ne va pas?" Devient "Assimilé?" .. Kinpouge est également converti correctement, mais ce n'est peut-être pas si bon car l'API de synthèse vocale sera lue à voix haute ou elle sera subtile.

Au bout d'un moment, le lapin noir s'assit.
Et il a fait une grimace très triste.
"Vous êtes-vous assimilé?" J'ai entendu une chanson de lapin blanc.
"Ouais, j'ai réfléchi pendant un moment," répondit le lapin noir.
Ensuite, Nibiki a fait une cachette dans le champ où des poussins et des fleurs de phénix dorés étaient en fleurs.

3. Corrigez les erreurs de conversion avec la détection d'erreur de reconnaissance vocale (β) de l'API COTOHA

À ce stade, j'étais un peu intéressé, alors j'ai pensé qu'il serait possible de corriger la phrase mélangée avec les erreurs de conversion ci-dessus en appliquant la détection d'erreur de reconnaissance vocale (β), alors j'ai essayé. Même en reconnaissance vocale, si la parole est courte, l'analyse syntaxique est insuffisante et une conversion erronée se produit. Puisqu'il le corrige, même s'il est utilisé à cette fin, il devrait être adapté à l'utilisation. Le code source est ci-dessous. Pour le moment, seuls ceux dont la fiabilité est supérieure à 0,9 sont remplacés par les résultats du premier candidat.

<détails>

Code source </ summary>



import requests
import json

access_token_publish_url = "https://api.ce-cotoha.com/v1/oauth/accesstokens"
api_base_url = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
clientid = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
clientsecret = "XXXXXXXXXXXXXXXXXXXXX"

headers = {'Content-Type': 'application/json',}
data = json.dumps({"grantType": "client_credentials","clientId": clientid,"clientSecret": clientsecret})
response = requests.post(access_token_publish_url, headers=headers, data=data)
print(response)
access_token = json.loads(response.text)["access_token"]

api_url = api_base_url + "nlp/beta/detect_misrecognition"
headers = {"Authorization": "Bearer " + access_token, "Content-Type": "application/json;charset=UTF-8"}

with open('./ehon_text/kanji_text.txt', 'r') as f:
    lines = f.readlines()

with open('./ehon_text/kanji_text2.txt', 'w') as f:
    for line in lines:
        print(line)
        data = json.dumps({"sentence": line})
        response = requests.post(api_url, headers=headers, data=data)
        result = json.loads(response.text)
        if result["result"]["score"] > 0.9:
            for candidate in result["result"]["candidates"]:
                if candidate["detect_score"] > 0.9:
                    line = line.replace(candidate["form"], candidate["correction"][0]["form"])
        # print(response)
        # print(json.loads(response.text))
        print(line)
        f.write(line)


Le résultat est le suivant: dans Google Transrate, tous les "deux" ont été convertis en "double", mais certains d'entre eux (mais pas tous) ont été améliorés. Aucune partie n'a été aggravée, donc je pense que c'est la bonne réponse. (Je veux dire, les lapins sont comptés comme des animaux)

before

Chaque matin, Nibiki sautait du lit et sautait dans la lumière du matin. Et nous avons aimé jouer ensemble toute la journée.

after

Chaque matin, ils sautaient de leur lit et sautaient dans la lumière du matin. Et nous avons aimé jouer ensemble toute la journée.

4. Reconnaître les émotions des phrases par l'analyse des émotions de l'API COTOHA

Avec l'API COTOHA, vous pouvez extraire des mots émotionnels du texte et prendre des négatifs et des positifs pour toute la phrase. En fait, il existe une synthèse vocale qui peut donner des émotions comme paramètres, donc si ce résultat peut être utilisé comme paramètre au moment de la synthèse vocale, il devrait être possible de lire à haute voix avec plus d'émotion. De plus, je ne vais pas l'utiliser cette fois car je ne fais pas de reconnaissance vocale, mais selon la façon dont je l'utilise, je pourrai peut-être prendre des émotions détaillées telles que "il n'y a rien" de l'utilisateur.

Beaucoup de choses qui traitent des émotions sont celles qui ne donnent que des aspects négatifs et positifs en conséquence, et celles qui renvoient plusieurs émotions telles que heureux, triste, en colère, etc. en pourcentage, mais dans l'API COTOHA, le premier et les caractéristiques concernant la phrase entière Ce dernier est proche de l'unité d'un mot typique.

Cette fois, je prévoyais de séparer les voix du lapin blanc et du lapin noir, selon le narrateur.

"Qu'est-ce qui ne va pas (triste)", a déclaré Rosagi (heureuse)

J'ai senti que ce serait étrange s'il y avait une différence dans les émotions de ces trois personnes dans une phrase, et j'ai pensé qu'un simple échantillon long serait plus facile à obtenir des résultats, alors je le jette dans l'API pour "phrase" C'est une unité.

Le code source est ci-dessous.

<détails>

Code source </ summary>



import requests
import json
import copy

access_token_publish_url = "https://api.ce-cotoha.com/v1/oauth/accesstokens"
api_base_url = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
clientid = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
clientsecret = "XXXXXXXXXXXXXXXXXXXXX"

headers = {'Content-Type': 'application/json',}
data = json.dumps({"grantType": "client_credentials","clientId": clientid,"clientSecret": clientsecret})
response = requests.post(access_token_publish_url, headers=headers, data=data)
access_token = json.loads(response.text)["access_token"]

api_url = api_base_url + "nlp/v1/sentiment"
headers = {"Authorization": "Bearer " + access_token, "Content-Type": "application/json;charset=UTF-8"}

with open('./ehon_text/kanji_text2.txt', 'r') as f:
    lines = f.readlines()

story = []
text_list = []
page_sentenses = []
aa = {"sentiment": "", "text": ""}
with open('./ehon_json/ehon.json', 'w') as f:
    for line in lines:
        for text in line.split("。"):
            if text != "\n":
                data = json.dumps({"sentence": text})
                response = requests.post(api_url, headers=headers, data=data)
                result = json.loads(response.text)
                # print(text)
                # print(result["result"]["sentiment"])
                text_list.append({"sentiment": result["result"]["sentiment"], "text": text})
        story.append(copy.deepcopy(text_list))
        text_list = []
    json.dump(story, f, indent=4, ensure_ascii=False)

Le résultat (un exemple de réponse) ressemble à ce qui suit. Je pensais que ce ne serait que Neutre en écriture, mais il y a des hauts et des bas inattendus d'émotions. Les négatifs et les positifs sont sortis correctement, donc je pense qu'il est utile de lire à haute voix avec émotion.

Chaque matin, ils sautaient de leur lit et sautaient dans la lumière du matin.
{'result': {'sentiment': 'Neutral', 'score': 0.3747452771403413, 'emotional_phrase': []}, 'status': 0, 'message': 'OK'}
Et j'ai fait un visage très triste
{'result': {'sentiment': 'Negative', 'score': 0.6020340536995118, 'emotional_phrase': [{'form': 'Ça a l'air très triste', 'emotion': 'N'}]}, 'status': 0, 'message': 'OK'}

5. Analysez les personnages avec l'estimation des attributs utilisateur de l'API COTOHA (β)

L'API COTOHA a une fonction d'estimation d'attributs utilisateur (β) et un personnage assez détaillé est renvoyé. Le nombre de haut-parleurs étant également plus important en synthèse vocale, je me suis demandé s'il serait possible de faire correspondre automatiquement les haut-parleurs à partir de ces informations. Je voulais vraiment tout faire automatiquement dans le programme, mais je ne pouvais penser à aucune logique pour décider quel énoncé appartenait à qui. .. Cette fois, cela a été fait manuellement. Dans le cas des livres d'images japonais, les lignes sont souvent correctement placées entre "", donc La spécification est de saisir d'abord le nombre de caractères, d'extraire le contenu de "" avec une expression régulière et de demander à l'utilisateur d'attribuer un identifiant pour chaque énoncé. L'identifiant du narrateur est défini sur 0. Le code source est ci-dessous

<détails>

Code source </ summary>


import requests
import re
import json

char0 = []
char_num = int(input("Please input number of characters =>"))
for i in range(1, char_num+1):
    exec('char{} = []'.format(i))

with open('./ehon_json/ehon.json', 'r') as f:
    story = json.load(f)

story_list = []
for page in story:
    page_list = []
    for sentense in page:
        # try:
        speech_list = re.split("(?<=」)|(?=「)", sentense["text"])
        for speech in speech_list:
            if speech != "":
                if speech.find("「") > -1:
                    while True:
                        try:
                            print(sentense)
                            print(speech)
                            id = int(input("Please input char ID =>"))
                            if id <= char_num and id > 0:
                                break
                        except:
                            print("once again")
                    exec('char{}.append(speech)'.format(id))
                    page_list.append({"sentiment": sentense["sentiment"], "text": speech, "char": id})
                else:
                    char0.append(speech)
                    page_list.append({"sentiment": sentense["sentiment"], "text": speech, "char": 0})
    story_list.append(copy.deepcopy(page_list))
print(story_list)

access_token_publish_url = "https://api.ce-cotoha.com/v1/oauth/accesstokens"
api_base_url = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
clientid = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
clientsecret = "XXXXXXXXXXXXXXXXXXXXX"

headers = {'Content-Type': 'application/json',}
data = json.dumps({"grantType": "client_credentials","clientId": clientid,"clientSecret": clientsecret})
response = requests.post(access_token_publish_url, headers=headers, data=data)
access_token = json.loads(response.text)["access_token"]

api_url = api_base_url + "nlp/beta/user_attribute"
headers = {"Authorization": "Bearer " + access_token, "Content-Type": "application/json;charset=UTF-8"}

char_list = []
for i in range(0, char_num+1):
    exec('l = char{}'.format(i))
    data = json.dumps({"document": l})
    response = requests.post(api_url, headers=headers, data=data)
    result = json.loads(response.text)
    char_list.append(result)
    print(result)

with open('./ehon_json/char.json', 'w') as f:
    json.dump(char_list, f, indent=4, ensure_ascii=False)


De cette façon, j'ai fait une liste d'énoncés pour chaque orateur, y compris le narrateur, le lapin blanc et le lapin noir, et les ai jetés dans l'API. Cliquez ici pour les résultats.

·narrateur

{
        "result": {
            "age": "40-49 ans",
            "civilstatus": "marié",
            "habit": [
                "SMOKING"
            ],
            "hobby": [
                "COLLECTION",
                "COOKING",
                "FORTUNE",
                "GOURMET",
                "INTERNET",
                "SHOPPING",
                "STUDY",
                "TVGAME"
            ],
            "location": "Kinki",
            "occupation": "employé"
        },
        "status": 0,
        "message": "OK"
    }

・ Shiroi Sagi

    {
        "result": {
            "age": "40-49 ans",
            "civilstatus": "marié",
            "earnings": "-1M",
            "hobby": [
                "COOKING",
                "GOURMET",
                "INTERNET",
                "TVDRAMA"
            ],
            "location": "Kanto",
            "occupation": "employé"
        },
        "status": 0,
        "message": "OK"
    }

・ Kuroisagi

    {
        "result": {
            "age": "40-49 ans",
            "earnings": "-1M",
            "hobby": [
                "INTERNET"
            ],
            "location": "Kanto",
            "occupation": "employé"
        },
        "status": 0,
        "message": "OK"
    }

Du haut, le narrateur, le lapin blanc et le lapin noir. Hmmm? Ce résultat a peut-être été un peu décevant. Ou plutôt, la documentation indiquait qu'elle renverrait quelque chose comme "gender", mais cela n'a pas été inclus dans les résultats. Est-ce toujours en version bêta? Mais c'est une histoire de mariage, et je pense que c'est un adulte, alors peut-être que c'est inattendu. Je me demande s'il serait impossible d'envoyer un énorme journal de conversation si je voulais l'obtenir avec une telle précision.

Si la précision augmente ici, il peut être possible de créer un modèle pour chaque personnage dans une certaine mesure et d'utiliser la reconnaissance vocale pour parler avec les personnages du livre d'images. Pour le moment, cette fois, j'ai sélectionné manuellement une voix comme celle-là en référence à ce résultat.

6. Utilisez l'API HOYA VoiceText pour sélectionner le meilleur haut-parleur et le meilleur style de parole, et synthétiser la voix.

Enfin, ces informations sont combinées et synthétisées vocalement. En ce qui concerne la synthèse vocale, je ne pouvais pas spécifier d'émotions pour l'API COTOHA, et comme je ne me suis inscrit que pour le plan gratuit, j'ai essayé d'utiliser VOICE TEXT de HOYA cette fois. En fait, je voulais faire une composition vocale avec ma propre voix à la station coe et en faire une application que mon père lirait à tout moment, mais je ne pouvais pas le faire de mon propre pouvoir.

À propos, veuillez noter que la voix synthétique de HOYA est également une licence qui interdit la distribution secondaire, etc.

VoixeText Web API

L'utilisation commerciale, l'utilisation secondaire et la distribution des données vocales créées avec la version gratuite sont interdites. Veuillez vérifier les conditions d'utilisation avant d'utiliser ce service.

Cette fois

narrateur:"hikari"
Lapin blanc:"haruka"
Kuroi Sagi:"takeru"

Et dit. Aussi, les émotions

    "Neutral":""
    "Positive":"happiness"
    "Negative":"sadness"

Il est défini comme. De plus, si vous synthétisez normalement, la voix sera interrompue car le tampon à l'arrière est insuffisant, donc la balise SSML `<vt_pause = 1000 /> 'est ajoutée après tous les mots pour allonger le fichier.

<détails>

Code source </ summary>


from voicetext import VoiceText
import copy
import json

speaker = {
    0:"hikari",
    1:"haruka",
    2:"takeru"
}

emotion = {
    "Neutral":"",
    "Positive":"happiness",
    "Negative":"sadness"
}

play_list = []
vt = VoiceText('XXXXXXXXXXXXXXXXX')
with open('./ehon_json/story.json', 'r') as f:
    story = json.load(f)
    for i, page in enumerate(story):
        play = {"image": "./ehon_image/{}.png ".format(i+1), "voice":[]}
        voice_list = []
        for j, speech in enumerate(page):
            print(speech)
            if speech["sentiment"] == "Neutral":
                vt.speaker(speaker[speech["char"]])
            else:
                vt.speaker(speaker[speech["char"]]).emotion(emotion[speech["sentiment"]])
            with open('./ehon_speech/{}_{}.wav'.format(i+1, j+1), 'wb') as f:
                print(speech["text"])
                f.write(vt.to_wave(speech["text"] + '<vt_pause=1000/>'))
            voice_list.append('./ehon_speech/{}_{}.wav'.format(i+1, j+1))
        play["voice"] = copy.deepcopy(voice_list)
        play_list.append(copy.deepcopy(play))
        voice_list = []


with open('./play_json/play.json', 'w') as f:
    json.dump(play_list, f, indent=4, ensure_ascii=False)


finalement

Voici le son généré cette fois par ces méthodes et lu en synchronisation avec l'image lue.

Pour le moment, je ne le mettrai que sur une partie. Je l'ai fait cette fois et c'était assez intéressant. Comme perspective d'avenir, il serait intéressant d'en faire un appareil comme une "caméra de lecture de livre d'images" comme Razpai, et j'ai pensé qu'il serait bon de le connecter à un projecteur pour en faire un théâtre. Si vous le connectez mieux avec l'API Vision, vous pouvez créer une expérience intéressante en liant des mots et des images. Il peut être intéressant d'écrire un livre d'images par vous-même ou de parler de graffitis sur le livre d'images. Les émotions peuvent être prises dans des unités assez petites, de sorte que la musique de fond et les effets sonores peuvent être ajoutés avec un peu plus de modifications.

L'API COTOHA semble être plus jouable, donc j'aimerais écrire un article si je continue à l'implémenter.

Pour le moment, je vais bien sûr lire le livre d'images pour ma fille. Au fait, ma fille a maintenant «1,5 mois».

Recommended Posts