Страницы

Поиск по вопросам

четверг, 11 июля 2019 г.

Как удалить нотоносец в задаче распознавания нотных знаков?

Имеется изображение какой-либо музыкальной композиции. Требуется произвести распознавание знаков, однако мешают нотные линии. Поиск контуров посредством findContours() выделяет всё подряд так, что в итоге нотные знаки никак не разделяются. Как преодолеть?


Ответ

Можно использовать морфологические операции. У нотных линий имеется одно существенное отличие, которое позволяет отделить их от любых других объектов, - это их ширина. Она фактически приближается к ширине самого изображения. Этим и нужно воспользоваться.
Но сначала необходимо произвести бинаризацию изображения:
// Загружаем изображение. cv::Mat src_mat = cv::imread("allegretto.png", cv::IMREAD_GRAYSCALE); if(src_mat.empty()) return;
// Собственно бинаризация. cv::Mat bin_mat; cv::threshold(src_mat, bin_mat, 0, 255, cv::THRESH_BINARY_INV | cv::THRESH_OTSU);
Флаг THRESH_BINARY_INV используется, чтобы поменять цвета переднего плана и фона. Знаки станут белыми, а фон - чёрным. Флаг THRESH_OTSU удобен тем, что автоматически подбирает порог для бинаризации. Получится вот такое изображение:

Теперь можно перейти к морфологии. Создаём матрицу структурного элемента с тем расчётом, чтобы по горизонтали оно сравнялось с половиной ширины исходного изображения. Применяем эрозию и дилатацию:
cv::Mat hrz_krn_mat = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(bin_mat.cols/2,1));
cv::Mat hrz_mph_mat; cv::morphologyEx(bin_mat, hrz_mph_mat, cv::MORPH_ERODE, hrz_krn_mat); cv::morphologyEx(hrz_mph_mat, hrz_mph_mat, cv::MORPH_DILATE, hrz_krn_mat);
В принципе две последние строки можно заменить на использование флага MORPH_OPEN (операция открытия), которая выполнит тоже самое: вызовет сначала эрозию, а затем дилатацию:
cv::Mat hrz_mph_mat; cv::morphologyEx(bin_mat, hrz_mph_mat, cv::MORPH_OPEN, hrz_krn_mat);
В результате получится:

Фактически, получена маска нотных линий. Однако просто так нельзя её вычесть из исходного бинаризованного изображения. Если это осуществить, то поскольку каждая из линий лежит на множестве нотных знаков, последние отчасти потеряют свои пиксели и результат окажется далёким от желаемого.
Необходимо получить маску нотных знаков таким образом, чтобы ликвидировать всякое присутствие нотоносца:
cv::Mat vrt_krn_mat = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(1,3));
cv::Mat vrt_mph_mat; cv::morphologyEx(bin_mat, vrt_mph_mat, cv::MORPH_OPEN, vrt_krn_mat);
Как хорошо заметно на следующем изображении, маска нотных знаков не содержит больших горизонтальных линий, но в то же время часть важных деталей исходного изображения также было потеряно:

Но теперь стало возможно создать маску нотных линий с разрезами в местах присутствия нотных знаков:
cv::Mat sbt_mat; cv::subtract(hrz_mph_mat, vrt_mph_mat, sbt_mat);

Остаётся лишь изъять маску нотных линий из исходного бинаризованного изображения:
cv::Mat dst_mat = cv::Mat::zeros(bin_mat.size(), CV_8U); bin_mat.copyTo(dst_mat, ~sbt_mat);
Теперь изображение можно пустить на вход findContours() с тем, чтобы получить контуры каждого из нотных знаков в отдельности.

Комментариев нет:

Отправить комментарий