Цель этого проекта состояла в том, чтобы создать приложение для нотной записи, в котором пользователь может рисовать музыкальные ноты в бесплатном рукописном формате. Нарисованные заметки будут распознаваться моделью ИИ и воспроизводиться пользователю в виде звука.

Первоначальная идея, исследование и реализация

Первоначальная идея заключалась в том, что пользователь мог нарисовать любой действительный нотный лист, настолько сложный, насколько это возможно, включая все доступные символы музыкального языка.

Поскольку проект подпадает под оптическое распознавание музыки (ORM), я начал изучать предыдущую работу по этой теме, а также искал доступные рукописные музыкальные наборы данных.

Я быстро понял, что оптическое распознавание музыки все еще находится на ранней стадии развития. И если добавить к этому мои ограниченные познания в теории музыки, то для того, чтобы я мог представить что-то значимое к сроку проекта, мне нужно было бы значительно снизить сложность проекта.

После некоторых размышлений, дальнейших исследований и обсуждений с моим руководителем Хесусом Хавьером Рейесом Торресом идея окончательной формы проекта была следующей:

  • ИИ будет распознавать символы по одному символу за раз, а это означает, что входными данными для модели будет изображение одного символа. Результатом будет класс этого символа.
  • Поскольку символы, которые соединяются с другими символами (например, последовательные восьмые ноты, соединенные друг с другом через луч), не могут быть легко введены в модель, символы ограничены четвертными нотами, половинными нотами и целыми нотами.

  • Ноты будут воспроизводиться только в до мажоре в фиксированном темпе.
  • Пользователю предоставляется один нотоносец, на котором можно делать заметки. Однако заметки можно рисовать в небольшом окне фиксированного размера.
  • После того, как заметка нарисована, пользователь может отправить ее в модель, где она будет немедленно классифицирована.
  • Определение высоты тона будет осуществляться по отдельному алгоритму.

На следующем изображении показан окончательный пользовательский интерфейс с одним нотоносцем. Синий прямоугольник — это область рисования, которая также является входной для модели ИИ.

Набор данных

Несмотря на то, что можно найти много наборов данных, наиболее полезным из них, который я нашел и решил использовать, был Набор данных рукописных онлайн-музыкальных символов (HOMUS).

Этот набор данных содержит 15200 образцов. Набор данных был создан, когда 100 разных музыкантов нарисовали 32 различных музыкальных символа, каждый символ 4 раза. Их попросили нарисовать символы не идеально, а в их собственном стиле. Некоторые символы были написаны дважды: справа и наоборот.

Каждый сэмпл записывается в отдельный текстовый файл с меткой семпла и определениями его строк. Таким образом, ни один из этих образцов не соответствовал моему изначально ожидаемому формату изображения.

Однако, поскольку я хотел, чтобы входными данными для моей модели было изображение, я написал скрипт, который будет преобразовывать информацию о строке для каждого образца в файл png 64x64.

В результате у меня теперь было 800 изображений четвертных нот, 800 изображений половинных нот и 400 изображений целых нот. Для четвертных и половинных нот у 400 штифт был направлен вверх, а для остальных 400 — вниз.

Предварительная обработка данных

Посмотрев на сгенерированные изображения, я быстро обнаружил проблемы со многими из них. Например, некоторые изображения, изначально помеченные как четвертные ноты, также могут быть классифицированы как половинные.

В свободном рисовании музыкантов многие из них рисовали крошечные кораблики для четвертей и половинных нот…

…или волнистые полуноты…

…или рисовать полуноты со слишком короткими основами, делая их слишком похожими на целые ноты.

Я вручную удалил такие изображения из своего набора данных. После этого у меня осталось 615 четвертных нот, 686 половинных нот и 389 целых нот.

Увеличение данных

1690 сэмплов — это не так много для обучения модели. Это натолкнуло меня на следующую мысль.

Я понял, что окончательный вход в модель — это не изображение 64x64 нарисованной заметки. Это связано с тем, что окно, в котором пользователь может рисовать, имеет размер 64x192. Заметку можно нарисовать где угодно, и она будет различаться по размеру. Это означает, что я не могу полагаться на то, что заметка рисуется в определенной позиции в окне рисования. Это означает, что модель должна уметь распознавать символ, независимо от того, где он расположен.

Подход, который я решил использовать, заключался в том, что для каждых 1690 образцов изображений 64x64, которые я сгенерировал ранее, я буду генерировать nновых изображений размером 64x192, чтобы они соответствовали рисуемому окну. Но для каждого нового изображения 64x192 исходная заметка 64x64 будет смещаться куда-то случайным образом. Таким образом, вместо того, чтобы быть в середине нового изображения, он может быть внизу или вверху или, возможно, охватывать левый или правый край изображения.

В конце концов я решил, что n = 30, а это означает, что на каждые 1690 у меня будет 30 новых изображений. Итак, всего у меня было около 50000 изображений. Это гораздо лучший размер набора данных, чем 1690!

Для этого я снова написал маленький скрипт.

Архитектура и обучение

Сценарий тренировочного процесса можно найти здесь.

