【python】OpenCV—findContours(4.4)

打印 上一主题 下一主题

主题 1379|帖子 1379|积分 4137



  
1、功能形貌

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

导入库函数,读入图片,判断图片是否存在,显示图片
  1. import cv2 as cv
  2. from math import atan2, cos, sin, sqrt, pi
  3. import numpy as np
  4. # Load the image
  5. img = cv.imread("1.jpeg")
  6. # Was the image there?
  7. if img is None:
  8.     print("Error: File not found")
  9.     exit(0)
  10. cv.imshow('Input Image', img)
复制代码
灰度化图片,二值化图片,为后续找轮廓做预备
  1. # Convert image to grayscale
  2. gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
  3. cv.imwrite("gray.jpg", gray)
  4. # Convert image to binary
  5. _, bw = cv.threshold(gray, 50, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
  6. cv.imwrite("bw.jpg", bw)
复制代码
找轮廓
  1. # Find all the contours in the thresholded image
  2. contours, _ = cv.findContours(bw, cv.RETR_LIST, cv.CHAIN_APPROX_NONE)
复制代码
遍历轮廓,剔除面积过小大概过大的轮廓,绘制轮廓,调用 getOrientation 获取物体方向并绘制,并显示绘制结果
  1. for i, c in enumerate(contours):
  2.     # Calculate the area of each contour
  3.     area = cv.contourArea(c)
  4.     # Ignore contours that are too small or too large
  5.     if area < 1000 or 100000 < area:
  6.         continue
  7.     # Draw each contour only for visualisation purposes with red color
  8.     cv.drawContours(img, contours, i, (0, 0, 255), 2)
  9.     # Find the orientation of each shape
  10.     getOrientation(c, img)
  11. cv.imshow('Output Image', img)
  12. cv.waitKey(0)
  13. cv.destroyAllWindows()
  14. # Save the output image to the current directory
  15. cv.imwrite("output_img.jpg", img)
复制代码
下面看看 getOrientation(c, img) 的实现
  1. def getOrientation(pts, img):
  2.     ## [pca]
  3.     # Construct a buffer used by the pca analysis
  4.     sz = len(pts)  # 轮廓的关键点数, pts (446, 1, 2)
  5.     data_pts = np.empty((sz, 2), dtype=np.float64)  # (446, 2)
  6.     for i in range(data_pts.shape[0]):
  7.         data_pts[i, 0] = pts[i, 0, 0]
  8.         data_pts[i, 1] = pts[i, 0, 1]
  9.     # Perform PCA analysis
  10.     mean = np.empty((0))
  11.     mean, eigenvectors, eigenvalues = cv.PCACompute2(data_pts, mean)
  12.     # Store the center of the object
  13.     cntr = (int(mean[0, 0]), int(mean[0, 1]))  # (177, 349)
  14.     ## [pca]
  15.     ## [visualization]
  16.     # Draw the principal components
  17.     cv.circle(img, cntr, 3, (255, 0, 255), 2)
  18.     p1 = (cntr[0] + 0.025 * eigenvectors[0, 0] * eigenvalues[0, 0],
  19.           cntr[1] + 0.025 * eigenvectors[0, 1] * eigenvalues[0, 0])
  20.     p2 = (cntr[0] - 0.025 * eigenvectors[1, 0] * eigenvalues[1, 0],
  21.           cntr[1] - 0.025 * eigenvectors[1, 1] * eigenvalues[1, 0])
  22.    #  乘以0.25是为了放大这个距离,使其在图像上更加明显。
  23.     drawAxis(img, cntr, p1, (255, 255, 0), 1)
  24.     drawAxis(img, cntr, p2, (0, 0, 255), 5)
  25.     angle = atan2(eigenvectors[0, 1], eigenvectors[0, 0])  # orientation in radians
  26.     ## [visualization]
  27.     # Label with the rotation angle
  28.     # label = "  Rotation Angle: " + str(-int(np.rad2deg(angle)) - 90) + " degrees"
  29.     label = str(-int(np.rad2deg(angle)) - 90) + " degrees"
  30.     textbox = cv.rectangle(img, (cntr[0]+15, cntr[1] - 50), (cntr[0] + 130, cntr[1] - 15), (255, 255, 255), -1)
  31.     cv.putText(img, label, (cntr[0]+15, cntr[1]-25), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1, cv.LINE_AA)
  32.     return angle
复制代码
其中 cv2.PCACompute2 获取特征值和特征向量
p1、p2 是计算特征向量乘以特征值,方便后续可视化物体方向,0.025 是系数,影响的是绘制时候的长度
drawAxis 绘制箭头,展示物体方向
  1. def drawAxis(img, p_, q_, color, scale):
  2.     p = list(p_)
  3.     q = list(q_)
  4.     ## [visualization1]
  5.     angle = atan2(p[1] - q[1], p[0] - q[0])  # angle in radians
  6.     hypotenuse = sqrt((p[1] - q[1]) **2 + (p[0] - q[0])**2)
  7.     # Here we lengthen the arrow by a factor of scale
  8.     q[0] = p[0] - scale * hypotenuse * cos(angle)
  9.     q[1] = p[1] - scale * hypotenuse * sin(angle)
  10.     cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), color, 3, cv.LINE_AA)
  11.     # create the arrow hooks 绘制箭头的钩子
  12.     p[0] = q[0] + 9 * cos(angle + pi / 4)
  13.     p[1] = q[1] + 9 * sin(angle + pi / 4)
  14.     cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), color, 3, cv.LINE_AA)
  15.     p[0] = q[0] + 9 * cos(angle - pi / 4)
  16.     p[1] = q[1] + 9 * sin(angle - pi / 4)
  17.     cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), color, 3, cv.LINE_AA)
  18.     ## [visualization1]
