【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://www.mzph.cn/diannao/47614.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

21天学通C++:第十三、十四章节

第十三章&#xff1a;类型转换运算符 类型转换是一种机制&#xff0c;让程序员能够暂时或永久性改变编译器对对象的解释。注意&#xff0c;这并不意味着程序员改变了对象本身&#xff0c;而只是改变了对对象的解释。可改变对象解释方式的运算符称为类型转换运算符。 为何需要…

数据库端口LookUp功能:从数据库中获取并添加数据到XML

本文将为大家介绍如何使用知行之桥EDI系统数据库端口的Lookup功能&#xff0c;从数据库中获取数据&#xff0c;并添加进输入的XML中。 使用场景&#xff1a;期待以输入xml中的值为判断条件从数据库中获取数据&#xff0c;并添加进输入xml中。 例如&#xff1a;接收到包含采购…

简述乐观锁和悲观锁——Java

悲观锁和乐观锁 悲观就是任何事都认为会往坏处发生&#xff0c;乐观就是认为任何事都会往好处发生。 打个比方&#xff0c;假如一个公司里只有一台打印机&#xff0c;如果多个人同时打印文件&#xff0c;可能出现混乱的问题&#xff0c;他的资料打印在了我的资料上&#xff0…

Yolov8网络结构学习

详解YOLOv8网络结构/环境搭建/数据集获取/训练/推理/验证/导出/部署 深入解析YOLOv8&#xff1a;网络结构与推理过程 YOLO? You Know! --YOLOV8详解 一&#xff1a;yolov8总体结构 1.Backbone:它采用了一系列卷积和 反卷积层只来提取特征&#xff0c;同时也使用了残差连接和…

网站开发:使用VScode安装yarn包和运行前端项目

一、首先打开PowerShell-管理员身份运行ISE 输入命令&#xff1a; set-ExecutionPolicy RemoteSigned 选择“全是”&#xff0c;表示允许在本地计算机上运行由本地用户创建的脚本&#xff0c;没有报错就行了 二、接着打开VScode集成终端 输入 npm install -g yarn 再次输入以…

小程序-3(页面导航+页面事件+生命周期+WXS)

目录 1.页面导航 声明式导航 导航到tabBar页面 导航到非tabBar页面 后退导航 编程式导航 后退导航 导航传参 声明式导航传参 编程式导航传参 在onload中接收导航参数 2.页面事件 下拉刷新 停止下拉刷新的效果 ​编辑 上拉触底 配置上拉触底距离 上拉触底的节…

js补环境系列之剖析:原型、原型对象、实例对象三者互相转化(不讲废话、全是干货)

【作者主页】&#xff1a;小鱼神1024 【擅长领域】&#xff1a;JS逆向、小程序逆向、AST还原、验证码突防、Python开发、浏览器插件开发、React前端开发、NestJS后端开发等等 思考下&#xff1a;js补环境中&#xff0c;什么场景会用到原型、原型对象、实例对象&#xff1f; 举…

谈谈大数据采集和常见问题

01 什么是数据采集 数据采集是大数据的基石&#xff0c;不论是现在的互联网公司&#xff0c;物联网公司或者传统的IT公司&#xff0c;每个业务流程环节都会产生大量的数据&#xff0c;同时用户操作的日志也会产生大量的数据&#xff0c;为了将这些结构化和非结构化的数据进行…

docker网络互联

最近学习docker的时候发现多了很多网卡&#xff0c;这些似乎都和docker有关&#xff0c;所以我便往下深入了解了一番&#xff1b; 一、docker网卡 docker 0是安装 docker 的时候生成的虚拟网桥&#xff0c;它在内核层连通了其他物理或者虚拟网卡&#xff0c;这就可以将所…

常用优秀内网穿透工具(实测详细版)

文章目录 1、前言2、安装Nginx3、配置Nginx4、启动Nginx服务4.1、配置登录页面 5、内网穿透5.1、cpolar5.1.1、cpolar软件安装5.1.2、cpolar穿透 5.2、Ngrok5.2.1、Ngrok安装5.2.2、随机域名5.2.3、固定域名5.2.4、前后端服务端口 5.3、NatApp5.4、Frp5.4.1、下载Frp5.4.2、暴露…

【数据结构】--- 栈和队列

