【python】OpenCV—findContours(4.4)

在这里插入图片描述

文章目录

  • 1、功能描述
  • 2、代码实现
  • 3、完整代码
  • 4、结果展示
  • 5、涉及到的库函数
  • 6、参考

1、功能描述

找出物体轮廓,根据 PCA 计算特征值和特征向量,绘制特征值和特征向量,来初步展示物体的方向

2、代码实现

导入库函数,读入图片,判定图片是否存在,显示图片

import cv2 as cv
from math import atan2, cos, sin, sqrt, pi
import numpy as np

# Load the image
img = cv.imread("1.jpeg")

# Was the image there?
if img is None:
    print("Error: File not found")
    exit(0)

cv.imshow('Input Image', img)

灰度化图片,二值化图片,为后续找轮廓做准备

# Convert image to grayscale
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
cv.imwrite("gray.jpg", gray)

# Convert image to binary
_, bw = cv.threshold(gray, 50, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
cv.imwrite("bw.jpg", bw)

找轮廓

# Find all the contours in the thresholded image
contours, _ = cv.findContours(bw, cv.RETR_LIST, cv.CHAIN_APPROX_NONE)

遍历轮廓,剔除面积过小或者过大的轮廓,绘制轮廓,调用 getOrientation 获取物体方向并绘制,并显示绘制结果

for i, c in enumerate(contours):

    # Calculate the area of each contour
    area = cv.contourArea(c)

    # Ignore contours that are too small or too large
    if area < 1000 or 100000 < area:
        continue

    # Draw each contour only for visualisation purposes with red color
    cv.drawContours(img, contours, i, (0, 0, 255), 2)

    # Find the orientation of each shape
    getOrientation(c, img)

cv.imshow('Output Image', img)
cv.waitKey(0)
cv.destroyAllWindows()

# Save the output image to the current directory
cv.imwrite("output_img.jpg", img)

下面看看 getOrientation(c, img) 的实现

def getOrientation(pts, img):
    ## [pca]
    # Construct a buffer used by the pca analysis
    sz = len(pts)  # 轮廓的关键点数, pts (446, 1, 2)
    data_pts = np.empty((sz, 2), dtype=np.float64)  # (446, 2)
    for i in range(data_pts.shape[0]):
        data_pts[i, 0] = pts[i, 0, 0]
        data_pts[i, 1] = pts[i, 0, 1]

    # Perform PCA analysis
    mean = np.empty((0))
    mean, eigenvectors, eigenvalues = cv.PCACompute2(data_pts, mean)

    # Store the center of the object
    cntr = (int(mean[0, 0]), int(mean[0, 1]))  # (177, 349)
    ## [pca]

    ## [visualization]
    # Draw the principal components
    cv.circle(img, cntr, 3, (255, 0, 255), 2)
    p1 = (cntr[0] + 0.025 * eigenvectors[0, 0] * eigenvalues[0, 0],
          cntr[1] + 0.025 * eigenvectors[0, 1] * eigenvalues[0, 0])
    p2 = (cntr[0] - 0.025 * eigenvectors[1, 0] * eigenvalues[1, 0],
          cntr[1] - 0.025 * eigenvectors[1, 1] * eigenvalues[1, 0])

   #  乘以0.25是为了放大这个距离,使其在图像上更加明显。

    drawAxis(img, cntr, p1, (255, 255, 0), 1)
    drawAxis(img, cntr, p2, (0, 0, 255), 5)

    angle = atan2(eigenvectors[0, 1], eigenvectors[0, 0])  # orientation in radians
    ## [visualization]

    # Label with the rotation angle
    # label = "  Rotation Angle: " + str(-int(np.rad2deg(angle)) - 90) + " degrees"
    label = str(-int(np.rad2deg(angle)) - 90) + " degrees"
    textbox = cv.rectangle(img, (cntr[0]+15, cntr[1] - 50), (cntr[0] + 130, cntr[1] - 15), (255, 255, 255), -1)
    cv.putText(img, label, (cntr[0]+15, cntr[1]-25), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1, cv.LINE_AA)

    return angle

其中 cv2.PCACompute2 获取特征值和特征向量

p1p2 是计算特征向量乘以特征值,方便后续可视化物体方向,0.025 是系数,影响的是绘制时候的长度

drawAxis 绘制箭头,展示物体方向

def drawAxis(img, p_, q_, color, scale):
    p = list(p_)
    q = list(q_)

    ## [visualization1]
    angle = atan2(p[1] - q[1], p[0] - q[0])  # angle in radians
    hypotenuse = sqrt((p[1] - q[1]) **2 + (p[0] - q[0])**2)

    # Here we lengthen the arrow by a factor of scale
    q[0] = p[0] - scale * hypotenuse * cos(angle)
    q[1] = p[1] - scale * hypotenuse * sin(angle)
    cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), color, 3, cv.LINE_AA)

    # create the arrow hooks 绘制箭头的钩子
    p[0] = q[0] + 9 * cos(angle + pi / 4)
    p[1] = q[1] + 9 * sin(angle + pi / 4)
    cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), color, 3, cv.LINE_AA)

    p[0] = q[0] + 9 * cos(angle - pi / 4)
    p[1] = q[1] + 9 * sin(angle - pi / 4)
    cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), color, 3, cv.LINE_AA)
    ## [visualization1]

