Время прочтения: 5 мин.

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

Идея обратиться к идентификации геометрических объектов на изображении родилась при решении задачи поиска фальсификата в pdf файлах. В период пандемии многие организации перешли на особый режим работы, личное взаимодействие с клиентами снизилось, поменялись бизнес-процессы. Изменения коснулись оформления первичных документов, чаще стали встречаться скан-копии файлов со вставленными подписями и печатями в виде отдельных картинок. Для оценки объема и формирования пула соответствующих документов потребовалось создать инструмент, способный «просмотреть» более 20 Гбайт файлов на предмет наличия указанных отклонений.

Задача:

Идентификация наличия круглых печатей на странице сканированного документа.

Реализация:

Python OpenCV (версия 4.6.0) – open source библиотека компьютерного зрения.

Библиотеки Python:

  • numpy – базовые методы для манипуляции с данными;
  • matplotlib.pyplot – демонстрация изображений;
  • imutils – загрузка, сохранение и обработка изображений;
  • os – взаимодействие с операционной системой.

На практике при работе с изображениями сталкиваются с проблемой обнаружения плоских геометрических фигур: прямые, окружности или эллипсы. Преобразование Хафа, разработанное в середине 20го века, является эффективным инструментом решения подобных задач. Метод описывает параметры искомых объектов и осуществляет их поиск на изображении.

Понадобится:

Функция для корректировки разрешения изображения не теряя пропорций, чтобы любое изображение привести к выбранным постоянным размерам:

#Параметры:
#image – изображение в виде массива байтов
#w_set – новая ширина
#h_set – новая высота
#int_set – алгоритм интерполяции
def  set_image_size (image, w_set = None, h_set = None, int_set= cv2.INTER_LINEAR):
    dimension = None 
    (h_img, w_img) = image.shape[:2]
    if w_set is None and h_set is None: 
        return image
    else:
        dw = w_set / float(w_img)
        dimension = (w_set, int(h_img * dw))
    result = cv2.resize(image, dimension, interpolation = int_set)
    return result

Функция для показа изображения:

#Параметры:
#image – изображение в виде массива байтов
#size – размер холста
def show_image (image, axis = False, size = (12, 12)): 
    plt.figure(figsize = size)
    if not axis: plt.axis('off')
    if len(image.shape) == 3: 
        plt.imshow(image[:, :, ::-1]) 
    else: 
        plt.imshow(image, cmap='gray')
    plt.show()

Преобразование Хафа:

Алгоритмы преобразования Хафа уже реализованы в библиотеке OpenCV и требуют лишь аккуратного и уместного использования. Качество входных данных существенно влияет на эффективность решения задачи распознавания фигур, поэтому преобразуемые изображения не должны быть сильно зашумлены. Исходные изображения будут разного качества, размера, цвета. Потребуется привести их к единому формату — 8-битный, фиксированного размера, одноканальный, имеющий градацию серого. Уберем шумы и четче выделим края объектов, для более простых вычислений в преобразовании функции HoughCircles от OpenCV:

file = 'ex1-000001.png'
#приводим изображение к единому размеру сохраняя пропорции
src = set_image_size(cv2.imread(file), 2480, 3507)
#конвертируем изображение в одноканальное с градацией серого
gray_img = cv2.cvtColor(src.copy(), cv2.COLOR_BGR2GRAY)
#применяем фильтры - уменьшаем нежелательный шум, сохраняя четкие края
blurred_gray_img = cv2.bilateralFilter(gray_img.copy(), 15, 10, 10)
gray_final = cv2.medianBlur(blurred_gray_img, 5)

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

img_rows = gray_final.shape[0]
plt.axis('off')
#функция преобразования Хафа
imgcircles = cv2.HoughCircles(gray_final, cv2.HOUGH_GRADIENT, 1, img_rows/8, param1=100, param2=85, minRadius=145, maxRadius=275)
if imgcircles is not None:
        imgcircles = np.uint16(np.around(imgcircles))
        for i in imgcircles[0, :]:
            center = (i[0], i[1])
            cv2.circle(src, center, 1, (0, 100, 100), 3)
            radius = i[2]
	#отрисовываем найденную окружность
            cv2.circle(src, center, radius, (255, 0, 191), 3)
 show_image(src)

Отмечу, что алгоритм преобразования Хафа идентифицирует круги на изображении. В обрабатываемых файлах круглые объекты — это печати, поэтому строгая идентификация внешнего контура печати не обязательна. Найденными считаем печати, в которых алгоритм выделил контур круга. Например, так:

Удачно подобранные параметры функции HoughCircles позволили эффективно подсвечивать наличие круглых печатей на изображении. Удалось добиться следующих показателей при условии, что успешным считалась идентификация всех печатей в скан-файле:

Из недостатков можно выделить следующее:

  • Для корректной работы все страницы документа должны быть в книжной ориентации или приведены к ней;
  • OpenCV не очень любит бледные печати:
  • Алгоритм видит ложные печати на документах с узорами (свидетельства и подобное) и видит ложные окружности на таблицах с мелкими ячейками.

Во всех остальных случаях печати успешно идентифицируются, в том числе на мятых документах и поверх текста.

Заключение:

Реализованный инструмент продемонстрировал отличный алгоритм – преобразование Хафа, применяемое для анализа изображений и компьютерного зрения на примере решения прикладной задачи. Проект реализован на языке Python c помощью библиотеки OpenCV. Подобные решения имеют низкий порог входа и высокую результативность, рекомендую к использованию!