使用OpenCV对点集从左上到右下排序

本文实现如何使用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

<think>好的,用户想了解如何在已知凸四边形的四个,按照左下、右下、右上、左上的顺序排列的情况下,用Python进行处理。首先,我需要明确用户的需求是什么。他们可能是在处理计算机图形学、几何计算或者图像处理中的问题,需要确定四边形的顶顺序是否正确,或者想要进行进一步的变换,比如透视变换。 首先,用户提到的四个的顺序是左下、右下、右上、左上。这通常是在需要将四边形映射到矩形时使用的顺序,比如在OpenCV中的透视变换。正确的顺序对于后续处理非常重要,因此用户可能需要确认这四个是否确实按照这个顺序排列,或者需要重新排列它们。 接下来,我需要考虑如何验证这四个是否构成凸四边形。凸四边形的定义是所有内角都小于180度,并且任意三不共线。检测凸性的方法通常包括计算各的凸包,如果凸包的数等于四,则说明是凸四边形。或者,可以检查所有四个是否按顺序排列,并且每个内角是否满足凸性条件。 然后,用户可能需要将这些按照指定的顺序排列。如果给定的没有按照这个顺序,可能需要重新排序排序的方法可能包括先找到最左边的,然后根据坐标进行排序,或者按照顺时针或逆时针顺序排列。例如,可以计算四个的中心,然后根据每个相对于中心的极角进行排序,以确保正确的顺序。 此外,用户可能需要将这些应用于某种变换,比如计算透视变换矩阵。这时,正确的顺序是至关重要的,因为目标通常需要按照对应的顺序排列。 在Python中,处理这类问题通常会用到NumPy库来进行数值计算,以及可能使用OpenCV库中的相关函数,比如cv2.getPerspectiveTransform。因此,可能需要编写代码来验证的顺序,调整的顺序,并进行相应的几何变换。 可能的步骤包括: 1. 输入四个的坐标。 2. 确认这些是否构成凸四边形。 3. 按照指定的顺序(左下、右下、右上、左上)排列这些。 4. 应用这些进行进一步的变换或计算。 需要注意的是,用户可能对凸四边形的处理不太熟悉,因此需要逐步解释每个步骤的目的和方法,确保代码示例清晰,并且有必要的注释。同时,需要处理可能的异常情况,比如不构成凸四边形时的错误处理。 最后,可能需要提供完整的代码示例,展示如何接收四个,检查凸性,排序,并进行应用。这将帮助用户更好地理解整个流程,并将其应用到实际项目中。</think>以下分步骤介绍如何处理已知凸四边形四个顶并按特定顺序排列的问题: 一、基本概念 凸四边形是指所有内角都小于180度,且任意两边延长线不会穿过四边形内部的平面图形。其顶按左下(LB)、右下(RB)、右上(RT)、左上(LT)顺序排列时,可形成闭合路径: $$ LB \rightarrow RB \rightarrow RT \rightarrow LT \rightarrow LB $$ 二、坐标处理步骤 1. 输入坐标验证 假设给定四个坐标: ```python points = [ (x1, y1), # 左下(LB) (x2, y2), # 右下(RB) (x3, y3), # 右上(RT) (x4, y4) # 左上(LT) ] ``` 2. 凸性验证(使用向量叉乘法) 计算相邻边向量的叉积符号是否一致: ```python def is_convex(points): signs = [] n = len(points) for i in range(n): dx1 = points[(i+1)%n][0] - points[i][0] dy1 = points[(i+1)%n][1] - points[i][1] dx2 = points[(i+2)%n][0] - points[(i+1)%n][0] dy2 = points[(i+2)%n][1] - points[(i+1)%n][1] cross = dx1*dy2 - dy1*dx2 if cross != 0: signs.append(cross > 0) return all(s == signs[0] for s in signs) ``` 3. 坐标排序校正(若需要) 当输入顺序不确定时,可通过以下方法排序: ```python import numpy as np def order_points(points): # 按x+y之和最小为左下,最大为右上 points = np.array(points) s = points.sum(axis=1) lb_idx = np.argmin(s) rt_idx = np.argmax(s) # 剩余按x坐标排序 remain = [i for i in range(4) if i not in [lb_idx, rt_idx]] remain.sort(key=lambda x: points[x][0]) rb_idx, lt_idx = remain return [points[lb_idx], points[rb_idx], points[rt_idx], points[lt_idx]] ``` 三、典型应用场景 1. 透视变换(常用于图像校正) ```python import cv2 def perspective_transform(image, pts): # 目标矩形尺寸 width = 400 height = 300 # 目标顺序必须与输入对应 dst = np.array([ [0, 0], [width-1, 0], [width-1, height-1], [0, height-1]], dtype="float32") # 计算变换矩阵 M = cv2.getPerspectiveTransform(pts.astype(np.float32), dst) # 执行变换 warped = cv2.warpPerspective(image, M, (width, height)) return warped ``` 四、数学验证方法 1. 对角线相交验证(凸四边形必要条件) 对于 $A(x_1,y_1)$, $B(x_2,y_2)$, $C(x_3,y_3)$, $D(x_4,y_4)$,线段AC与BD必须相交。 相交条件可通过参数方程联立求解: $$ \begin{cases} x = x_1 + t(x_3 - x_1) \\ y = y_1 + t(y_3 - y_1) \\ x = x_2 + s(x_4 - x_2) \\ y = y_2 + s(y_4 - y_2) \end{cases} $$ 要求解得的 $t,s \in (0,1)$ 五、注意事项 1. 坐标精度问题:处理浮坐标时需设置合理误差范围 2. 退化情况处理:当三共线时应视为无效四边形 3. 方向一致性:确保所有按顺时针或逆时针统一排列 4. 坐标系差异:注意图像坐标系(Y轴向下)与数学坐标系(Y轴向上)的区别 完整代码示例: ```python import cv2 import numpy as np class QuadrilateralProcessor: def __init__(self, points): self.points = np.array(points) assert len(self.points) == 4, "必须输入4个顶" def validate(self): if not is_convex(self.points): raise ValueError("不构成凸四边形") return True def reorder(self): self.points = order_points(self.points) return self.points def transform(self, image): self.validate() ordered = self.reorder() return perspective_transform(image, ordered) ``` 此方案可应用于文档扫描、车牌识别、AR标记检测等需要处理四边形区域的场景。实际使用时需根据具体需求调整坐标排序策略和误差阈值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值