可以看到有三个 cv2.line,第一个是绘制方向的直线,第二个和第三个分别绘制箭头,偏离直线 ±45°

scale 控制箭头直线的长度

3、完整代码

import cv2 as cv
from math import atan2, cos, sin, sqrt, pi
import numpy as np


def drawAxis(img, p_, q_, color, scale):
    p = list(p_)
    q = list(q_)

    ## [visualization1]
    angle = atan2(p[1] - q[1], p[0] - q[0])  # angle in radians
    hypotenuse = sqrt((p[1] - q[1]) **2 + (p[0] - q[0])**2)

    # Here we lengthen the arrow by a factor of scale
    q[0] = p[0] - scale * hypotenuse * cos(angle)
    q[1] = p[1] - scale * hypotenuse * sin(angle)
    cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), color, 3, cv.LINE_AA)

    # create the arrow hooks 绘制箭头的钩子
    p[0] = q[0] + 9 * cos(angle + pi / 4)
    p[1] = q[1] + 9 * sin(angle + pi / 4)
    cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), color, 3, cv.LINE_AA)

    p[0] = q[0] + 9 * cos(angle - pi / 4)
    p[1] = q[1] + 9 * sin(angle - pi / 4)
    cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), color, 3, cv.LINE_AA)
    ## [visualization1]


def getOrientation(pts, img):
    ## [pca]
    # Construct a buffer used by the pca analysis
    sz = len(pts)  # 轮廓的关键点数, pts (446, 1, 2)
    data_pts = np.empty((sz, 2), dtype=np.float64)  # (446, 2)
    for i in range(data_pts.shape[0]):
        data_pts[i, 0] = pts[i, 0, 0]
        data_pts[i, 1] = pts[i, 0, 1]

    # Perform PCA analysis
    mean = np.empty((0))
    mean, eigenvectors, eigenvalues = cv.PCACompute2(data_pts, mean)

    # Store the center of the object
    cntr = (int(mean[0, 0]), int(mean[0, 1]))  # (177, 349)
    ## [pca]

    ## [visualization]
    # Draw the principal components
    cv.circle(img, cntr, 3, (255, 0, 255), 2)
    p1 = (cntr[0] + 0.025 * eigenvectors[0, 0] * eigenvalues[0, 0],
          cntr[1] + 0.025 * eigenvectors[0, 1] * eigenvalues[0, 0])
    p2 = (cntr[0] - 0.025 * eigenvectors[1, 0] * eigenvalues[1, 0],
          cntr[1] - 0.025 * eigenvectors[1, 1] * eigenvalues[1, 0])

   #  乘以0.25是为了放大这个距离,使其在图像上更加明显。

    drawAxis(img, cntr, p1, (255, 255, 0), 1)
    drawAxis(img, cntr, p2, (0, 0, 255), 5)

    angle = atan2(eigenvectors[0, 1], eigenvectors[0, 0])  # orientation in radians
    ## [visualization]

    # Label with the rotation angle
    # label = "  Rotation Angle: " + str(-int(np.rad2deg(angle)) - 90) + " degrees"
    label = str(-int(np.rad2deg(angle)) - 90) + " degrees"
    textbox = cv.rectangle(img, (cntr[0]+15, cntr[1] - 50), (cntr[0] + 130, cntr[1] - 15), (255, 255, 255), -1)
    cv.putText(img, label, (cntr[0]+15, cntr[1]-25), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1, cv.LINE_AA)

    return angle


# Load the image
img = cv.imread("1.jpeg")

# Was the image there?
if img is None:
    print("Error: File not found")
    exit(0)

cv.imshow('Input Image', img)

