После определённого количества безуспешных попыток написания проекта с нуля и проверки разных подходов, наткнулся на следующую статью: В ней описан подход к распознаванию нот с помощью связки Convolutional и Recurrent сетей. Этот подход используется, в частности, для распознавания текста. Поэтому я решил взять одну из реализаций Optical Character Recognition и обучить сеть на базе нот.
Была выбрана эта tensorflow модель. Научная статья, объясняющая принцип её работы.
Наиболее полная информация касательно датасетов по распознаванию нот находится здесь. Однако, датасетов с размеченной высотой нот, подходящих для моих задач не нашлось. Единственный доступный датасет со звуковысотным маппингом был взят отсюда, у ребят написавших оригинальную статью, ссылка на которую представлена выше. Он представляет из себя сгенерированные в Lilypond изображения с соответствующими лейблами для каждой ноты и знака.
94,984 случайных монофонических последовательностей, состоящих из 52 символов: музыкальных нот от С4 до Е5, 4 длительности (половина, четверь, восьмая, шестнадцатая), 4 паузы тех же длительностей, символы размеров (3/4, 4/4, 6/8), знаки альтерации (диез, бемоль, бекар), скрипичный ключ, тактовая черта.
Поскольку датасет составлен из сгенерированных изображений, распознавание не будет адекватно работать с фотографиями. Также, с нотами, чья структура отличается от характеристик датасета.
Датасет был обработан, чтобы соответстовать требованиям сети для обучения (созданы лейблы, изображения уменьшены и т.д.). 95% - обучающая выборка 5% - тестовая.
Сеть обучалась в течение около 8 часов на NVIDIA Tesla K80. 16 эпох.
1. Сейчас модель может распознавать небольшие последовательности. Ей нельзя скормить весь лист с нотами. Однако, эта проблема решаема. Я пытался сделать сегментацию листа (вычленение тактов) силами OpenCV и добился определённых результатов. Но код очень по-разному работает для изображений разного качества. Поэтому, в долгосрочной перспективе, тут нужно тренировать отдельный слой сети. Для этого нужны данные с разметкой тактов, систем и прочих высокоуровневых элементов системы нотного листа.
2. Не успел сделать конвертацию в MIDI. К сожалению, вопрос в лоб не решить. Думал просто конвертировать обратно в Lilypond но сделать это оказалось не так легко из-за того, что программа сама проставляет знаки альтерации в зависимости от тональности и ещё пары тонкостей. Возможно, тут лучше подойдёт OpenXML, на изучение формата которого, однако, у меня не хватило времени. Всё-таки сделал, читаем ниже.
3. Также не хватило времени на "выкат в продакшен" версии для удобного тестирования без каких-либо установок. Поэтому, чтобы поиграться с распознаванием, нужно поставить https://github.com/emedvedev/attention-ocr и запустить функцию тестирования, это опишу ниже.
4. Также не успел, как следует протестить и собрать статистику. Единичный прогон по тестовой выборке показал результат в 99,5%.
Для быстрой установки под Линукс читаем ниже пункт - Быстрая установка и запуск на Линуксе.
Устанавливаем https://www.tensorflow.org/install/
Устанавливаем https://github.com/emedvedev/attention-ocr
Качаем тестовую выборку: https://github.com/Dene33/notes-recognition/blob/master/notesTest.tfrecords?raw=true
Качаем чекпоинты (это и есть обученная модель) и распаковываем: https://github.com/Dene33/notes-recognition/blob/master/checkpoints.zip?raw=true
Запускаем:
aocr test --visualize ПУТЬ/К/notesTest.tfrecords --log-path ./log/log.log --max-width 1000 --max-height 61 --max-prediction 36 --full-ascii --model-dir ПУТЬ/К/checkpoints
Небольшое примечание. Распакованные checkpoints.zip содержат папку checkpointsDL - путь нужно указывать к ней.
Наблюдаем за процессом. А потом смотрим на результат в папке out. Там будут находиться папки для каждой картинки, для которой было проведено распознавание. В каждой папке файл word.txt где первая строка - предсказанный результат, вторая - ground-truth Также в папке находится гифка, показывающая процесс работы нейросети.
Для перевода результата в читаемый формат (переименование папок и файлов), кидаем питоновский скрипт в папку out и запускаем.
Для проигрывания MIDI, кидаем в ту же папку этот скрипт и запускаем (важно, перед этим нужно запустить скрипт для перевода файлов в читаемый формат). В каждой папке для всех распознанных изображений скрипт создаст .py файл, запустив который проиграется MIDI. Этот .py файл также создаст .mid файл. Ограничения: не учитывается тональность, из-за чего некоторые ноты могут в итоге звучать неправильно (тут нужно более развёрнутое пояснение, которое я как-нибудь напишу).
pip install mido
pip install python-rtmidi
sudo pip install aocr
pip install tensorflow
pip install midogit clone https://github.com/Dene33/notes-recognition
cd notes-recognition
unzip checkpoints.zip -d checkpointsaocr test --visualize notesTest.tfrecords --max-width 1000 --max-height 61 --max-prediction 36 --full-ascii --model-dir ./checkpoints/checkpointsDL
По поводу перевода результата в читаемый формат и проигрывания MIDI смотрим выше.
Чтобы протестировать на своих картинках, создаём папку с картинками. Максимальная высота картинок - 60, ширина - 1000. Создаём текстовый файл (например labels.txt) с лейблами такого формата:
./datasets/images/hello.jpg hello ./datasets/images/world.jpg world
где ./datasets/images/hello.jpg - путь до картинки, hello - её лейбл. Какому символу какая нота соответствует можно посмотреть тут, где 1 столбец - лейблы, 2 - соответствующая нота или знак.
Затем, чтобы создать tfrecord из картинок выполняем:
aocr dataset ПУТЬ/К/labels.txt ПУТЬ/К/МОДЕЛИ/testing.tfrecords
Запускаем:
aocr test --visualize ПУТЬ/К/testing.tfrecords --log-path ./log/log.log --max-width 1000 --max-height 61 --max-prediction 36 --full-ascii --model-dir ПУТЬ/К/checkpoints
Примеры картинок из обучаемой выборки и соответствующих лейблов:
Ссылка на .tfrecord который использовался для обучения модели: https://drive.google.com/file/d/1wd716Lg6Uz5Vxr6x-vEeoLKMb09YapPL/view?usp=sharing
1. Экспорт SaveModel для Tensorflow
2. Конвертация в MIDI
3. Сегментация листа на такты и обратная сборка всей структуры после прохода распознаванием по каждому сегменту
4. Поднять Tensorflow Serving
5. Разметка датасета под распознавание фотографий и более широкого спектра символов
ETH-wallet: 0x136f0a3d6f3Db9db3b742153a396B8b47721Bb24