【python】OpenCV—Coordinates Sorted Clockwise

在这里插入图片描述

文章目录

  • 1、需求介绍
  • 2、算法实现
  • 3、完整代码

1、需求介绍

调用 opencv 库,绘制轮廓的矩形边框,坐标顺序为右下→左下→左上→右上,我们实现一下转化为熟悉的 左上→右上→右下→左下 形式

按照这样的顺序组织边界框坐标是执行透视转换或匹配对象角点(例如计算对象之间的距离)等操作的先决条件

2、算法实现

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2021/10/17 0:20
# @File    : order_coordinates.py.py
# @Software: PyCharm
# 导入包
from __future__ import print_function
from imutils import perspective
from imutils import contours
import numpy as np
import argparse
import imutils
import cv2
from scipy.spatial import distance as distdef order_points_old(pts):# 初始化将被排序的坐标列表,这样,列表中的第一个条目是左上,第二个条目是右上,第三个条目是右下,第四个条目是左下rect = np.zeros((4, 2), dtype="float32")"""array([[ 90, 236],[ 42, 236],[ 42, 190],[ 90, 190]])"""# 左上点的总和最小,而右下点的总和最大s = pts.sum(axis=1)  # x+y array([326, 278, 232, 280])rect[0] = pts[np.argmin(s)]  # 2, array([ 42., 190.], dtype=float32)rect[2] = pts[np.argmax(s)]  # 0, array([ 90., 236.], dtype=float32)# 现在,计算点之间的差值,右上角的差值最小,而左下角的差值最大diff = np.diff(pts, axis=1)  # array([[146], [194], [148], [100]])rect[1] = pts[np.argmin(diff)]rect[3] = pts[np.argmax(diff)]# 返回有序坐标return rect

def order_points_old 前,边界框四个顶点的坐标顺序
右下→左下→左上→右上

def order_points_old 后,边界框四个顶点的坐标顺序
左上→右上→右下→左下

假设矩形框比较正,无倾斜,左上角坐标为 (x,y)

  • 右上角(x+w,y)
  • 左下角(x,y+h)
  • 右下角(x+w,y+h)

横纵坐标之差的大小

  • 左上角(y-x)
  • 右上角(y-x-w)
  • 左下角(y-x+h)
  • 右下角(y-x+h-w)

这么对比分析的话,确实差值右上角最小,最下角最大

缺陷

当两点的和或差相同时会发生什么?如果数组 和 或数组 diff 有相同的值,我们有选择不正确索引的风险,这会对我们的排序产生严重影响。

改进:

def order_points(pts):# 根据点的 x 坐标对点进行排序xSorted = pts[np.argsort(pts[:, 0]), :]# 从根据点的 x 坐标排序的坐标点中获取最左和最右的点leftMost = xSorted[:2, :]rightMost = xSorted[2:, :]# 现在,根据y坐标对最左边的坐标排序,这样我们就可以分别获取左上角和左下角的点leftMost = leftMost[np.argsort(leftMost[:, 1]), :](tl, bl) = leftMost# 现在我们有了左上角的坐标,用它作为锚点来计算左上角和右下角点之间的欧氏距离;根据勾股定理,距离最大的点就是右下点D = dist.cdist(tl[np.newaxis], rightMost, "euclidean")[0](br, tr) = rightMost[np.argsort(D)[::-1], :]# 按左上、右上、右下和左下顺序返回坐标return np.array([tl, tr, br, bl], dtype="float32")

下面找张图片来实战一下

输入图片

在这里插入图片描述

# 构造参数解析并解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-n", "--new", type=int, default=1,help="whether or not the new order points should should be used")
args = vars(ap.parse_args())
# 加载我们的输入图像,将其转换为灰度,并稍微模糊它
image = cv2.imread("1.jpg")gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.imwrite("gray.jpg", gray)

gray.jpg
在这里插入图片描述

gray = cv2.GaussianBlur(gray, (7, 7), 0)
cv2.imwrite("GaussianBlur.jpg", gray)

GaussianBlur.jpg

在这里插入图片描述

# 执行边缘检测,然后执行膨胀+腐蚀以缩小对象边缘之间的间隙
edged = cv2.Canny(gray, 50, 100)
cv2.imwrite("Canny.jpg", edged)

Canny.jpg

在这里插入图片描述

edged = cv2.dilate(edged, None, iterations=1)
cv2.imwrite("dilate.jpg", edged)

dilate.jpg

在这里插入图片描述