前言 前面学习了数据结构的顺序表、单链表、双向循环链表这些结构&#xff1b;现在就来学习栈和队列&#xff0c;这里可以简单的说栈和队列是具有特殊化的线性表 一、栈 1.1、栈的概念和结构 栈是一种遵循先入后出逻辑的线性数据结构。 栈是一种特殊的线性表&#xff0c;它只允…

vivado FFT IP Core

文章目录 前言FFT IP 接口介绍接口简介tdata 格式说明 其他细节关于计算精度及缩放系数计算溢出架构选择数据顺序实时/非实时模式数据输入输出时序关于配置信息的应用时间节点 FFT IP 例化介绍控制代码实现 & 测试参考文献 前言 由于计算资源受限&#xff0c;准备将上位机 …

【漏洞复现】泛微E-Cology WorkflowServiceXml SQL注入漏洞

0x01 产品简介 泛微e-cology是一款由泛微网络科技开发的协同管理平台&#xff0c;支持人力资源、财务、行政等多功能管理和移动办公。 0x02 漏洞概述 泛微OAE-Cology 接口/services/WorkflowServiceXml 存在SQL注入漏洞&#xff0c;可获取数据库权限&#xff0c;导致数据泄露…

Qt日志库QsLog使用教程

前言 最近项目中需要用到日志库。上一次项目中用到了log4qt库&#xff0c;这个库有个麻烦的点是要配置config文件&#xff0c;所以这次切换到了QsLog。用了后这个库的感受是&#xff0c;比较轻量级&#xff0c;嘎嘎好用&#xff0c;推荐一波。 下载QsLog库 https://github.c…

【踩坑日记】【教程】嵌入式 Linux 通过 nfs 下载出现 T T T T [Retry count exceeded: starting again]

文章目录 1 本篇文章解决的问题2 问题解决原理3 问题环境4 开启 ubuntu-20.04 的 nfs24.1 确认 nfs2 是否已经开启4.2 开启 nfs2 5 卸载 iptables5.1 卸载 iptables5.2 禁用 ufw5.3 尝试重新下载 6 原理分析6.1 nfs2 开启部分6.2 卸载 iptables 部分 7 后记7.1 拓扑结构一7.2 拓…

【博士每天一篇文献-算法】连续学习算法之HNet:Continual learning with hypernetworks

阅读时间&#xff1a;2023-12-26 1 介绍 年份&#xff1a;2019 作者&#xff1a;Johannes von Oswald&#xff0c;Google Research&#xff1b;Christian Henning&#xff0c;EthonAI AG&#xff1b;Benjamin F. Grewe&#xff0c;苏黎世联邦理工学院神经信息学研究所 期刊&a…

【Vue3 ts】echars图表展示统计的月份数据

图片展示 此处内容为展示24年各个月份产品的创建数量。在后端统计24年各个月份产品数量后&#xff0c;以数组的格式发送给前端&#xff0c;前端负责展示。 后端 entity层&#xff1a; Data Schema(description "月份统计")public class MonthCount {private Stri…

处理uniapp刷新后,点击返回按钮跳转到登录页的问题

在使用uniapp的原生返回的按钮时&#xff0c;如果没有刷新会正常返回到对应的页面&#xff0c;如果刷新后会在当前页反复横跳&#xff0c;或者跳转到登录页。那个时候我第一个想法时&#xff1a;使用浏览器的history.back()方法。因为浏览器刷新后还是可以通过右上角的返回按钮…

01认识Java(介绍安装调试)

单元概述 本章主要介绍Java语言的发展历史&#xff0c;了解Java的运行原理及Java编程语言的特点&#xff0c;通过搭建Eclipse集成开发环境来运行Java应用程序。 1.1 Java简介 1.1.1 什么是Java 计算机语言是人与计算机之间的通讯语言&#xff0c;分为机器语言、汇编语言、高…

短视频是如何一步步“蚕食”我们大脑的?

点击上方△腾阳 关注 转载请联系授权 你好&#xff0c;我是腾阳。 今天我们将深入探讨短视频是如何「蚕食」我们的大脑。 首先问下自己&#xff0c;你有多久没有看完一篇长文了&#xff1f; 你是否曾在清晨阳光中&#xff0c;被手机屏幕上短视频图标吸引&#xff0c;而忘记…