# Convert image to grayscale
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
cv.imwrite("gray.jpg", gray)

# Convert image to binary
_, bw = cv.threshold(gray, 50, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
cv.imwrite("bw.jpg", bw)

# Find all the contours in the thresholded image
contours, _ = cv.findContours(bw, cv.RETR_LIST, cv.CHAIN_APPROX_NONE)

for i, c in enumerate(contours):

    # Calculate the area of each contour
    area = cv.contourArea(c)

    # Ignore contours that are too small or too large
    if area < 1000 or 100000 < area:
        continue

    # Draw each contour only for visualisation purposes with red color
    cv.drawContours(img, contours, i, (0, 0, 255), 2)

    # Find the orientation of each shape
    getOrientation(c, img)

cv.imshow('Output Image', img)
cv.waitKey(0)
cv.destroyAllWindows()

# Save the output image to the current directory
cv.imwrite("output_img.jpg", img)

4、结果展示

输入图片

在这里插入图片描述

灰度图

在这里插入图片描述

二值化后的结果

在这里插入图片描述

绘制的方向,drawAxis(img, cntr, p2, (0, 0, 255), 1)

在这里插入图片描述

drawAxis(img, cntr, p2, (0, 0, 255), 5)

在这里插入图片描述

角度怎么分析呢?

在这里插入图片描述

上图展示的是正方向

逆时针旋转能还原成上图情况就是负角度

在这里插入图片描述

仔细核对发现这个不符合上述的规则,个人理解,因为这个和正方向是反的(还原不到上述的正方向)

逆时针旋转,还原到 x 朝左,正好是 93°

5、涉及到的库函数

一、函数简介

cv2.PCACompute2 是 OpenCV 库中用于执行主成分分析(PCA)的函数。 PCA是一种统计方法,用于减少数据集的维度,同时保留数据中的主要变化特征。通过PCA,可以找到数据中的“主成分”,这些主成分定义了数据的主要变化方向。

二、函数参数

cv2.PCACompute2 函数接受多个参数,以下是其主要参数的解释:

  • data_pts:一个二维NumPy数组,包含所有数据点的坐标。每个数据点由一个包含两个元素的元组(x, y)表示。
  • mean:可选参数,用于指定数据点的平均值。如果未提供,OpenCV会自动计算数据点的平均值。
  • eigenvectors:可选参数,用于指定特征向量。如果未提供,OpenCV会自动计算特征向量。
  • eigenvalues:可选参数,用于指定特征值。如果未提供,OpenCV会自动计算特征值。
  • noise_cov:可选参数,用于指定噪声的协方差矩阵。如果未提供,OpenCV会使用单位协方差矩阵。
  • flags:可选参数,用于指定计算方式。默认值为0,表示使用OpenCV内置的计算方式。
  • iterations:可选参数,用于指定迭代次数。默认值为0,表示使用OpenCV内置的迭代次数。
  • eigenvalue_threshold:可选参数,用于指定特征值阈值。如果特征值小于这个阈值,它们将被忽略。默认值为0.0,表示不使用阈值。
  • eigenvector_threshold:可选参数,用于指定特征向量阈值。如果特征向量的模小于这个阈值,它们将被忽略。默认值为0.0,表示不使用阈值。

三、函数返回值

cv2.PCACompute2 函数返回以下三个值:

  • mean:数据点的平均值。
  • eigenvectors:特征向量。这些特征向量指向PCA认为信息最丰富的方向。
  • eigenvalues:特征值。特征值表示了对应特征向量方向上的方差大小。

四、使用示例

以下是一个使用 cv2.PCACompute2 函数的简单示例:

import numpy as np  
import cv2  
  
# 生成一组多元正态分布的数据  
mean = [20, 20]  
cov = [[5, 5], [5, 25]]  
X = np.random.multivariate_normal(mean, cov, 500)  
  
# 执行PCA计算  
mean, eigenvectors, eigenvalues = cv2.PCACompute2(X.T)  
  
# 输出结果  
print("Mean:", mean)  
print("Eigenvectors:\n", eigenvectors)  
print("Eigenvalues:\n", eigenvalues)

在这个示例中,我们首先生成了一组多元正态分布的数据,然后使用 cv2.PCACompute2 函数执行PCA计算,并输出平均值、特征向量和特征值。

五、注意事项

在使用 cv2.PCACompute2 函数之前,需要确保已经安装了OpenCV库。

  • 输入的 data_pts 参数应该是一个二维 NumPy 数组,且每个数据点应该由一个包含两个元素的元组(x, y)表示。
  • 根据实际需求,可以选择性地提供 mean、eigenvectors、eigenvalues、noise_cov 等参数。如果未提供这些参数,OpenCV会自动计算它们。
  • 返回值中的 eigenvectors 和 eigenvalues 分别表示了数据的主成分方向和对应的特征值大小。这些结果可以用于进一步的数据分析和处理。

通过合理使用该函数,可以有效地减少数据的维度并提取出数据中的主要变化特征。

6、参考

  • 使用OpenCV如何确定一个对象的方向
  • https://automaticaddison.com/how-to-determine-the-orientation-of-an-object-using-opencv/

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【论文阅读笔记】VLP: A Survey on Vision-language Pre-training

目录 前言2 特征提取&#xff08;Feature extraction&#xff09;2.1.1 图象特征提取OD-based Region feature / RoIFreeze the pre-trained object detectorsGrid features&#xff08;网格特征&#xff09;CNN-GFsEnd-to-End Training&#xff08;端到端训练&#xff09;ViT-…

Swarm-LIO: Decentralized Swarm LiDAR-inertial Odometry论文翻译

文章目录 前言一、介绍二、相关工作三、方法A. 问题表述B. 框架概述C. 群体系统的初始化D. 去中心化激光雷达-惯性状态估计 四. 实验A. 室内飞行B. 退化环境飞行C. 去中心化部署 五. 结论和未来工作 前言 原文&#xff1a;原文 准确的自我状态和相对状态估计是完成群体任务的关…

Apache Hive分布式容错数据仓库系统

Apache Hive™是一个分布式的、容错的数据仓库系统&#xff0c;它支持大规模的分析&#xff0c;并使用SQL方便地读取、写入和管理驻留在分布式存储中的pb级数据。 Apache Hive Apache Hive是什么 Apache Hive是一个分布式的、容错的数据仓库系统&#xff0c;支持大规模的分析…

Vue CLI: 安装、项目创建及基本概念指南,vue生命周期

只有经历地狱般的磨砺&#xff0c;才能练就创造出天堂的力量&#xff1b;只有流过血的手指&#xff0c;才能弹奏出世间绝唱 文章目录 vue-cli介绍安装创建项目入口文件修改文件夹名称的步骤ES6模块化单文件组件 vue生命周期vue动画 vue-cli 介绍 vue-lic是一个开发vue项目的脚…

Geotrust SSL证书

在数字化浪潮席卷全球的今天&#xff0c;互联网已成为信息交流与商业活动不可或缺的平台。然而&#xff0c;随着网络应用的深入&#xff0c;数据泄露、身份盗用、网络诈骗等安全问题也日益凸显&#xff0c;严重威胁着用户的隐私与财产安全。在这样的背景下&#xff0c;数字证书…

css实现边框双色凹凸半圆

整体效果如下图&#xff1a; 结构代码&#xff1a; <div classline-outside-wrap><div classwrap><img src../img/avatar2x.png/><div classcontent-wrap></div></div></div> 内凹框实现&#xff1a; .content-wrap{width:100%;he…

HarmonyOS:自定义组件冻结功能

一、简介 自定义组件冻结功能专为优化复杂UI页面的性能而设计&#xff0c;尤其适用于包含多个页面栈、长列表或宫格布局的场景。在这些情况下&#xff0c;当状态变量绑定了多个UI组件&#xff0c;其变化可能触发大量UI组件的刷新&#xff0c;进而导致界面卡顿和响应延迟。为了提…

Kafka相关API开发

(一)引入依赖 用API直接去操作kafka(读写数据)在实际开发中用的并不多&#xff0c;学习它主要还是为了加深对Kafka功能的理解。kafka的读写操作&#xff0c;实际开发中&#xff0c;是通过各类更上层的组件去实现。而这些组件在读写kafka数据时&#xff0c;用的当然是kafka的jav…

警务辅助人员管理系统小程序ssm+论文源码调试讲解

2系统关键技术 2.1 微信小程序 微信小程序&#xff0c;简称小程序&#xff0c;英文名Mini Program&#xff0c;是一种全新的连接用户与服务的方式&#xff0c;可以快速访问、快速传播&#xff0c;并具有良好的使用体验。 小程序的主要开发语言是JavaScript&#xff0c;它与普…

微服务设计模式 - 断路器模式 (Circuit Breaker Pattern)

微服务设计模式 - 断路器模式 (Circuit Breaker Pattern) 定义 断路器模式&#xff08;Circuit Breaker Pattern&#xff09;是云计算和微服务架构中的一种保护性设计模式&#xff0c;其目的是避免系统中的调用链出现故障时&#xff0c;导致系统瘫痪。通过断路器模式&#xff…

Yelp 数据集进行用户画像, 使用聚类做推荐

使用 Yelp 数据集进行用户画像&#xff08;User Profiling&#xff09;是一项有趣的任务&#xff0c;可以理解用户的偏好、行为和特征。以下是总结的一个基本的步骤&#xff0c;帮助构建用户画像 pandas 加载数据&#xff1a; import pandas as pd# 加载数据 users pd.read_…

DDRPHY数字IC后端设计实现系列专题之后端设计导入,IO Ring设计

本章详细分析和论述了 LPDDR3 物理层接口模块的布图和布局规划的设计和实 现过程&#xff0c;包括设计环境的建立&#xff0c;布图规划包括模块尺寸的确定&#xff0c;IO 单元、宏单元以及 特殊单元的摆放。由于布图规划中的电源规划环节较为重要&#xff0c; 影响芯片的布线资…

前端路由如何从0开始配置?vue-router 的使用

在 Web 开发中&#xff0c;路由是指根据 URL 的不同部分将请求分发到不同的处理函数或页面的过程。路由是单页应用&#xff08;SPA, Single Page Application&#xff09;和服务器端渲染&#xff08;SSR, Server-Side Rendering&#xff09;应用中的一个重要概念。 在开发中如何…

强化学习的数学原理-06随即近似理论和随机梯度下降

文章目录 Robbins-Monro algorithmStochastic gradient descentBGD、MBGD、 and SGDSummary Robbins-Monro algorithm 迭代式求平均数的算法 S t o c h a s t i c a p p r o x i m a t i o n ( S A ) Stochastic \; approximation \;(SA) Stochasticapproximation(SA)&#xf…

Apache Hive 通过Docker快速入门

QuickStarted 介绍 在伪分布式模式下在 docker 容器内运行 Apache Hive&#xff0c;以便为 Hive 提供以下快速启动/调试/准备测试环境 快速入门 步骤 1&#xff1a;拉取镜像 从 DockerHub 拉取镜像&#xff1a;https://hub.docker.com/r/apache/hive/tags。以下是最新的镜像…

【K8S系列】Kubernetes 中 NodePort 类型的 Service 无法访问的问题【已解决】

在 Kubernetes 中&#xff0c;NodePort 类型的 Service 允许用户通过每个节点的 IP 地址和指定的端口访问应用程序。如果 NodePort 类型的 Service 无法通过节点的 IP 地址和指定端口进行访问&#xff0c;可能会导致用户无法访问应用。本文将详细分析该问题的常见原因及其解决方…

逻辑卷动态扩容与缩容-----

一、创建逻辑卷 需求&#xff1a;创建一个2.5G大小的逻辑卷 思路&#xff1a; 1. 物理的设备 2. 将物理设备做成物理卷 pv 3. 创建卷组并将物理卷加入其中 vg 4. 创建逻辑卷 lv 5. 格式化逻辑卷 mkfs.ext4 6. 挂载使用 mount 步骤&#xff1a; 1. 物理设备【如何来分区】…

开关灯问题(c语言)

样例&#xff1a;10 10 &#xff0c;输出&#xff1a;1&#xff0c;4&#xff0c;9 5 5 &#xff0c;输出&#xff1a;1&#xff0c;4 代码如下 #include<stdio.h> //引入bool值的概念 #include<stdbool.h> int main() {int n 0;//n为灯的数量int m 0;…

扫雷游戏(C语言详解)

扫雷游戏&#xff08;C语言详解&#xff09; 放在最前面的1、前言&#xff08;扫雷游戏的简介&#xff09;2、扫雷游戏的规则&#xff08;简易版&#xff09;3、代码实现&#xff08;3.1&#xff09;提醒一下&#xff1a;( i ) 提醒1&#xff1a;( ii ) 提醒2&#xff1a; &…

在面试了些外包以后,我有了些自己的思考

大家好&#xff0c;我是洋子&#xff0c;最近公司在降本增效&#xff0c;需要把外包从北京迁移到陕西的某新一线城市&#xff0c;其实就是变相裁员&#xff0c;减少外包的成本&#xff0c;裁掉现有的员工&#xff0c;重新招聘新人 在整个测试行业&#xff0c;外包测试的比重是…