对于目标检测任务中对某一类的检测结果进行输出的时候,一般都是无序的,很明显这样子很难满足的我们的需求,我们更喜欢他是这样子输出的:
👇
我们可以看到——”按顺序输出结果“中的字段是完美的和上面图片中的识别结果一一对应的,这其实也很容易做到。
我们只需要把这些个矩形框一个个抽出来,放在一张白纸上面,如下图所示:
NOTE:下图是模拟的复杂样例,并非上图中的矩形框抽出来。
红色箭头代表的就是正确的排序方式,看到这里有一些朋友可能已经有了思路,我这边使用了数据结构算法中的图论算法,我们可以把上面四个箭头当作四层, 每一层从根节点也就是最左边的矩形框出发进行连边操作,每一次连边必须满足以下要求:
- 连边的矩形框与上一个节点的Iou!=0。
- 连边的矩形框的质心必须处于上一个节点质心的右边。
- 连边的矩形框的质心必须与上一个节点的质心有着距离最近的关系。
根据这些约束条件,我们可以选取最左上的节点当作第一层的根节点,开始我们的连边操作:
当最有一个节点的最右边已经没有了节点的时候,至此我们结束了第一层搜索,删掉第一层连接的所有矩形框,进入第二层搜索。由于删掉了第一层所有的矩形框,第二层的第一个节点又变成了最左上角的节点,选取她开始第二轮的遍历连边操作。
以此类推,我们便完成了所有排序的工作:
放上一些彩蛋图:
🍉参考实现代码如下:
from collections import deque
import mathdef draw(points):import matplotlib.pyplot as plt# 中心点坐标# points = [(67.0, 72.0), (172.5, 70.0), (274.5, 79.5), (380.0, 79.0), (489.5, 149.5), (73.5, 241.0), # (185.0, 249.0), (386.5, 239.0), (604.5, 128.5), (719.5, 151.0), (209.5, 403.0), (374.0, 393.0), # (180.5, 562.0), (288.5, 568.5), (394.5, 573.5)]# 提取 x 和 y 坐标x_coords, y_coords = zip(*points)# 创建一个散点图plt.scatter(x_coords, y_coords, marker='o', color='blue')# 添加坐标标签for i, point in enumerate(points):plt.annotate(f"{i, point}", (x_coords[i], y_coords[i]), textcoords="offset points", xytext=(0,10), ha='center', fontsize=8)# 设置图的标题和坐标轴标签plt.title("centre plot")plt.xlabel("X")plt.ylabel("Y")# 设置坐标轴原点为左上角plt.gca().invert_yaxis()# 显示图plt.grid()plt.show()plt.savefig("switch_sort.jpg",dpi=300)def convert_to_center(xmin, ymin, xmax, ymax):center_x = (xmin + xmax) / 2.0center_y = (ymin + ymax) / 2.0return (int(center_x),int(center_y))def cal_dist(point1, point2):x1, y1 = point1x2, y2 = point2distance = math.sqrt((x2 - x1)**2 + (y2 - y1)**2)return distancedef cal_xIou(ret1, ret2):x1,y1 = convert_to_center(*ret1)x2,y2 = convert_to_center(*ret2)xmin1,ymin1,xmax1,ymax1 = ret1xmin2,ymin2,xmax2,ymax2 = ret2# print(ymin1, ymin2, ymax1, ymax2)if ymax1 < ymin2 or ymax2 < ymin1:return Falseelse:return True# def find_right_shortestPoint(centres_coordinates, now_point):
# shortest_dist = float('inf')
# for iter_point in centres_coordinates:
# dist = cal_dist(now_point, iter_point)
# if dist < shortest_dist and dist != 0:
# if now_point[1] < iter_point[1]:
# return iter_point
# return ValueError("找不到距离最近的框,是不是总共只有一个框!")def find_shortestPoint(bboxes, centres_coordinates, backup_centres_coordinates, now_point, seen):shortest_dist = float('inf')target=(-1,-1)for iter_point in centres_coordinates:if iter_point in seen:continue# print(now_point, iter_point)if now_point[0] < iter_point[0]:dist = cal_dist(now_point, iter_point)# print(dist)if dist < shortest_dist and dist != 0:nowPoint_idx = backup_centres_coordinates.index(now_point)iterPoint_idx = backup_centres_coordinates.index(iter_point)nowBbox = bboxes[nowPoint_idx]iterBbox = bboxes[iterPoint_idx]# print(nowBbox, iterBbox)if not cal_xIou(nowBbox, iterBbox):continueshortest_dist = disttarget = iter_point return targetdef main_layers(bboxes):#[ [xmin,ymin,xmax,ymax]]backup_bboxes = bboxes.copy()centres_coordinates = []for bbox in bboxes:centre = convert_to_center(*bbox)centres_coordinates.append(centre)length_data = len(bboxes)backup_centres_coordinates = centres_coordinates.copy()# backup_centres_coordinates = centres_coordinates.copy()max_h, max_w = 0, 0for cc in centres_coordinates:max_h = max(max_h, cc[0])max_w = max(max_h, cc[1])# draw(centres_coordinates) grap = [[0 for _ in range(int(max_w + 10))] for _ in range(int(max_h) + 10)]for cen in centres_coordinates:cen_x, cen_y = int(cen[0]), int(cen[1])grap[cen_x][cen_y] = 1result = {}queue = []seen = set()start_point = min(centres_coordinates, key=lambda point: (point[0], -point[1]))queue.append(start_point)seen.add(start_point)layer = 1iter_idx = 1print("Now Layer:{} start point:{}".format(layer, start_point))while iter_idx <= length_data:now_point = queue[-1]iter_point = find_shortestPoint(backup_bboxes, centres_coordinates, backup_centres_coordinates,now_point, seen)judge=Trueif iter_point[0]==-1:judge = Falseif judge:queue.append(iter_point)iter_idx += 1seen.add(iter_point)else:result[layer] = queueif iter_idx == length_data:breaklayer += 1for q in queue:# print(q) # print(centres_coordinates)# backup_bboxes.remove(centres_coordinates.index(q))centres_coordinates.remove(q)queue = []seen = set()start_point = min(centres_coordinates, key=lambda point: (point[0], -point[1]))queue.append(start_point)seen.add(start_point)iter_idx += 1print("Now Layer:{} start point:{}".format(layer, start_point))sorted_dict = dict(sorted(result.items(),key=lambda item: item[1][0][1]))for key in sorted_dict:print(key, sorted_dict[key])return sorted_dict
🍉使用方法:
bboxes = [[box[0], box[1], box[0]+box[2], box[1]+box[3]] for box in used_res]sorted_dict = main_layers(bboxes)centres_coordinates = []for bbox in bboxes:centre = convert_to_center(*bbox)centres_coordinates.append(centre)sorted_used_res = []for layer in sorted_dict:for iter_column in sorted_dict[layer]:iter_index = centres_coordinates.index(iter_column)sorted_used_res.append(used_res[iter_index])