本文实现如何使用OpenCV对点集从左上到右下排序。本文使用的案例图片如下:
需要实现的效果如下:
1.分阶段实现
让我们看看考虑4行的工作流。我考虑的是沿着y轴将图像分成4段,形成4行。对于图像的每一段,找出每一个以该段为中心的形状。最后,按每个线段的x坐标排列形状。
1.1导入必要的库:
import cv2
import numpy as np
1.2.定义一个辅助函数
它将接收一个图像输入,并将处理后的图像返回给某个对象,以便python稍后检索它们的轮廓:
def process_img(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_canny = cv2.Canny(img_gray, 100, 100)
kernel = np.ones((2, 3))
img_dilate = cv2.dilate(img_canny, kernel, iterations=1)
img_erode = cv2.erode(img_dilate, kernel, iterations=1)
return img_erode
1.3.定义一个返回轮廓中心的函数:
def get_centeroid(cnt):
length = len(cnt)
sum_x = np.sum(cnt[..., 0])
sum_y = np.sum(cnt[..., 1])
return int(sum_x / length), int(sum_y / length)
1.4.定义获取所有中心点的函数
它接收处理后的图像,并返回图像中找到的形状的中心点:
def get_centers(img):
contours, hierarchies = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
for cnt in contours:
if cv2.contourArea(cnt) > 100:
yield get_centeroid(cnt)
1.5.定义一个获取每行中心点的函数
该函数接受图像img、中心点坐标centers、图像的段数row_amt和图像高度row_h作为输入。它将返回row_amt
数组(根据它们的x
坐标排序),每个数组都包含位于图像对应行中心的每个点:
def get_rows(img, centers, row_amt, row_h):
centers = np.array(centers)
d = row_h / row_amt
for i in range(row_amt):
f = centers[:, 1] - d * i
a = centers[(f < d) & (f > 0)]
yield a[a.argsort(0)[:, 0]]
1.6.实现中心点提取
读取图像,使用定义的处理函数得到其处理后的形状,使用定义的中心函数得到图像中每个形状的中心:
img = cv2.imread("shapes.png")
img_processed = process_img(img)
centers = list(get_centers(img_processed))
1.7.定义相关变量
获取图像的高度,用于定义的get_rows函数,并定义一个计数变量count:
h, w, c = img.shape
count = 0
1.8.可视化结果
循环遍历被分为4行的形状的中心,画一条线连接这些行,以便显示:
for row in get_rows(img, centers, 4, h):
cv2.polylines(img, [row], False, (255, 0, 255), 2)
for x, y in row:
1.9.在每个中心点上计标号
count += 1
cv2.circle(img, (x, y), 10, (0, 0, 255), -1)
cv2.putText(img, str(count), (x - 10, y + 5), 1, cv2.FONT_HERSHEY_PLAIN, (0, 255, 255), 2)
1.10.显示
cv2.imshow("Ordered", img)
cv2.waitKey(0)
2.完整代码
import cv2
import numpy as np
def process_img(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_canny = cv2.Canny(img_gray, 100, 100)
kernel = np.ones((2, 3))
img_dilate = cv2.dilate(img_canny, kernel, iterations=1)
img_erode = cv2.erode(img_dilate, kernel, iterations=1)
return img_erode
def get_centeroid(cnt):
length = len(cnt)
sum_x = np.sum(cnt[..., 0])
sum_y = np.sum(cnt[..., 1])
return int(sum_x / length), int(sum_y / length)
def get_centers(img):
contours, hierarchies = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
for cnt in contours:
if cv2.contourArea(cnt) > 100:
yield get_centeroid(cnt)
def get_rows(img, centers, row_amt, row_h):
centers = np.array(centers)
d = row_h / row_amt
for i in range(row_amt):
f = centers[:, 1] - d * i
a = centers[(f < d) & (f > 0)]
yield a[a.argsort(0)[:, 0]]
img = cv2.imread("shapes.png")
img_processed = process_img(img)
centers = list(get_centers(img_processed))
h, w, c = img.shape
count = 0
for row in get_rows(img, centers, 4, h):
cv2.polylines(img, [row], False, (255, 0, 255), 2)
for x, y in row:
count += 1
cv2.circle(img, (x, y), 10, (0, 0, 255), -1)
cv2.putText(img, str(count), (x - 10, y + 5), 1, cv2.FONT_HERSHEY_PLAIN, (0, 255, 255), 2)
cv2.imshow("Ordered", img)
cv2.waitKey(0)
3.结果展示
下面是当你指定4行时的编号图像:
下面是指定6行时的编号图像:
参考目录
https://stackoverflow.com/questions/66946804/python-sorting-items-from-top-left-to-bottom-right-with-opencv