edged = cv2.erode(edged, None, iterations=1)
cv2.imwrite("erode.jpg", edged)

erode.jpg
在这里插入图片描述

# 在边缘图中找到轮廓
cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
# 从左到右对轮廓进行排序并初始化边界框点颜色
(cnts, _) = contours.sort_contours(cnts)
colors = ((0, 0, 255), (240, 0, 159), (255, 0, 0), (255, 255, 0))
# 分别在轮廓上循环
for (i, c) in enumerate(cnts):# 如果轮廓不够大,则忽略它if cv2.contourArea(c) < 100:continue# 计算轮廓的旋转边界框,然后绘制轮廓box = cv2.minAreaRect(c)box = cv2.cv.BoxPoints(box) if imutils.is_cv2() else cv2.boxPoints(box)box = np.array(box, dtype="int")cv2.drawContours(image, [box], -1, (0, 255, 0), 2)# 显示原始坐标print("Object #{}:".format(i + 1))print("detect order:\n", box)# 对轮廓中的点进行排序,使它们以左上、右上、右下和左下的顺序出现,然后绘制旋转边界框的轮廓rect = order_points_old(box)print("old order:\n", rect)rect_new = order_points(box)print("new order1:\n", rect_new)# 检查是否应使用新方法对坐标进行排序if args["new"] > 0:rect = perspective.order_points(box)# 显示重新排序的坐标print("new order2:\n", rect.astype("int"))print("")# 遍历原始点并绘制它们for ((x, y), color) in zip(rect, colors):cv2.circle(image, (int(x), int(y)), 5, color, -1)# 在左上角绘制对象编号cv2.putText(image, "Object #{}".format(i + 1),(int(rect[0][0] - 15), int(rect[0][1] - 15)),cv2.FONT_HERSHEY_SIMPLEX, 0.55, (255, 255, 255), 2)# 显示cv2.imwrite(f"result_{i+1}.jpg", image)cv2.imshow("Image", image)cv2.waitKey(0)

输出

result_1.jpg

在这里插入图片描述

Object #1:
detect order:[[ 90 236][ 42 236][ 42 190][ 90 190]]
old order:[[ 42. 190.][ 90. 190.][ 90. 236.][ 42. 236.]]
new order1:[[ 42. 190.][ 90. 190.][ 90. 236.][ 42. 236.]]
new order2:[[ 42 190][ 90 190][ 90 236][ 42 236]]

result_2.jpg

在这里插入图片描述

Object #2:
detect order:[[192 192][171  94][342  59][362 157]]
old order:[[171.  94.][342.  59.][362. 157.][192. 192.]]
new order1:[[171.  94.][342.  59.][362. 157.][192. 192.]]
new order2:[[171  94][342  59][362 157][192 192]]

result_3.jpg

在这里插入图片描述

Object #3:
detect order:[[395 229][348 229][348 183][395 183]]
old order:[[348. 183.][395. 183.][395. 229.][348. 229.]]
new order1:[[348. 183.][395. 183.][395. 229.][348. 229.]]
new order2:[[348 183][395 183][395 229][348 229]]

result_4.jpg

在这里插入图片描述

Object #4:
detect order:[[400 383][377 258][489 237][512 363]]
old order:[[377. 258.][489. 237.][512. 363.][400. 383.]]
new order1:[[377. 258.][489. 237.][512. 363.][400. 383.]]
new order2:[[377 258][489 237][512 363][400 383]]

result_5.jpg

在这里插入图片描述

Object #5:
detect order:[[495 211][392 161][450  42][553  92]]
old order:[[450.  42.][553.  92.][495. 211.][392. 161.]]
new order1:[[450.  42.][553.  92.][495. 211.][392. 161.]]
new order2:[[450  42][553  92][495 211][392 161]]

result_6.jpg

在这里插入图片描述

Object #6:
detect order:[[520 255][491 226][520 197][549 226]]
old order:[[491. 226.][520. 197.][520. 255.][520. 255.]]
new order1:[[491. 226.][520. 197.][549. 226.][520. 255.]]
new order2:[[491 226][520 197][549 226][520 255]]

注意到目标 6:

横纵坐标和:
520 + 255 = 775
491 + 226 = 717
520 + 197 = 717
549 + 226 = 775

横纵坐标差:
520 – 255 = 265
491 – 226 = 265
520 – 197 = 323
549 – 226 = 323

和、差里面出现了相等的元素,old order 方法可能会出现错误顺序的赋值导致错误,新的方法则不会

