本文来源公众号“OpenCV与AI深度学习”,仅用于学术分享,侵权删,干货满满。
原文链接:实战 | 使用OpenCV确定对象的方向(附源码)
导读
本文将介绍如何使用OpenCV确定对象的方向(即旋转角度,以度为单位)。
1 先决条件
安装Python3.7或者更高版本。可以参考下文链接:
https://automaticaddison.com/how-to-set-up-anaconda-for-windows-10/
或者自行下载安装:https://www.python.org/getit/
2 相关包安装与设置
在我们开始之前,让我们确保我们已经安装了所有的软件包。检查您的机器上是否安装了OpenCV 。如果你使用 Anaconda,你可以输入:
conda install -c conda-forge opencv
或者使用pip安装,指令:
pip install opencv-python
安装科学计算库 Numpy:
pip install numpy
3 准备测试图像
找一张图片。我的输入图像宽度为 1200 像素,高度为 900 像素。我的输入图像的文件名是input_img.jpg。
4 编写代码
这是代码。它接受一个名为input_img.jpg的图像并输出一个名为output_img.jpg的带注释的图像。部分代码来自官方 OpenCV 实现:
https://docs.opencv.org/4.x/d1/dee/tutorial_introduction_to_pca.html
代码示例:
import cv2 as cv
from math import atan2, cos, sin, sqrt, pi
import numpy as npdef 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 radianshypotenuse = sqrt((p[1] - q[1]) * (p[1] - q[1]) + (p[0] - q[0]) * (p[0] - q[0]))# Here we lengthen the arrow by a factor of scaleq[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 hooksp[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 analysissz = len(pts)data_pts = np.empty((sz, 2), dtype=np.float64)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 analysismean = np.empty((0))mean, eigenvectors, eigenvalues = cv.PCACompute2(data_pts, mean)# Store the center of the objectcntr = (int(mean[0,0]), int(mean[0,1]))## [pca]## [visualization]# Draw the principal componentscv.circle(img, cntr, 3, (255, 0, 255), 2)p1 = (cntr[0] + 0.02 * eigenvectors[0,0] * eigenvalues[0,0], cntr[1] + 0.02 * eigenvectors[0,1] * eigenvalues[0,0])p2 = (cntr[0] - 0.02 * eigenvectors[1,0] * eigenvalues[1,0], cntr[1] - 0.02 * eigenvectors[1,1] * eigenvalues[1,0])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 anglelabel = " Rotation Angle: " + str(-int(np.rad2deg(angle)) - 90) + " degrees"textbox = cv.rectangle(img, (cntr[0], cntr[1]-25), (cntr[0] + 250, cntr[1] + 10), (255,255,255), -1)cv.putText(img, label, (cntr[0], cntr[1]), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,0), 1, cv.LINE_AA)return angle# Load the image
img = cv.imread("input_img.jpg")# 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)# Convert image to binary
_, bw = cv.threshold(gray, 50, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)# 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 contourarea = cv.contourArea(c)# Ignore contours that are too small or too largeif area < 3700 or 100000 < area:continue# Draw each contour only for visualisation purposescv.drawContours(img, contours, i, (0, 0, 255), 2)# Find the orientation of each shapegetOrientation(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)
5 运行结果
使用PCA(主成分分析)方法获取物体的主方向,效果如下:
了解旋转轴
每个对象的正 x 轴是红线。每个对象的正 y 轴是蓝线。
全局正x 轴从左到右水平穿过图像。全局正z 轴指向此页外。全局正y 轴从图像底部垂直指向图像顶部。
使用右手定则测量旋转,将四根手指伸直(食指到小指)沿全局正 x 轴方向伸出。
然后将四根手指逆时针旋转 90 度。您的指尖指向正 y 轴,而您的拇指指向页面外指向正 z 轴。
计算0~180°之间的方向
如果我们要计算对象的方向并确保结果始终在 0 到 180 度之间,我们可以使用以下代码:
# This programs calculates the orientation of an object.
# The input is an image, and the output is an annotated image
# with the angle of otientation for each object (0 to 180 degrees)import cv2 as cv
from math import atan2, cos, sin, sqrt, pi
import numpy as np# Load the image
img = cv.imread("input_img.jpg")# 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)# Convert image to binary
_, bw = cv.threshold(gray, 50, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)# 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 contourarea = cv.contourArea(c)# Ignore contours that are too small or too largeif area < 3700 or 100000 < area:continue# cv.minAreaRect returns:# (center(x, y), (width, height), angle of rotation) = cv2.minAreaRect(c)rect = cv.minAreaRect(c)box = cv.boxPoints(rect)box = np.int0(box)# Retrieve the key parameters of the rotated bounding boxcenter = (int(rect[0][0]),int(rect[0][1])) width = int(rect[1][0])height = int(rect[1][1])angle = int(rect[2])if width < height:angle = 90 - angleelse:angle = -anglelabel = " Rotation Angle: " + str(angle) + " degrees"textbox = cv.rectangle(img, (center[0]-35, center[1]-25), (center[0] + 295, center[1] + 10), (255,255,255), -1)cv.putText(img, label, (center[0]-50, center[1]), cv.FONT_HERSHEY_SIMPLEX, 0.7, (0,0,0), 1, cv.LINE_AA)cv.drawContours(img,[box],0,(0,0,255),2)cv.imshow('Output Image', img)
cv.waitKey(0)
cv.destroyAllWindows()# Save the output image to the current directory
cv.imwrite("min_area_rec_output.jpg", img)
最终输出结果:
参考链接:https://automaticaddison.com/how-to-determine-the-orientation-of-an-object-using-opencv/
THE END!
文章结束,感谢阅读。您的点赞,收藏,评论是我继续更新的动力。大家有推荐的公众号可以评论区留言,共同学习,一起进步。