在这篇博文中,我将向你展示如何想出一个解决方案来计算我们的相机到一个已知物体或标记物的距离。
为了确定从我们的相机到一个已知物体或标记物的距离,我们将利用三角形相似性。
三角形相似性大致是这样的:假设我们有一个已知宽度为w
的标记或物体,然后将这个标记放置在距离相机D
的距离处。我们用相机拍下物体的照片,然后测量它的视宽度
(像素P)。这允许我们推导出相机的感知焦距F:F = (P x D) / W
例如,假设我放置一张8.5 x 11英寸
的标准纸(水平;W = 11
),离相机 D = 24英寸
,并拍一张照片。当我测量图像中纸张的宽度时,我注意到纸张的感知宽度是P = 248像素
。
焦距F是:F = (248px x 24in) / 11in = 543.45
当我继续移动我的相机离物体/标记物更近或更远时,我可以应用三角形相似度来确定物体到相机的距离:D’ = (W x F) / P
再一次,为了使这个更具体,让我们假设再向外移动相机3英尺,并拍摄一张相同的纸。通过自动图像处理,我能够确定这张纸的感知宽度现在是170像素
。把这个代入方程,我们得到:D’ = (11in x 543.45) / 170 = 35in
注意:当我为这个例子拍摄照片时,我的卷尺有一点松弛,因此结果大约差1英寸。此外,我还仓促地拍摄了照片,没有100%在卷尺上的脚标记上,这增加了1英寸的误差。尽管如此,三角形相似性仍然保持不变,你可以使用这种方法来计算从一个物体或标记到你的相机的距离。
让我们进入一些代码,看看如何使用Python、OpenCV、图像处理和计算机视觉技术来找到相机到一个对象或标记物的距离。
打开一个新文件,命名为distance_to_camera.py
,然后我们开始工作:
# import the necessary packages
from imutils import paths
import numpy as np
import imutils
import cv2
def find_marker(image):
# convert the image to grayscale, blur it, and detect edges
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(gray, 35, 125)
# find the contours in the edged image and keep the largest one;
# we'll assume that this is our piece of paper in the image
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
c = max(cnts, key = cv2.contourArea)
# compute the bounding box of the of the paper region and return it
return cv2.minAreaRect(c)
要做的第一件事是导入必要的包。使用来自imutils
的paths
来加载目录中的可用图像。我们将使用NumPy
进行数值处理,导入cv2
包。
然后定义find_marker
函数。这个函数只接受一个参数image
,用来寻找我们想要计算到的距离的对象。在这种情况下,我们使用一张8.5 x 11英寸
的标准纸作为我们的标记。
我们的第一个任务是在图像中找到这张纸。为了做到这一点,我们将把图像转换为灰度,稍微模糊它以去除高频噪声,并应用边缘检测。应用这些步骤后,我们的图像应该是这样的:
正如你所看到的,我们的标记(这张纸)的边缘已经明显地被发现了。现在我们需要做的就是找到代表这张纸的轮廓。
使用cv2.findContours函数
找到标记。然后选择面积最大的轮廓。我们假设面积最大的轮廓是这张纸。这个假设适用于这个特定的示例,但实际上在图像中找到标记是高度特定于应用程序的。
在我们的例子中,简单的边缘检测和寻找最大轮廓工作得很好。我们还可以通过应用轮廓近似来让这个例子更加稳健,丢弃任何没有4个点的轮廓(因为一张纸是矩形,因此有4个点),然后找到最大的4点轮廓。
在图像中寻找标记的其他替代方法是利用颜色,标记(纸张)的颜色与图像中场景的其他部分有本质上的不同。您还可以应用诸如关键点检测、局部不变描述符和关键点匹配等方法来查找标记;但是,这些方法超出了本文的范围。
无论如何,现在我们有了对应于标记的轮廓,我们将包含(x, y)坐标和框的宽度和高度(像素)的边界框返回给调用函数。
def distance_to_camera(knownWidth, focalLength, perWidth):
# compute and return the distance from the maker to the camera
return (knownWidth * focalLength) / perWidth
该函数接受标记的knownWidth
、计算的focalLength
和图像中对象的感知宽度(以像素度量),并应用上面详细介绍的三角形相似性来计算到对象的实际距离。
要了解如何使用这些函数,请继续阅读:
# initialize the known distance from the camera to the object, which in this case is 24 inches
KNOWN_DISTANCE = 24.0
# initialize the known object width, which in this case, the piece of paper is 12 inches wide
KNOWN_WIDTH = 11.0
# load the furst image that contains an object that is KNOWN TO BE 2 feet
# from our camera, then find the paper marker in the image, and initialize the focal length
image = cv2.imread("images/2ft.png")
marker = find_marker(image)
focalLength = (marker[1][0] * KNOWN_DISTANCE) / KNOWN_WIDTH
确定图像中物体或标记物的距离的第一步是标定和计算焦距。要做到这一点,我们需要知道:
我们所做的并不是真正的相机标定,真正的相机标定涉及相机的固有参数。
我们将已知的KNOWN_DISTANCE
从相机到对象的距离初始化为24英寸
。将对象的KNOWN_WIDTH
初始化为11英寸
(即标准8.5 x 11英寸
纸)。
下一步很重要:这是我们简单的标定步骤。
我们将从磁盘上加载第一张图像—我们将使用该图像作为标定图像。(也就是说需要一张标定图像来得到focalLength
)
加载图像后,我们在图像中找到一张纸,然后使用三角形相似性计算focalLength
。
现在我们已经“标定”了我们的系统,并有了focalLength
,我们可以很容易地计算从相机到图像中标记的距离。
# loop over the images
for imagePath in sorted(paths.list_images("images")):
# load the image, find the marker in the image, then compute the
# distance to the marker from the camera
image = cv2.imread(imagePath)
marker = find_marker(image)
inches = distance_to_camera(KNOWN_WIDTH, focalLength, marker[1][0])
# draw a bounding box around the image and display it
box = cv2.cv.BoxPoints(marker) if imutils.is_cv2() else cv2.boxPoints(marker)
box = np.int0(box)
cv2.drawContours(image, [box], -1, (0, 255, 0), 2)
cv2.putText(image, "%.2fft" % (inches / 12),
(image.shape[1] - 200, image.shape[0] - 20), cv2.FONT_HERSHEY_SIMPLEX,
2.0, (0, 255, 0), 3)
cv2.imshow("image", image)
cv2.waitKey(0)
我们开始循环图像路径。
然后,对于列表中的每个图像,我们将图像从磁盘上加载,找到图像中的标记,然后计算对象到相机的距离。
我们简单地在标记周围画一个边界框,并显示距离。
这种方法的主要限制是,您需要对正在检测的对象有一个直线视图。当视点变成角度时,它扭曲了边界框的计算,从而扭曲了距离导致距离不准。
要查看脚本的运行情况,请打开一个终端,导航到你的代码目录,并执行以下命令:
python distance_to_camera.py
如果一切顺利,你首先会看到我们用来标定系统和计算初始focalLength
的图像:
从上面的图像中,我们可以看到我们的焦距根据代码中的KNOWN_DISTANCE
和KNOWN_WIDTH
变量确定,相机到纸张的距离是2英尺
。
现在我们有了焦距,我们可以计算相机到后续图像中的标记的距离:
同样,需要注意的是,当我为这个例子捕捉照片时,我做得太匆忙了,在卷尺上留下了太多的松弛。此外,我也没有确保我的相机 100% 排列在脚标记上,所以再次,在这些例子中有大约1英寸的误差。
也就是说,本文中详述的三角形相似性方法仍然有效,它可以让你找到图像中物体或标记到相机的距离。
为了进行距离测量,我们首先需要标定我们的系统。在本文中,我们使用了简单的“像素/度量”技术。
但是,通过计算外部和内部参数,对相机进行适当的标定,可以获得更好的精度:
外部参数
是用于将物体从世界坐标系转换到相机坐标系的旋转和平移矩阵内部参数
是相机的内部参数,例如焦距,用来将信息转换为像素最常见的方法是使用OpenCV进行棋盘格摄像机标定。这样做将消除径向畸变和切向畸变,这两种畸变都会影响输出图像,从而影响图像中物体的输出测量。
作为人类,我们认为拥有两只眼睛是理所当然的。即使我们在事故中失去一只眼睛,我们仍然可以看到和感知我们周围的世界。然而,只有一只眼睛,我们失去了重要的信息-深度。
深度感知给了我们从我们所站的地方到面前物体的距离感知。为了感知深度,我们需要两只眼睛。
另一方面,大多数相机只有一只眼睛,这就是为什么使用标准2D相机获取深度信息如此具有挑战性。
为了获得更准确的深度信息,你应该考虑使用激光雷达。激光雷达使用光传感器来测量传感器和它前面的任何物体之间的距离。
激光雷达在自动驾驶汽车中尤其受欢迎,因为仅靠摄像头是远远不够的。
链接:https://pan.baidu.com/s/1L-DSBMzUTTBsaWU1Va3J1g
提取码:123a
在这篇博文中,我们学习了如何确定图像中已知物体到相机的距离。
为了完成这个任务,我们利用了三角形相似性,这要求我们在应用我们的算法之前知道两个重要的参数:
然后,计算机视觉和图像处理算法可以用来自动确定感知到的物体的宽度/高度(以像素为单位),通过三角形相似性,得到相机的焦距。然后,在随后的图像中,我们只需要找到我们的标记/对象,并利用计算的焦距来确定从相机到对象的距离。
https://www.pyimagesearch.com/2015/01/19/find-distance-camera-objectmarker-using-python-opencv/