скрипт получения из видеофайла, двух файлов, звуковая дорожка wav, и текст txt распознанной речи
Напишем скрипт на python, который из видеофайла, по пути указанного через атрибуты на вход, создаст два файла, один аудиофайл в формате wav, это звук из этого видео, а второй txt файл, в котором будет текст из распознанных из этого звукового файла слов. Причём видео может быть на одном из 142 возможных языков для распознавания.
Предисловие, зачем это может быть нужно. У меня например возникло желание узнать что говорится в одном видео с Youtube, вот ссылка https://www.youtube.com/watch?v=xoHPnwuyIJc
Но проблема в том что у видео нет субтитров, и у него язык Хинди, я об этом просто догадался. Поэтому я скачал его себе на компьютер, и обработал следующим скриптом, чтобы получить хоть какую-то информацию по данной теме. Если у кого-то похожая задача, то этот скрипт может быть им использован тоже, потому что довольно универсален и достаточно прост.
Язык, который нужно распознать из видео, пользователь указывает самостоятельно при запуске скрипта. Использована библиотека следующих языков https://cloud.google.com/speech-to-text/docs/speech-to-text-supported-languages.
Для создания виртуального окружения, перейдите в каталог своего проекта и выполните:
### Активация
обновить менеджер пакетов pip
установите следующие пакеты
speech_recognition argparse moviepy.editor googletrans pydub numpy
С помощью этой команды:
создайте файл, например pars_video.py, это и будет скрипт со следующим содержимым:
☯
Terminal:
⌕
≡
✕
try:
import speech_recognition as sr
import sys
import argparse
from moviepy.editor import *
import os
from googletrans import Translator
from pydub import AudioSegment
import numpy as np
import re
# Определим словарь из возможных кодов взятых на сайте https://cloud.google.com/speech-to-text/docs/speech-to-text-supported-languages
codes = {'Afrikaans (South Africa)':'af-ZA','Albanian (Albania)': 'sq-AL','Amharic (Ethiopia)': 'am-ET ','Arabic (Algeria)': 'ar-DZ','Arabic (Bahrain)': 'ar-BH','Arabic (Egypt)': 'ar-EG','Arabic (Iraq)': 'ar-IQ','Arabic (Israel)': 'ar-IL','Arabic (Jordan)': 'ar-JO','Arabic (Kuwait)': 'ar-KW','Arabic (Lebanon)': 'ar-LB','Arabic (Mauritania)': 'ar-MR','Arabic (Oman)': 'ar-OM','Arabic (Qatar)': 'ar-QA','Arabic (Saudi Arabia)': 'ar-SA','Arabic (State of Palestine)': 'ar-PS','Arabic (Tunisia)': 'ar-TN','Arabic (United Arab Emirates)': 'ar-AE','Arabic (Yemen)': 'ar-YE','Armenian (Armenia)': 'hy-AM','Azerbaijani (Azerbaijan)': 'az-AZ','Basque (Spain)': 'eu-ES','Bengali (Bangladesh)': 'bn-BD','Bengali (India)': 'bn-IN','Bosnian (Bosnia and Herzegovina)': 'bs-BA','Bulgarian (Bulgaria)': 'bg-BG','Burmese (Myanmar)': 'my-MM','Catalan (Spain)': 'ca-ES','Chinese, Cantonese (Traditional Hong Kong)': 'yue-Hant-HK','Chinese, Mandarin (Simplified, China) ': 'zh','Chinese, Mandarin (Traditional, Taiwan)': 'zh-TW','Croatian (Croatia)': 'hr-HR','Czech (Czech Republic)': 'cs-CZ','Danish (Denmark)': 'da-DK','Dutch (Belgium)': 'nl-BE','Dutch (Netherlands)': 'nl-NL','English (Australia)': 'en-AU','English (Canada)': 'en-CA','English (Ghana)': 'en-GH','English (Hong Kong)': 'en-HK','English (India)': 'en-IN','English (Ireland)': 'en-IE','English (Kenya)': 'en-KE','English (New Zealand)': 'en-NZ','English (Nigeria)': 'en-NG','English (Pakistan)': 'en-PK','English (Philippines)': 'en-PH','English (Singapore)': 'en-SG','English (South Africa)': 'en-ZA','English (Tanzania)': 'en-TZ','English (United Kingdom)': 'en-GB','English (United States)': 'en-US','Estonian (Estonia)': 'et-EE','Filipino (Philippines)': 'fil-PH','Finnish (Finland)': 'fi-FI','French (Belgium)': 'fr-BE','French (Canada)': 'fr-CA','French (France)': 'fr-FR','French (Switzerland)': 'fr-CH','Galician (Spain)': 'gl-ES','Georgian (Georgia)': 'ka-GE','German (Austria)': 'de-AT','German (Germany)': 'de-DE','German (Switzerland)': 'de-CH','Greek (Greece)': 'el-GR','Gujarati (India)': 'gu-IN','Hebrew (Israel)': 'iw-IL','Hindi (India)': 'hi-IN','Hungarian (Hungary)': 'hu-HU','Icelandic (Iceland)': 'is-IS','Indonesian (Indonesia)': 'id-ID','Italian (Italy)': 'it-IT','Italian (Switzerland)': 'it-CH','Japanese (Japan)': 'ja-JP','Javanese (Indonesia)': 'jv-ID','Kannada (India)': 'kn-IN','Kazakh (Kazakhstan)': 'kk-KZ','Khmer (Cambodia)': 'km-KH','Korean (South Korea)': 'ko-KR','Lao (Laos)': 'lo-LA','Latvian (Latvia)': 'lv-LV','Lithuanian (Lithuania)': 'lt-LT','Macedonian (North Macedonia)': 'mk-MK','Malay (Malaysia)': 'ms-MY','Marathi (India)': 'mr-IN','Mongolian (Mongolia)': 'mn-MN','Nepali (Nepal)': 'ne-NP','Norwegian Bokmål (Norway)': 'no-NO','Persian (Iran)': 'fa-IR','Polish (Poland)': 'pl-PL','Portuguese (Brazil)': 'pt-BR','Portuguese (Portugal)': 'pt-PT','Punjabi (Gurmukhi India)': 'pa-Guru-IN','Romanian (Romania)': 'ro-RO','Russian (Russia)': 'ru-RU','Kinyarwanda (Rwanda)': 'rw-RW','Serbian (Serbia)': 'sr-RS','Sinhala (Sri Lanka)': 'si-LK','Slovak (Slovakia)': 'sk-SK','Slovenian (Slovenia)': 'sl-SI','Swati (South Africa)': 'ss-latn-za','Southern Sotho (South Africa)': 'st-ZA','Spanish (Argentina)': 'es-AR','Spanish (Bolivia)': 'es-BO','Spanish (Chile)': 'es-CL','Spanish (Colombia)': 'es-CO','Spanish (Costa Rica)': 'es-CR','Spanish (Dominican Republic)': 'es-DO','Spanish (Ecuador)': 'es-EC','Spanish (El Salvador)': 'es-SV','Spanish (Guatemala)': 'es-GT','Spanish (Honduras)': 'es-HN','Spanish (Mexico)': 'es-MX','Spanish (Nicaragua)': 'es-NI','Spanish (Panama)': 'es-PA','Spanish (Paraguay)': 'es-PY','Spanish (Peru)': 'es-PE','Spanish (Puerto Rico)': 'es-PR','Spanish (Spain)': 'es-ES','Spanish (United States)': 'es-US','Spanish (Uruguay)': 'es-UY','Sundanese (Indonesia)': 'su-ID','Swahili (Kenya)': 'sw-KE','Swahili (Tanzania)': 'sw-TZ','Swedish (Sweden)': 'sv-SE','Tamil (India)': 'ta-IN','Tamil (Malaysia)': 'ta-MY','Tamil (Singapore)': 'ta-SG','Tamil (Sri Lanka)': 'ta-LK','Telugu (India)': 'te-IN','Thai (Thailand)': 'th-TH','Setswana (South Africa)': 'tn-latn-za','Turkish (Turkey)': 'tr-TR','Tsonga (South Africa)': 'ts-ZA','Ukrainian (Ukraine)': 'uk-UA','Urdu (India)': 'ur-IN','Urdu (Pakistan)': 'ur-PK','Uzbek (Uzbekistan)': 'uz-UZ','Venda (South Africa)': 've-ZA','Vietnamese (Vietnam)': 'vi-VN','isiXhosa (South Africa)': 'xh-ZA','Zulu (South Africa)': 'zu-ZA'}
#тригер если есть хотябы один фрагмент резульата, то будет больше нуля
n = 0
for i, (language, code) in enumerate(codes.items()):
print(f"{i+1}. {language}: {code}")
print('показан список доступных языков, которые могут быть в источнике звука. выбирайте тот который считаете нужным. скрипт будет искать слова на этом языка в вашем входящем видео файле и вносить их в текст.')
print('Укажите на каком языке речь в вашем видео. Напишите только номер:')
# регулярным выражение отсекаем со входа прочие символы кроме числа
inp = re.findall(r'\d+', input("Enter a number: "))
choice = int(inp[0])
selected_language = list(codes.keys())[choice-1]
selected_code = codes[selected_language]
print(f"You selected '{selected_language}' with code '{selected_code}'.")
def createParser ():
parser = argparse.ArgumentParser(description='Process some input.')
#Определить аргументы, которые мы принимаем
parser.add_argument('-p', '--path', type=str, help='the path to process')
args = parser.parse_args()
return args
if __name__ == '__main__':
args = createParser()
def get_audio(args=args):
# аргумент физический адрес файла видео
if args.path:
print ("Получаем аудио из видео по адресу, {}!".format (args.path) )
# создать объект видеоклипа из входного файла
clip = VideoFileClip(args.path)
# Извлечь имя файла из пути
file_name = os.path.basename(args.path)
# Распечатать имя файла
print(file_name)
new_wav=file_name+'.wav'
# извлечь звук из клипа
audio = clip.audio
# сохранить аудио как новый файл
audio.write_audiofile(new_wav, codec='pcm_s16le')
print('Первая часть скрипта успешно отработана. Звук записан в созданный аудио файл: '+new_wav)
# создать объект распознавателя
r = sr.Recognizer()
try:
# Загрузим аудиофайл
audio_file = AudioSegment.from_file(new_wav)
# Установите продолжительность сегмента в миллисекундах (177 seconds = 177000 milliseconds)
segment_duration = 177000
# Рассчитать количество сегментов
num_segments = int(np.ceil(audio_file.duration_seconds / (segment_duration/1000)))
print('Приступили ко второй части скрипта, процесс идёт, ждите завершения.')
print(f'Всего предстоит обработать {num_segments} сегментов по {segment_duration/1000} секунд каждый.')
# Разделить аудиофайл на сегменты, создаём словарь сегметов
segments = []
for i in range(num_segments):
start = i * segment_duration
end = min((i+1) * segment_duration, len(audio_file))
#учитываем что последний сегмент может быть другого размера
if i == num_segments - 1:
segment = audio_file[start:]
else:
segment = audio_file[start:end]
segments.append(segment)
except Exception as err:
print('разбивка', err)
#Обрабатывать каждый сегмент отдельно
for i, segment in enumerate(segments):
# Сохраним каждый сегмент во временный новый файл
print(f'начало обработки {i+1} сегмента')
filename = f'segment_{i+1}.wav'
segment.export(filename, format='wav')
n=int(i+1)
# откройте аудиофайл с помощью класса Recognizer
with sr.AudioFile(filename) as source:
# прослушать данные (загрузить аудио в память)
audio_data = r.record(source)
#удаляем временный файл фрагмента
os.remove(filename)
# транскрибировать аудиоданные в текст
if audio_data is not None:
try:
text = r.recognize_google(audio_data, language=selected_code)
print('УСПЕХ!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
file=file_name+'.txt'
# Записать текст фрагмента в конец файла. внимание файл будет создан, но если вы запустите скрипт с теми же параметрами ещё раз, то файл будет дописывать результы не удаляя предыдущих
with open(file, 'a') as f:
f.write(text)
print(f'содержимое {i+1} сегмента {text}') # печатает содержимое файла на данный момент
print(f'сегмент № {i+1} обработан и добавлен в файл результата {file}:')
except Exception as e:
print(f"Error: {str(e)}")
else:
if n<num_segments:
print("Работа скрипта не завершена, из-за ошибки в данных аудиопотока")
else:
print("Работа скрипта завершена")
return file
print(f'Результат заключительной части скрипта, находится в файле: {get_audio()}')
#обернули всё try - except, чтобы выбросить исключение и понять в чём ошибка в случае таковой
except Exception as err:
if n<1:
print('Ничего не получилось, ибо: '+str(err))
else:
print("Работа скрипта завершена, с ошибками")
Так, как необходимый адрес вместе с именем входного видеофайла path мы будем добавлять через именованный параметр (-p,--path), прямо в командной строке при вызове срипта, то наша команда в итоге будет иметь примерно следующий вид:
не забывая, что мы уже находимся в активированном окружении
python pars_video.py -p /home/username/Videos/dhvid2.mp4
Далее просто выбираем язык и ждём завершения работы программы.
код скрипта также можно взять и здесь https://gitlab.com/pub_ramanzes/pars_video
-
Создано 17.06.2023 15:46:40
-
Roman Sakhno

Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.