3、完整代码

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2021/10/17 0:20
# @File    : order_coordinates.py.py
# @Software: PyCharm
# 导入包
from __future__ import print_function
from imutils import perspective
from imutils import contours
import numpy as np
import argparse
import imutils
import cv2
from scipy.spatial import distance as distdef order_points_old(pts):# 初始化将被排序的坐标列表,这样,列表中的第一个条目是左上,第二个条目是右上,第三个条目是右下,第四个条目是左下rect = np.zeros((4, 2), dtype="float32")"""array([[ 90, 236],[ 42, 236],[ 42, 190],[ 90, 190]])"""# 左上点的总和最小,而右下点的总和最大s = pts.sum(axis=1)  # x+y array([326, 278, 232, 280])rect[0] = pts[np.argmin(s)]  # 2, array([ 42., 190.], dtype=float32)rect[2] = pts[np.argmax(s)]  # 0, array([ 90., 236.], dtype=float32)# 现在,计算点之间的差值,右上角的差值最小,而左下角的差值最大diff = np.diff(pts, axis=1)  # array([[146], [194], [148], [100]])rect[1] = pts[np.argmin(diff)]rect[3] = pts[np.argmax(diff)]# 返回有序坐标return rectdef order_points(pts):# 根据点的 x 坐标对点进行排序xSorted = pts[np.argsort(pts[:, 0]), :]# 从根据点的 x 坐标排序的坐标点中获取最左和最右的点leftMost = xSorted[:2, :]rightMost = xSorted[2:, :]# 现在,根据y坐标对最左边的坐标排序,这样我们就可以分别获取左上角和左下角的点leftMost = leftMost[np.argsort(leftMost[:, 1]), :](tl, bl) = leftMost# 现在我们有了左上角的坐标,用它作为锚点来计算左上角和右下角点之间的欧氏距离;根据勾股定理,距离最大的点就是右下点D = dist.cdist(tl[np.newaxis], rightMost, "euclidean")[0](br, tr) = rightMost[np.argsort(D)[::-1], :]# 按左上、右上、右下和左下顺序返回坐标return np.array([tl, tr, br, bl], dtype="float32")# 构造参数解析并解析参数
ap = argparse.ArgumentParser()
ap.add_argument("-n", "--new", type=int, default=1,help="whether or not the new order points should should be used")
args = vars(ap.parse_args())
# 加载我们的输入图像,将其转换为灰度,并稍微模糊它
image = cv2.imread("1.jpg")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.imwrite("gray.jpg", gray)
gray = cv2.GaussianBlur(gray, (7, 7), 0)
cv2.imwrite("GaussianBlur.jpg", gray)
# 执行边缘检测,然后执行膨胀+腐蚀以缩小对象边缘之间的间隙
edged = cv2.Canny(gray, 50, 100)
cv2.imwrite("Canny.jpg", edged)
edged = cv2.dilate(edged, None, iterations=1)
cv2.imwrite("dilate.jpg", edged)
edged = cv2.erode(edged, None, iterations=1)
cv2.imwrite("erode.jpg", edged)
# 在边缘图中找到轮廓
cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
# 从左到右对轮廓进行排序并初始化边界框点颜色
(cnts, _) = contours.sort_contours(cnts)
colors = ((0, 0, 255), (240, 0, 159), (255, 0, 0), (255, 255, 0))
# 分别在轮廓上循环
for (i, c) in enumerate(cnts):# 如果轮廓不够大,则忽略它if cv2.contourArea(c) < 100:continue# 计算轮廓的旋转边界框,然后绘制轮廓box = cv2.minAreaRect(c)box = cv2.cv.BoxPoints(box) if imutils.is_cv2() else cv2.boxPoints(box)box = np.array(box, dtype="int")cv2.drawContours(image, [box], -1, (0, 255, 0), 2)# 显示原始坐标print("Object #{}:".format(i + 1))print("detect order:\n", box)# 对轮廓中的点进行排序,使它们以左上、右上、右下和左下的顺序出现,然后绘制旋转边界框的轮廓rect = order_points_old(box)print("old order:\n", rect)rect_new = order_points(box)print("new order1:\n", rect_new)# 检查是否应使用新方法对坐标进行排序if args["new"] > 0:rect = perspective.order_points(box)# 显示重新排序的坐标print("new order2:\n", rect.astype("int"))print("")# 遍历原始点并绘制它们for ((x, y), color) in zip(rect, colors):cv2.circle(image, (int(x), int(y)), 5, color, -1)# 在左上角绘制对象编号cv2.putText(image, "Object #{}".format(i + 1),(int(rect[0][0] - 15), int(rect[0][1] - 15)),cv2.FONT_HERSHEY_SIMPLEX, 0.55, (255, 255, 255), 2)# 显示cv2.imwrite(f"result_{i+1}.jpg", image)cv2.imshow("Image", image)cv2.waitKey(0)

