Python:使用 OpenCV 从左上角到右下角对项目进行排序
Posted
技术标签:
【中文标题】Python:使用 OpenCV 从左上角到右下角对项目进行排序【英文标题】:Python: Sorting items from top left to bottom right with OpenCV 【发布时间】:2021-06-30 22:45:08 【问题描述】:如何尝试从左上角到右下角对图片的项目进行排序,如下图所示?目前收到此错误,代码如下。
错误:
a = sorted(keypoints, key=lambda p: (p[0]) + (p1))[0] # 找到左上角 ValueError:具有多个元素的数组的真值不明确。使用 a.any() 或 a.all()
本题仿照:Ordering coordinates from top left to bottom right
def preprocess(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_blur = cv2.GaussianBlur(img_gray, (5, 5), 1)
img_canny = cv2.Canny(img_blur, 50, 50)
kernel = np.ones((3, 3))
img_dilate = cv2.dilate(img_canny, kernel, iterations=2)
img_erode = cv2.erode(img_dilate, kernel, iterations=1)
return img_erode
image_final = preprocess(picture_example.png)
keypoints, hierarchy = cv2.findContours(image_final, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
points = []
while len(keypoints) > 0:
a = sorted(keypoints, key=lambda p: (p[0]) + (p[1]))[0] # find upper left point
b = sorted(keypoints, key=lambda p: (p[0]) - (p[1]))[-1] # find upper right point
cv2.line(image_final, (int(a.pt[0]), int(a.pt[1])), (int(b.pt[0]), int(b.pt[1])), (255, 0, 0), 1)
# convert opencv keypoint to numpy 3d point
a = np.array([a.pt[0], a.pt[1], 0])
b = np.array([b.pt[0], b.pt[1], 0])
row_points = []
remaining_points = []
for k in keypoints:
p = np.array([k.pt[0], k.pt[1], 0])
d = k.size # diameter of the keypoint (might be a theshold)
dist = np.linalg.norm(np.cross(np.subtract(p, a), np.subtract(b, a))) / np.linalg.norm(b) # distance between keypoint and line a->b
if d/2 > dist:
row_points.append(k)
else:
remaining_points.append(k)
points.extend(sorted(row_points, key=lambda h: h.pt[0]))
keypoints= remaining_points
新图片:
参考订购图片:
将使用质心来确定中心点排序。
【问题讨论】:
为什么会被否决?努力学习 底部的皱眉脸不容易被识别为一体! 【参考方案1】:生成的编号取决于您希望有多少行。使用我将向您展示如何制作的程序,您可以在运行程序之前指定行数。
例如,这里是原图:
这是指定 4 行时的编号图像:
这是指定 6 行时的编号图像:
对于您提供的另一张图片(其框架被裁剪,因此该框架不会被检测为形状),您可以看到将有 4 行,因此将 4 放入程序将为您提供:
让我们看一下考虑到 4 行的工作流程。我使用的概念是将图像沿 y 轴分成 4 段,形成 4 行。对于图像的每个片段,找到每个以该片段为中心的形状。最后,按 x 坐标对每个段中的形状进行排序。
-
导入必要的库:
import cv2
import numpy as np
-
定义一个函数,该函数将接受图像输入并将处理后的图像返回到允许 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
-
定义一个返回轮廓中心的函数:
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)
-
定义一个函数,该函数将接收图像数组
img
、坐标数组centers
、图像的段数row_amt
,以及每个段的高度row_h
,作为输入。它将返回 row_amt
数组(按它们的 x 坐标排序),每个数组都包含 centers
中位于图像对应行中的每个点:
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]]
-
读入图像,使用定义的
processed
函数获取其处理后的形式,并使用定义的centers
函数获取图像中每个形状的中心:
img = cv2.imread("shapes.png")
img_processed = process_img(img)
centers = list(get_centers(img_processed))
-
获取图像的高度以用于定义的
get_rows
函数,并定义一个计数变量count
来跟踪编号:
h, w, c = img.shape
count = 0
-
循环穿过分为 4 行的形状中心,绘制连接行的线以进行可视化:
for row in get_rows(img, centers, 4, h):
cv2.polylines(img, [row], False, (255, 0, 255), 2)
for x, y in row:
-
添加到
count
变量,并从row
数组中将count
绘制到图像上的特定位置:
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)
总共:
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)
【讨论】:
谢谢,你能更深入地了解这三行吗? f = 中心[:, 1] - d * i a = 中心[(f 0)] 产生 a[a.argsort(0)[:, 0]] 我们想要找到centers
数组中位于i
行内的所有点。 f = centers[:, 1] - d * i
返回 centers
数组,其中每个 y 坐标减去图像顶部和 i
行顶部之间的距离。所以基本上它就像i
行向上移动直到它触及图像的顶部。使用移位的图像,我们可以简单地检查点是否位于图像顶部和行的高度内,因此a = centers[(f < d) & (f > 0)]
。
a.argsort(0)
返回 a 的索引,其中 x 坐标和 y 坐标已排序。由于我们只想按 x 坐标对行进行排序,因此我们使用切片 [:, 0]
,这意味着 0
列中的所有行。所以a.argsort(0)[:, 0]
是索引数组,yield a[a.argsort(0)[:, 0]]
产生按 0 列排序的行。我意识到a[a.argsort(0)[:, 0]]
实际上可以替换为a[a[:, 0].argsort()]
【参考方案2】:
这与您从中获取代码的链接问题中的任务不完全相同:
-
你有轮廓,而另一个问题有要点。
你必须想出一种方法来对轮廓进行排序(它们可能在一维上重叠等等......)。有多种方法可以做到这一点,具体取决于您的用例。最简单的可能是使用轮廓的质心。这可以在这里完成:Center of mass in contour (Python, OpenCV)。然后,您可以从中创建一个对象数组,其中包含点并使用您找到的代码。
您找到的代码假定点基本上或多或少位于网格上。因此,参考图像上的所有点 1-5 大致在一条线上。在您发布的新图片中,情况并非如此。在这里采用聚类方法可能会更好:使用某种方法(可能是一个from here)对中心点 y 坐标进行聚类。然后对于每个集群:按中心 x 坐标对元素进行排序。
正如我已经说过的,有多种方法可以做到这一点,这几乎不取决于您的用例。
【讨论】:
以上是关于Python:使用 OpenCV 从左上角到右下角对项目进行排序的主要内容,如果未能解决你的问题,请参考以下文章