从上到下,从左到右对页面(2D 平面)上的框进行排序。 (Python)

Posted

技术标签:

【中文标题】从上到下,从左到右对页面(2D 平面)上的框进行排序。 (Python)【英文标题】:Sorting Boxes on a Page(2D Plane) top to bottom, left to right. (Python) 【发布时间】:2022-01-09 20:38:24 【问题描述】:

我正在尝试对 OCR 引擎生成的框进行排序。来自引擎的边界框是随机的,没有任何特定的排序。我想从左到右,从上到下对盒子进行排序。哪个是同一行(行)中的框应该从左到右排序,然后它应该到行(行)下面并从左到右排序等等。

我拥有每个矩形(框)的所有 4 个点

这里是一些示例图片。

Sample Image 1

Sample Image 2

Sample Image 3

【问题讨论】:

建议:1)计算每个框的中心坐标; 2)在垂直坐标上使用聚类对每行的框进行分组; 3)对行进行排序,对每一行的框进行排序。 你能分开重叠的盒子吗?从上到下是否与框的顶部或中心相关? @Stef 是的,我想到了同样的方法,但clustering on vertical co-ordinate 是我正在努力做的事情。如何在 y 轴上聚类?我尝试了一些方法,即采用最顶部的元素,并获取位于所选点中心以下的所有点,并将这些点考虑在同一条线上。删除选定的框并重复。但它似乎效果不佳。 @SyedAbdul 您是否尝试过使用 scikit-learn 的聚类算法?如果你知道行数,那么 k-means 应该是完美的。如果您不知道行数,那么层次聚类应该可以很好地工作。见scikit-learn.org/stable/modules/clustering.html @SyedAbdul 特别是,如果您知道不在同一条线上的两个不同框之间的垂直距离始终至少是某个常数(例如 0.5 厘米),那么 scikit 中的任何聚类算法-learn 接受distance_threshold 参数可以很好地工作。 【参考方案1】:

你可以使用sorted函数,它是Python的内置函数,如下(这段代码假设rects被存储为(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)]

【讨论】:

盒子不是直线,就像你举的例子是所有的y轴都是直线并且盒子的大小相同。但在我的情况下,盒子的大小都不同,盒子的中心点也不在一条直线上。所以直接排序的功能是行不通的。【参考方案2】:

这是一个命题:

按框中心纵坐标排序; 如果两个在排序列表中相邻的框的垂直重叠大于第一个框高度的 50%,则认为它们“在同一行”。

我们将使用 python 的sortedmore_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】:

您能否总结一下您正在考虑对盒子进行排序的规则?您想在 Zigzag 订单扫描中对盒子进行排序吗?无论如何,如果您想应用特定规则,您可以将自定义的 Comparator 函数传递给 sorted 函数。

【讨论】:

以上是关于从上到下,从左到右对页面(2D 平面)上的框进行排序。 (Python)的主要内容,如果未能解决你的问题,请参考以下文章

CSS 列,从上到下然后从左到右

如何在从左到右和从上到下排序的二维数组中搜索数字?

如何在 Flutter 中创建从左到右或从上到下的叠加飞溅动画

从上到下打印二叉树,同节点的从左到右。

从上到下按层打印二叉树,每层打印顺序从左到右

关于小班从上到下,从左到右自动编号问题