Python:使用OpenCV从左上到右下对项目进行排序

我如何尝试从左上角到右下角对图片的项目进行排序,如下图所示?当前使用以下代码收到此错误。

错误:

a = sorted(keypoints, key=lambda p: (p[0]) + (p 1 ))[0] # 找到左上点 ValueError: 包含多个元素的数组的真值不明确。使用 a.any() 或 a.all()

这个问题是从这个建模的:Ordering坐标从左上角到右下角

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

新图片:

参考订购图片:

将使用质心来确定中心点顺序。

回答

结果编号取决于您希望有多少行。通过我将向您展示如何制作的程序,您可以在运行程序之前指定行数。

例如,这是原始图像:

这是指定 4 行时的编号图像:

这是指定 6 行时的编号图像:

对于您提供的其他图像(其框架被裁剪,因此框架不会被检测为形状),您可以看到将有 4 行,因此将 4 放入程序将为您提供:


让我们看一下考虑 4 行的工作流程。我使用的概念是将图像沿 y 轴分成 4 段,形成 4 行。对于图像的每个片段,找到其中心在该片段中的每个形状。最后,按 x 坐标对每个线段中的形状进行排序。

  1. 导入必要的库:
import cv2
import numpy as np
  1. 定义一个函数,该函数将接收图像输入并将处理后的图像返回到允许 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. 定义一个返回轮廓中心的函数:
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. 定义一个函数,该函数将接收处理过的图像并返回图像中找到的形状的中心点:
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. 定义一个函数,该函数将接受图像数组 、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]]
  1. 读入图像,使用processed定义的函数获取其处理形式,并使用定义的函数获取图像中每个形状的中心centers
img = cv2.imread("shapes.png")
img_processed = process_img(img)
centers = list(get_centers(img_processed))
  1. 获取用于get_rows定义函数的图像高度,并定义一个计数变量count,以跟踪编号:
h, w, c = img.shape
count = 0
  1. 循环遍历分为 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. 添加到count变量,并countrow数组中将绘制到图像上的特定位置:
        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. 最后,显示图像:
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)

  • We want to find all the points in the `centers` array that lies within the `i` row. The `f = centers[:, 1] - d * i` returns the `centers` array with each y coordinate subtracted by the distance between the top of the image and the top of the `i` row. So basically its like the `i` row got shifted upwards until it touched the top of the image. With the shifted image, we can simple check if the points lie within the top of the image and the height of the row, hence the `a = centers[(f < d) & (f > 0)]`.
  • The `a.argsort(0)` retuurns a's indices with its x coordinates and y coordinates sorted. Since we only want to sort the row by its x coordinates, we use the slice `[:, 0]`, meaning all the rows at the `0` column. So `a.argsort(0)[:, 0]` is the array of indices, and yield `a[a.argsort(0)[:, 0]]` yields the rows sorted by the 0 columns. I realized that `a[a.argsort(0)[:, 0]]` can actually be replaced with `a[a[:, 0].argsort()]`

以上是Python:使用OpenCV从左上到右下对项目进行排序的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>