参考学习来自:OpenCV基础(26)使用 Python 和 OpenCV 顺时针排序坐标

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3248969.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

采用反相正基准电压电路的反相运算放大器(运放)

采用反相正基准电压电路的反相运算放大器(运放) 采用反相正基准电压电路的同相运算放大器&#xff08;运放&#xff09; 设计目标 输入ViMin输入ViMax输出VoMin输出VoMax电源电压Vcc电源电压Vee电源电压Vref-5V-1V0.05V3.3V5V0V5V 设计说明1 此设计使用具有反相正基准的反…

gite+picgo+typora打造个人免费笔记软件

文章目录 1️⃣个人笔记软件2️⃣ 配置教程2.1 使用软件2.2 node 环境配置2.3 软件安装2.4 gite仓库设置2.5 配置picgo2.6 测试检验2.7 github教程 &#x1f3a1; 完结撒花 1️⃣个人笔记软件 最近换了环境&#xff0c;没有之前的生产环境舒适&#xff0c;写笔记也没有劲头&…

盒须图boxplot 展示第6条线

正常情况下,盒须图是有5条线的,但是实际产品场景是需要6条线,看了下echarts官网,没看到可配置的地方,只能自己骚操作了,效果图如下: 重点:用两条x轴,第6条线挂在第二条x轴上,且第二条x轴不展示。 option = {...,xAxis: [{type: category,data: [Class1, Class2, Cl…

Web前端网页设计与制作(想起来哪里写哪里版)

本文技术栈基于HtmlCssJavaScript制作web页面以及功能实现的部分设计与展示&#xff0c;实际的网站开发会涉及更多的细节&#xff0c;包括但不限于用户认证、数据库存储、前端框架的使用、响应式设计、安全性措施等。 废话不说&#xff0c;直接看源码 1.index 首页&#xff1a…

【漏洞复现】泛微e-cology9 WorkflowServiceXml SQL注入漏洞

文章目录 前言漏洞描述影响范围 漏洞复现nuclei脚本 安全修复 前言 泛微协同管理应用平台e-cology是一套兼具企业信息门户、知识文档管理、工作流程管理、人力资源管理、客户关系管理、项目管理、财务管理、资产管理、供应链管理、数据中心功能的企业大型协同管理平台。 漏洞…

算法学习day16——刷题(仍然是数组)

一、图片平滑器 图像平滑器 是大小为 3 x 3 的过滤器&#xff0c;用于对图像的每个单元格平滑处理&#xff0c;平滑处理后单元格的值为该单元格的平均灰度。 每个单元格的 平均灰度 定义为&#xff1a;该单元格自身及其周围的 8 个单元格的平均值&#xff0c;结果需向下取整…

使用llama-cpp-python制作api接口

文章目录 概要整体操作流程技术细节小结 概要 使用llama-cpp-python制作api接口&#xff0c;可以接入gradio当中&#xff0c;参考上一节。 llama-cpp-python的github网址 整体操作流程 下载llama-cpp-python。首先判断自己是在CPU的环境下还是GPU的环境下。以下操作均在魔搭…

应用层——HTTP

像我们电脑和手机使用的应用软件就是在应用层写的&#xff0c;当我们的数据需要传输的时候换将数据传递到传输层。 应用层专门给用户提供应用功能&#xff0c;比如HTTP,FTP… 我们程序员写的一个个解决我们实际的问题都在应用层&#xff0c;我们今天来聊一聊HTTP。 协议 协议…

Java案例遍历集合中的自定义对象

目录 一&#xff1a;案例要求&#xff1a; 二案例分析&#xff1a; ​编辑三&#xff1a;具体代码&#xff1a; 四&#xff1a;运行结果&#xff1a; 一&#xff1a;案例要求&#xff1a; 二案例分析&#xff1a; 三&#xff1a;具体代码&#xff1a; Ⅰ&#xff1a; pack…

pyinstaller用法详解3

本文使用创作助手。 大家好&#xff0c;时隔多日&#xff0c;我又更新了pyinstaller的用法详解&#xff01; 当然&#xff0c;这一次要比之前更详细&#xff0c;十分详细。 谢谢大家的支持&#xff0c;我们现在开始&#xff01; 一、快速开始使用pyinstaller 我之前的文章…

Web3时代的教育技术革新:智能合约在学习管理中的应用

随着区块链技术的发展和普及&#xff0c;Web3时代正在为教育技术带来前所未有的革新和机遇。智能合约作为区块链技术的核心应用之一&#xff0c;不仅在金融和供应链管理等领域展示了其巨大的潜力&#xff0c;也在教育领域中逐渐探索和应用。本文将探讨智能合约在学习管理中的具…

Java 中的迭代器

Iterator (迭代器) 正是由于每一个容器都有取出元素的功能&#xff0c;这些功能定义都一样&#xff0c;所以对共性的取出功能进行了抽取&#xff0c;从而出现了Iterator接口。由于每一个容器的数据结构不一样&#xff0c;所以具体的实现方式也不同&#xff0c;每一个容器都在其…

[激光原理与应用-115]:南京科耐激光-激光焊接-焊中检测-智能制程监测系统IPM介绍 - 19 - 主要硬件的介绍、安装与调试

目录 一、概述 1.1 前言 1.2 系统组成 1.2.1 机柜版&#xff1a; 1.2.2 非机柜版 1.3适用范围 1.4 工作条件 1.5 安全说明 1.6 装箱清单 二、硬件安装 2.1 光学传感器安装 2.1.1 转接件安装 2.1.2 光路校准模块的安装与光路校准 2.1.3 光学传感器的安装 2.2 通…

Docker安装mysql详细教程, mysqld: Can‘t read dir of ‘/etc/mysql/conf.d/‘(已解决)

文章目录 一、下载MySQL的docker镜像二、启动MySQL容器2.1 命令2.2 报错mysqld: Cant read dir of /etc/mysql/conf.d/ (Errcode: 2 - No such file or directory) 三、进入mysql容器四、修改mysql默认配置4.1 查看mysql挂载的文件夹4.2 mysql配置 五、补充 如果还没在虚拟机/服…

新版本cesium编译1.103之后的版本

cesium1.1之后的版本文件结构域1.1之前的版本有了很大的差别&#xff0c;源码也全部移到了packages目录中。有很多依赖包没有写在根目录的package.json文件中。npm i 后直接编译会保持。 cesium源码git https://github.com/CesiumGS/cesium 1、添加缺少的包&#xff0c;缺少的…

《昇思25天学习打卡营第25天|onereal》

初学入门/初学教程/08-模型训练.ipynb 模型训练 模型训练一般分为四个步骤&#xff1a; 构建数据集。定义神经网络模型。定义超参、损失函数及优化器。输入数据集进行训练与评估。 现在我们有了数据集和模型后&#xff0c;可以进行模型的训练与评估。 构建数据集 首先从数…

SSD实现

一、模型 此模型主要由基础网络组成&#xff0c;其后是几个多尺度特征块。基本网络用于从输入图像中提取特征&#xff0c;因此它可以使用深度卷积神经网络。 单发多框检测选用了在分类层之前截断的VGG&#xff0c;现在也常用ResNet替代&#xff1b;可以设计基础网络&#xff0c…

synchronized的实现原理和锁升级 面试重点

1.synchronized的实现原理 synchronized是Java 中的一个很重要的关键字&#xff0c;主要用来加锁&#xff0c;synchronized所添加的锁有以下几个特点。synchronized的使用方法比较简单&#xff0c;主要可以用来修饰方法和代码块。根据其锁定的对象不同&#xff0c;可以用来定义…

生命周期的妙用——Vue3

Vue3的生命周期 从Vue2到Vue3&#x1f47e;不只onMounted又见keep-alive主要属性被你包裹应用场景 ) 从Vue2到Vue3&#x1f47e; Vue 3 保留了大多数 Vue 2 的生命周期钩子&#xff0c;同时引入了组合 API 中的生命周期钩子。以下是 Vue 3 中的生命周期钩子&#xff1a; 不…

数据库管理的艺术(MySQL):DDL、DML、DQL、DCL及TPL的实战应用(下:数据操作与查询)

文章目录 DML数据操作语言1、新增记录2、删除记录3、修改记录 DQL数据查询语言1、查询记录2、条件筛选3、排序4、函数5、分组条件6、嵌套7、模糊查询8、limit分页查询 集合操作union关键字和运算符in关键字any关键字some关键字all关键字 联合查询1、广义笛卡尔积2、等值连接3、…