复制代码
可以看到有三个 cv2.line,第一个是绘制方向的直线,第二个和第三个分别绘制箭头,偏离直线 ±45°
scale 控制箭头直线的长度
3、完备代码

  1. import cv2 as cvfrom math import atan2, cos, sin, sqrt, piimport numpy as npdef drawAxis(img, p_, q_, color, scale):
  2.     p = list(p_)
  3.     q = list(q_)
  4.     ## [visualization1]
  5.     angle = atan2(p[1] - q[1], p[0] - q[0])  # angle in radians
  6.     hypotenuse = sqrt((p[1] - q[1]) **2 + (p[0] - q[0])**2)
  7.     # Here we lengthen the arrow by a factor of scale
  8.     q[0] = p[0] - scale * hypotenuse * cos(angle)
  9.     q[1] = p[1] - scale * hypotenuse * sin(angle)
  10.     cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), color, 3, cv.LINE_AA)
  11.     # create the arrow hooks 绘制箭头的钩子
  12.     p[0] = q[0] + 9 * cos(angle + pi / 4)
  13.     p[1] = q[1] + 9 * sin(angle + pi / 4)
  14.     cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), color, 3, cv.LINE_AA)
  15.     p[0] = q[0] + 9 * cos(angle - pi / 4)
  16.     p[1] = q[1] + 9 * sin(angle - pi / 4)
  17.     cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), color, 3, cv.LINE_AA)
  18.     ## [visualization1]
  19. def getOrientation(pts, img):
  20.     ## [pca]
  21.     # Construct a buffer used by the pca analysis
  22.     sz = len(pts)  # 轮廓的关键点数, pts (446, 1, 2)
  23.     data_pts = np.empty((sz, 2), dtype=np.float64)  # (446, 2)
  24.     for i in range(data_pts.shape[0]):
  25.         data_pts[i, 0] = pts[i, 0, 0]
  26.         data_pts[i, 1] = pts[i, 0, 1]
  27.     # Perform PCA analysis
  28.     mean = np.empty((0))
  29.     mean, eigenvectors, eigenvalues = cv.PCACompute2(data_pts, mean)
  30.     # Store the center of the object
  31.     cntr = (int(mean[0, 0]), int(mean[0, 1]))  # (177, 349)
  32.     ## [pca]
  33.     ## [visualization]
  34.     # Draw the principal components
  35.     cv.circle(img, cntr, 3, (255, 0, 255), 2)
  36.     p1 = (cntr[0] + 0.025 * eigenvectors[0, 0] * eigenvalues[0, 0],
  37.           cntr[1] + 0.025 * eigenvectors[0, 1] * eigenvalues[0, 0])
  38.     p2 = (cntr[0] - 0.025 * eigenvectors[1, 0] * eigenvalues[1, 0],
  39.           cntr[1] - 0.025 * eigenvectors[1, 1] * eigenvalues[1, 0])
  40.    #  乘以0.25是为了放大这个距离,使其在图像上更加明显。
  41.     drawAxis(img, cntr, p1, (255, 255, 0), 1)
  42.     drawAxis(img, cntr, p2, (0, 0, 255), 5)
  43.     angle = atan2(eigenvectors[0, 1], eigenvectors[0, 0])  # orientation in radians
  44.     ## [visualization]
  45.     # Label with the rotation angle
  46.     # label = "  Rotation Angle: " + str(-int(np.rad2deg(angle)) - 90) + " degrees"
  47.     label = str(-int(np.rad2deg(angle)) - 90) + " degrees"
  48.     textbox = cv.rectangle(img, (cntr[0]+15, cntr[1] - 50), (cntr[0] + 130, cntr[1] - 15), (255, 255, 255), -1)
  49.     cv.putText(img, label, (cntr[0]+15, cntr[1]-25), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1, cv.LINE_AA)
  50.     return angle
  51. # Load the imageimg = 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
  52. gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
  53. cv.imwrite("gray.jpg", gray)
  54. # Convert image to binary
  55. _, bw = cv.threshold(gray, 50, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
  56. cv.imwrite("bw.jpg", bw)
  57. # Find all the contours in the thresholded image
  58. contours, _ = cv.findContours(bw, cv.RETR_LIST, cv.CHAIN_APPROX_NONE)
  59. for i, c in enumerate(contours):
  60.     # Calculate the area of each contour
  61.     area = cv.contourArea(c)
  62.     # Ignore contours that are too small or too large
  63.     if area < 1000 or 100000 < area:
  64.         continue
  65.     # Draw each contour only for visualisation purposes with red color
  66.     cv.drawContours(img, contours, i, (0, 0, 255), 2)
  67.     # Find the orientation of each shape
  68.     getOrientation(c, img)
  69. cv.imshow('Output Image', img)
  70. cv.waitKey(0)
  71. cv.destroyAllWindows()
  72. # Save the output image to the current directory
  73. 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 函数的简单示例:
  1. import numpy as np  
  2. import cv2  
  3.   
  4. # 生成一组多元正态分布的数据  
  5. mean = [20, 20]  
  6. cov = [[5, 5], [5, 25]]  
  7. X = np.random.multivariate_normal(mean, cov, 500)  
  8.   
  9. # 执行PCA计算  
  10. mean, eigenvectors, eigenvalues = cv2.PCACompute2(X.T)  
  11.   
  12. # 输出结果  
  13. print("Mean:", mean)  
  14. print("Eigenvectors:\n", eigenvectors)  
  15. 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/

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

光之使者

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表