Преобразования

  • Оттенки серого
  • Случайный горизонтальный флип
  • Случайное вращение (5 градусов)

Параметры

  • Размер партии = 30
  • Критерий = потеря перекрестной энтропии
  • Оптимизатор = SGD
  • Скорость обучения = 0,001
  • Эпохи = 20

Модель

Модель настолько стандартна, насколько может быть сверточная модель. Сверточные слои используют ядра 3x3. Первый уровень имеет 32 фильтра, а второй — 64. Уровень пула использует ядро ​​2x2. Показатель отсева для обоих слоев отсева составляет 0,5.

Полученные результаты

Первые результаты после 20 эпох уже были действительно отличными!

  • Потеря = 0,0076
  • Точность набора данных проверки = 99,78.

Использование модели

Теперь, когда модель была обучена, мне нужно было использовать ее с пользой. Сначала я создал настольное приложение в Unity, где пользователь мог рисовать в движущемся окне и воспроизводить заметки.

Я экспортировал обученную модель в формат ONNX, который был совместим с облегченной кроссплатформенной библиотекой вывода нейронных сетей для Unity под названием Barracuda.

Сначала модель работала некорректно. Для изображений размером ~‹10 пикселей они классифицировались как целые ноты, а для всего остального всегда классифицировались как четвертные. Я решил, что это произошло из-за того, что размер цифрового пера, который мог использовать пользователь, был слишком большим (3 пикселя в ширину). После изменения ширины на 1 пиксель модель заработала! Я предполагаю, что причина, по которой это было проблемой, заключалась в том, что обученные изображения содержали тонкие линии шириной в 1 пиксель, которые особенно распространены среди целых и половинных нот. Но поскольку начало четвертной ноты состоит из множества линий в 1 пиксель, расположенных близко друг к другу, то любое изображение, в котором много черных пикселей близко друг к другу, классифицируется как четвертные ноты.

Линии персонала

Из первоначальных исследований казалось, что линии нотоносца долгое время было трудно удалить, но теперь есть способы сделать это. Для меня это не было проблемой, поскольку, хотя визуально это выглядит так, как будто изображение, на котором пользователь рисует заметку, содержит линии нотоносца, текстура, которую на самом деле рисует пользователь, не содержит линий нотоносца. Это означает, что я мог бы легко отправить в сеть изображение с белым фоном (добавленным к прозрачному фону непосредственно перед классификацией), содержащим только черные пиксели, которые нарисовал пользователь.

В поисках подачи

Классификация класса нот дает нам только продолжительность ноты. Но нам также нужно знать высоту тона, т. е. высокая это или низкая нота, или что-то среднее.

Это было сделано отдельно без использования ML. Алгоритм принимает на вход изображение 64x192. Затем он перемещает окно размером 8x8 пикселей (первоначально 16x16, но эксперименты дали лучший результат с размером 8) по изображению и вычисляет плотность для каждого окна. Плотность просто означает количество черных пикселей. И, в конце концов, он выбирает окно с наибольшей плотностью.

Окно с наибольшей плотностью считается ближайшим к центру головки ноты. Таким образом, положение окна считается положением нотной головки. А поскольку у меня есть положение нотной головки в пиксельных координатах изображения 64x192, исходя из высоты, я могу предсказать высоту тона.

Это работает полунормально. Если вы хорошо нарисуете ноты, не слишком большие, и расположите их достаточно тщательно, то в большинстве случаев высота будет предсказана правильно. В моих экспериментах с разумными рисунками шаг отклонялся не более чем на 1.

Собираем все вместе

Теперь, когда я смог классифицировать ноты и рассчитать их высоту, остальная часть работы заключалась в том, чтобы заставить систему вводить и удалять ноты. И вдобавок воспроизводить звук нот с правильной высотой тона и продолжительностью.

Для воспроизведения звука я сгенерировал аудиофайлы для каждой возможной комбинации высоты тона и класса: всего 39, так как возможные ноты варьируются от C4 до A5.

Заключение

Несмотря на то, что сложность проекта была значительно снижена, я считаю, что опыт перехода от идеи к реальному рабочему решению был замечательным. Я многому научился на этом пути (многое из этого было связано с предварительной обработкой данных во время этого проекта) и смог пройти через каждую часть конвейера проекта.

Конечный результат не кажется лучшим с точки зрения UX, и есть определенно много направлений для изучения: обнаружение большего количества символов, обнаружение заметок с лучами, улучшение процесса рисования, обеспечение хорошей работы на экранах разных размеров и т. д.

Ресурсы

Проект Unityhttps://gitlab.com/mikson60/musical-notation-project/

Zip, содержащий исполняемый файл (только для Windows с фиксированным размером 1920x1080. Может не работать в другом месте — https://drive.google.com/file/d/1fwpaP0xQAAEn634-tBG3cmIMovIQYi-d/view?usp= обмен

Окончательный расширенный набор данных, используемый для обученияhttps://drive.google.com/file/d/1cQnAsML-d1xdg3-sl9SFLTYfpTQsFEUo/view?usp=sharing