使用Python和OpenCV查找从摄像机到对象/标记的距离

 

 

 

几天前,PyImageSearch读者Cameron通过电子邮件发送了邮件,询问了寻找从相机到图像中对象/标记之间距离的方法。他花了一些时间进行研究,但没有找到实现方案。

我确切地知道卡梅伦的感受。几年前,我正在做一个小项目,分析棒球在离开投手的手并驶向本垒板时的运动。

使用运动分析和基于轨迹的跟踪,我能够找到/估计视频帧中的球位置。而且由于棒球的大小已知,因此我也能够估算出到本垒板的距离。

这是一个有趣的项目,尽管该系统不如我希望的那样精确-球移动如此之快的“运动模糊”使得很难获得高度准确的估算值。

我的项目肯定是一个“异常”情况,但总的来说,确定从相机到标记的距离实际上是计算机视觉/图像处理领域中一个非常研究好的问题。您会发现非常简单明了的技术,例如三角形相似性。而且,您可以使用相机模型的固有参数找到复杂的方法(尽管更为准确)。

在此博客文章中,我将向您展示Cameron和我如何提出一种解决方案来计算从相机到已知物体或标记的距离。

 

对象/标记与相机距离的三角形相似度

为了确定从相机到已知物体或标记的距离,我们将利用三角形相似度

三角形相似性是这样的:假设我们有一个标记或对象,宽度称为W ^。然后,我们将该标记放置在距相机一定距离D处。我们使用相机拍摄物体的图片,然后测量像素P的视在宽度。这使我们能够得出摄像机的可感知焦距F

F =(P x D)/ W

例如,假设我将标准的8.5 x 11 英寸纸(水平;W = 11D = 24英寸放在我的相机前面,然后拍照。当我测量图像中纸的宽度时,我注意到纸的感知宽度为P = 248像素

那么我的焦距F是:

F =(248px x 24in)/ 11in = 543.45

当我继续将相机移离对象/标记越来越近时,可以应用三角形相似度来确定对象到相机的距离:

D'=(宽x F)/ P

再说一次,为了使它更具体,假设我将我的相机移离标记笔3英尺(或36英寸),并拍摄了同一张纸的照片。通过自动图像处理,我可以确定现在感知到的纸张宽度为170像素。将其插入方程式,我们现在得到:

D'=(11英寸x 543.45)/ 170 = 35英寸

或大约36英寸,即3英尺。

注意:在本示例中拍摄照片时,我的卷尺有些松弛,因此结果相差约1英寸。此外,我还匆忙拍摄了照片,而不是在卷尺的脚标记上方100%拍摄,这增加了1英寸的误差。综上所述,三角形相似性仍然成立,您可以使用此方法轻松计算从对象或标记到相机的距离。

现在有意义吗?

太棒了 让我们进入一些代码,看看如何使用Python,OpenCV,图像处理和计算机视觉技术完成从相机到物体或标记的距离查找。

使用Python和OpenCV查找从相机到对象/标记的距离

让我们继续进行,开始这个项目。打开一个新文件,将其命名为 distance_to_camera py  ,我们将开始工作:

Find distance from camera to object using Python and OpenCV
 
 
 
 
 
Python
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 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)

我们要做的第一件事是导入必要的包(第2-5行)。我们将使用 路径   从 imutils   加载可用图像目录中。我们将使用NumPy进行数值处理,并使用 cv2   进行OpenCV绑定。

在这里,我们定义 find_marker   函数。此函数接受单个参数 image  ,用于查找要计算距离的对象。

在这种情况下,我们将使用8.5 x 11英寸的标准纸作为标记。

我们的首要任务是现在在图像中找到这张纸。

为此,我们将图像转换为灰度,将其稍微模糊以消除高频噪声,然后在9-11行上进行边缘检测  。

应用这些步骤后,我们的图像应如下所示:

图1:应用边缘检测找到我们的标记,在这种情况下,标记是一张纸。

如您所见,标记笔(纸)的边缘已经明显地暴露了出来。现在,我们要做的就是找到代表纸张的轮廓(即轮廓)。

我们 使用 cv2第15和16行找到标记  findContours  函数(小心处理不同的OpenCV版本),然后在第17行上确定具有最大面积的轮廓  。

我们假设最大面积的轮廓就是我们的纸。 该假设适用于此特定示例,但实际上在图像中查找标记是高度特定于应用程序的。

在我们的示例中,简单的边缘检测和找到最大轮廓效果很好。我们还可以通过应用轮廓逼近,舍弃不具有4个点的任何轮廓(因为一张纸是矩形,因此具有4个点),然后找到最大的4点轮廓,来使此示例更加健壮。

注意:有关此方法的更多信息,可以在构建脚踏式移动文档扫描器的这篇文章中找到。

