Сортировка полей на странице (2D плоскость) сверху вниз, слева направо. (Python)

#python #sorting #opencv #ocr

#python #сортировка #opencv #ocr

Вопрос:

Я пытаюсь отсортировать поля, которые являются результатом OCR engine. Ограничивающие рамки из движка являются случайными и не имеют какой-либо конкретной сортировки. Я хотел бы отсортировать поля слева направо, сверху вниз. Какие поля в одной строке (строке) должны быть отсортированы слева направо, а затем они должны перейти в нижнюю строку (строку) и отсортировать ее слева направо и так далее.

У меня есть все 4 точки каждого прямоугольника (прямоугольника)

Вот несколько примеров изображений.

Пример изображения 1

Пример изображения 2

Пример изображения 3

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

1. Предложение: 1) Вычислите координаты центра каждого поля; 2) Используйте кластеризацию по вертикальной координате, чтобы сгруппировать поля в строке; 3) Отсортируйте строки и отсортируйте поля в каждой строке.

2. Можете ли вы разделить перекрывающиеся блоки? Имеет ли значение сверху вниз верх или центр поля?

3. @Stef Да, я думал о том же подходе, но clustering on vertical co-ordinate это то, что я изо всех сил пытаюсь сделать. Как кластеризировать по оси y? Есть несколько подходов, которые я попробовал, которые берут самый верхний элемент и получают все точки, которые находятся ниже центра выбранной точки, и рассматривают эти точки в одной строке. Удалите выбранные поля и повторите. Но, похоже, это работает не очень хорошо.

4. @SyedAbdul Вы пробовали использовать алгоритмы кластеризации из scikit-learn? Если вы знаете количество строк, то k-means должно быть идеальным для этого. Если вы не знаете количество строк, то иерархическая кластеризация должна работать довольно хорошо. См. scikit-learn.org/stable/modules/clustering.html

5. @SyedAbdul В частности, если вы знаете, что расстояние по вертикали между двумя разными блоками, которые не находятся на одной строке, всегда составляет хотя бы некоторую константу (скажем, 0,5 см), тогда любой из алгоритмов кластеризации из scikit-learn, который принимает distance_threshold параметр, может работать довольно хорошо.

Ответ №1:

Вы можете использовать функцию сортировки, которая является встроенной функцией Python, как показано ниже (этот код предполагает, что прямоугольники хранятся в виде кортежей (x, y, width, height) или (x1, y1, x2, y2):

 from operator import itemgetter, attrgetter
rects = [(8, 10, 10, 10), (0, 5, 10, 10), (0, 0, 10, 10), (1, 10, 10, 10)]

rects = sorted(rects, key=itemgetter(1,0))
 

Результат:

 [(0, 0, 10, 10), (0, 5, 10, 10), (1, 10, 10, 10), (8, 10, 10, 10)]
 

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

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

Ответ №2:

Вот предложение:

  • Сортировка по вертикальной координате центра поля;
  • Два соседних блока в отсортированном списке считаются «на одной строке», если их вертикальное перекрытие превышает 50% высоты первого блока.

Мы будем использовать python sorted и more_itertools.split_when для сортировки, а затем группировать в соответствии с этими критериями.

 from more_itertools import split_when

def get_y_center(b):
    up, down= b[0], b[2]
    return (up   down) / 2

def not_vertically_overlapping(b1, b2):
    up1, down1 = b1[0], b1[2]
    up2, down2 = b2[0], b2[2]
    return down1 < up2 or (down1 - up2) < (up2 - up1) 

def groupbyrow(boxes):
    sorted_boxes = sorted(boxes, key=get_y_center)
    return list(split_when(sorted_boxes, not_vertically_overlapping))
 

Тестирование:

 import random
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors

# GENERATE RANDOM BOXES

centers = [(random.gauss(i, 5),random.gauss(j,0.25)) for i in range(0,40,10) for j in range(0,40,2)]
random.shuffle(centers)
boxes = [(y-(h:=random.gauss(1,0.25)),x-(w:=random.gauss(5,2)),y h,x w) for x,y in centers]

# GROUP BOXES BY ROW

rows = groupbyrow(boxes)

# DRAW BOXES WITH ONE COLOUR PER ROW

def draw_box(box, colour):
    u,l,d,r = box
    xs = [l, r, r, l, l]
    ys = [u, u, d, d, u]
    plt.plot(xs, ys, colour)

colours = list(mcolors.CSS4_COLORS.keys())

for i,row in enumerate(rows):
    for box in row:
        draw_box(box, colours[i])

plt.gca().set_aspect('equal')
plt.show()
 

Поля, окрашенные по строкам

Ответ №3:

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