按序输出方形位置信息

1. 需求

现在有这样一个需求:

如下图,将图中的方框的信息(如每本书的书名)输出,且顺序为从左到右、从上到下。已知每个方框的左上角、右下角坐标。

而且这两点坐标由上层代码给出,此处仅处理本需求,不涉及上层代码,故此处不贴出源代码,仅展示上层提供的数据。

上层检测代码给我们提供了每个方框左上角及右下角的坐标,用一个列表box表示。并将所有box装在一个列表box_list里:

1
2
3
4
5
6
7
8
box = [x1,y1,x2,y2,name]
box_list = [
[x1,y1,x2,y2,name],
[x1,y1,x2,y2,name],
[x1,y1,x2,y2,name],
[x1,y1,x2,y2,name],
[x1,y1,x2,y2,name],
]

对应坐标位置关系如下图所示,name代表该方形名称。

最终的输出结果示例:

1
2
3
4
5
1行第1列:box1
1行第2列:box2
2行第1列:box3
2行第2列:box4
2行第3列:box5

2. 方法一

2.1 想法

看到这个问题,我的第一个想法(也是最笨的方法)就是:将每个点的坐标进行比较。

具体来说就是先找到y坐标最小的点,以该点所在框为基准,循环判断每一个框的y坐标值,若他们的差的绝对值在一定范围则划定为同一行

实现这个想法需要考虑的问题有:

  • 该差值是基准框与其他框的上边框还是下边框比较
  • 基准框与其他框上下边框位置关系。不同的位置关系可能导致不同的结果,即在不在同一行的判断。

例如下面这种情况,A方框到底是属于第一行还是第二行呢?用几个方框的上下边框y坐标显然很难表达。

正是由于过于复杂、过于笨拙,遂很快放弃这种方案。

3. 方法二

3.1 想法

既然同时用上下边角坐标比较计算过于拙劣,那么简化一下,用他们的平均值坐标,也就是利用方形的重心,不就可以大大降低难度!而且准确率也比自己枚举法列出的情况高得多。

3.2 准备工作

获取到重心坐标后,对x、y方向坐标进行排序是必须的。对列表进行排序比较容易想到的是利用sort()函数。但单纯调用sort()默认是对列表第一个数进行排序,仍不能达到我们的目标,故需要进行一些处理。

利用sort函数的key属性,我们可以给sort传递一个函数,只要这个函数返回列表对应位数,就可以对指定位数进行排序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 获取列表的第二个元素
def takeSecond(elem):
return elem[1]
# 获取列表的第一个元素
def takeSum(elem):
return elem[0]+elem[1]


# 对列表第二个数排序(从小到大)
box_list.sort(key=takeSecond)
print(box_list)
# 对列表第一、二个数的和排序
box_list.sort(key=takeSum)
print(box_list)

3.3 代码实现

首先写上获取方形重心坐标函数.

计算得出重心x、y坐标后加入在原列表5、6号位,方便使用

1
2
3
4
5
6
def getBarycenter(box_list):
num = 0
for box in box_list:
box_list[num].append(int((box[0]+box[2])/2))
box_list[num].append(int((box[1]+box[3])/2))
num += 1

然后将同一行按序放在一个列表里

先对重心y坐标排序,以最小的那个方形为基础,判断谁与之同行。只需循环遍历判断两个重心y坐标差值绝对值是否在一个误差区间内即可。这个误差需要根据实际情况进行调整

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 获取重心x坐标
def takeXC(elem):
return elem[5]
# 获取重心y坐标
def takeYC(elem):
return elem[6]

# 获取一行
def getBox(box_list,dev):
print_list = []
box_list.sort(key=takeYC)
for box in box_list:
if abs(box[6] - box_list[0][6]) <= dev:
print_list.append(box)

print_list.sort(key=takeXC)
for box in print_list:
box_list.remove(box)
return print_list

接着循环调用上面的函数直至排序完

而且将每行都装在一个列表里,方便后面打印信息

1
2
3
4
5
6
7
# 从左到右从上到下
def getSortBoxs(box_list,dev=20):
getBarycenter(box_list)
sort_boxs = []
while box_list != []:
sort_boxs.append(getBox(box_list,dev))
return sort_boxs

最后将信息打印出来

1
2
3
4
5
# 打印
def printBoxs(result):
for line_boxs,i in zip(result,range(1,len(result)+1)):
for box,j in zip(line_boxs,range(1,len(line_boxs)+1)):
print(f'第{i}行第{j}列:' + str(box[4]))

至此,我们可以测试一下。

假设有一个这样的列表:

1
2
3
4
5
6
7
8
9
box_list = [
[50,50,70,90,1],
[90,110,130,160,2],
[10,120,40,150,3],
[60,10,80,40,4],
[90,70,120,110,5],
[20,60,40,100,6],
[50,110,70,130,7],
]

他的图形大致为如下所示:

完整代码如下

输出结果:

至此需求解决。

4. 方法三

4.1 想法

待续…

4.2 代码实现

待续…


按序输出方形位置信息
https://huihui486.github.io/2023/02/04/Python/按序输出方形位置信息/
作者
灰灰
发布于
2023年2月4日
许可协议