在图像中查找标记的其他替代方案是利用颜色,以使标记的颜色与图像中其余场景的颜色实质上不同。您还可以应用诸如关键点检测,局部不变描述符和关键点匹配之类的方法来查找标记。但是,这些方法不在本文讨论范围之内,并且还是高度特定于应用程序的。

无论如何,现在我们有了与标记相对应的轮廓,我们将包含(x,y)坐标以及框的宽度和高度(以像素为单位)的边界框返回  到第20行的调用函数。

我们还快速定义一个函数,该函数使用上面详述的三角形相似度计算到对象的距离:

Find distance from camera to object using Python and OpenCV
 
 
 
 
 
Python
 
22
23
24
def distance_to_camera(knownWidth, focalLength, perWidth):
# compute and return the distance from the maker to the camera
return (knownWidth * focalLength) / perWidth

该函数接受一个 knownWidth  标记的,一个计算 长焦点  和图像(以像素为单位)中的对象的感知宽度,并适用上面详述来计算到对象的实际距离的三角形相似。

要了解我们如何利用这些功能,请继续阅读:

Find distance from camera to object using Python and OpenCV
 
 
 
 
 
Python
 
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 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

找到与图像中的对象或标记的距离的第一步是校准  和计算焦距。为此,我们需要知道:

  • 相机到物体的距离。
  • 该对象的宽度(以英寸,米等为单位)。注意: 也可以使用高度,但是本示例仅使用宽度。

让我们再说一秒钟,我们正在做  的不是真正的相机校准。真正的相机校准涉及相机的固有参数,您可以在此处进一步了解。

第28行,我们  将从摄像机到对象的已知KNOWN_DISTANCE初始化 为24英寸。在  第32行上,我们  将对象的KNOWN_WIDTH初始化 为11英寸(即,水平放置8.5 x 11英寸的标准纸)。

下一步很重要:  这是我们简单的校准步骤。

我们在第37行的 磁盘上加载了第一张图像  -我们将使用该图像作为校准图像。

加载图像后,我们在第38行找到图像中的纸  ,然后使用三角形相似度  在  第39行 计算出 focusLength

现在,我们已经“校准”了系统并拥有了 focusLength  ,我们可以很容易地计算后续图像中相机到标记的距离。

让我们看看如何做到这一点:

Find distance from camera to object using Python and OpenCV
 
 
 
 
 
Python
 
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# 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)

我们开始在第42行上遍历图像路径。

然后,对于列表中的每个图像,我们在第45行上将图像从磁盘上加载下来,在第46行上找到图像中的标记    然后在第47行上计算对象到摄像机的距离  。

从那里,我们只需在标记周围画一个边界框并在第50-57行显示距离  ( boxPoints   是在第50行计算的,请  注意处理不同的OpenCV版本)。

结果

要查看我们的脚本,请打开一个终端,导航到您的代码目录,然后执行以下命令:

Find distance from camera to object using Python and OpenCV
 
 
 
 
 
Shell
 
1
$ python distance_to_camera.py

如果一切顺利的话,你应该首先看到的结果 2ft.png  ,这是我们用来“校准”我们的系统和计算我们最初的图像 长焦点  :

图2:此图像用于计算系统的初始焦距。我们首先利用图像中对象/标记的已知宽度和到对象的已知距离开始。

从上图可以看出,  根据代码中的KNOWN_DISTANCE   和 KNOWN_WIDTH变量,可以正确确定焦距,并且到纸的距离为2英尺 。

现在我们有了焦距,我们可以在后续图像中计算到标记的距离:

图3:利用焦距确定我们的纸笔标记距相机大约3英尺。

在上面的示例中,我们的相机现在距离标记大约3英尺。

让我们尝试移回另一只脚:

图4:利用计算出的焦距确定我们的相机距离我们的标记大约4英尺。

再次提醒您,当我为该示例拍摄照片时,我仓促行事,并在卷尺上留下了太多的懈怠。此外,我也没有确保我的相机在脚标上对齐了100%,因此,在这些示例中,还是有大约1英寸的误差。

综上所述,本文详细介绍的三角形相似度方法仍然可以使用,并允许您查找图像中的对象或标记到相机的距离。

摘要

在此博客文章中,我们学习了如何确定图像中已知物体到相机的距离  。

为了完成此任务,我们利用了  三角形相似度,这需要我们在应用算法之前了解两个重要参数:

  1. 我们用作标记的对象在某种距离度量中的宽度(或高度),例如英寸或米。
  2.  步骤1中相机到标记的  距离(以英寸或米为单位)。

然后,可以使用计算机视觉和图像处理算法来自动确定以像素为单位的对象的感知宽度/高度,并完成三角形相似度并为我们提供焦距。

然后,在后续图像中,我们只需要找到标记/对象​​并利用计算出的焦距来确定距相机到对象的距离。

发布了48 篇原创文章 · 获赞 23 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/BC_COM/article/details/103077847