文章目录
- 1、功能描述
- 2、原理分析
- 3、代码实现
- 4、效果展示
- 5、完整代码
- 6、参考
1、功能描述
输入图片,计算出图片中的目标到相机间的距离
2、原理分析
用最简单的三角形相似性
已知参数,物体的宽度 W W W,物体到相机的距离 D D D,物体的宽在画面中的像素数 P P P,可以求出相机的焦距 F F F
F = P × D W F = \frac{P \times D}{W} F=WP×D
后续移动物体到不同的距离, W W W 已知, F F F 已知,通过 P P P 即可计算物体到相机间的距离 D D D
D = F × W P D = \frac{F \times W}{ P} D=PF×W
该方法缺点,物体需正对镜头,不然角度产生的物体形变会影响测量精度
可以利用标定求出相机参数,这样应用的鲁棒性会更高
3、代码实现
# import the necessary packages
from imutils import paths
import numpy as np
import imutils
import cv2
# 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
D,KNOWN_WIDTH
W,读入图片,通过找到画面中的物体,求出来 P,计算 focalLength
F
计算 P 是通过 find_marker
函数实现的
def find_marker(image):
# convert the image to grayscale, blur it, and detect edges
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# cv2.imwrite("gray.jpg", gray)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
# cv2.imwrite("gray-blur.jpg", gray)
edged = cv2.Canny(gray, 35, 125)
# cv2.imwrite("edged.jpg", edged)
# 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 = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# cnts = imutils.grab_contours(cnts)
# img_copy = image.copy()
# cv2.drawContours(img_copy, cnts, -1, (0,255,0))
# cv2.imwrite("draw_edged.jpg", img_copy)
c = max(cnts, key=cv2.contourArea)
# compute the bounding box of the of the paper region and return it
return cv2.minAreaRect(c)
输入图片,灰度化,高斯模糊,Canny 算子进行边缘检测,findContours
找出潜在轮廓,求面积最大的轮廓max(cnts, key=cv2.contourArea)
,求最大面积轮廓的最小外接矩阵
为什么要通过求最大面积轮廓的最小外接矩阵来定位到画面中的物品呢?这里比较灵活,可以尝试其他任何方法,核心目的是找出画面中物体的宽,只是正好样例图片可以
现在开始测试其他距离的效果
# 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.BoxPoints(marker) if imutils.is_cv2() else cv2.boxPoints(marker)
box = np.intp(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)
遍历图片,求最大面积轮廓的最小外接矩阵,调用 distance_to_camera
,求实际距离
绘制最大轮廓的最小外接矩阵,注释上距离
这里 12 表示下面的转换关系,foot 和 inches
def distance_to_camera(knownWidth, focalLength, perWidth):
# compute and return the distance from the maker to the camera
return (knownWidth * focalLength) / perWidth
根据 W、F 和 P 来求 D
4、效果展示
输入图片
灰度化后的结果
高斯模糊
Canny 算子计算边缘
找出画面中所有的轮廓
找出轮廓,获取到 P,结合已知的 W 和 D,求出 F
换一个距离,测试一下算法,计算得到 P,根据已知的 W 和 F,求出 D
再换一个距离,测试一下算法,计算得到 P,根据已知的 W 和 F,求出 D
5、完整代码
# USAGE
# python 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)
# cv2.imwrite("gray.jpg", gray)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
# cv2.imwrite("gray-blur.jpg", gray)
edged = cv2.Canny(gray, 35, 125)
# cv2.imwrite("edged.jpg", edged)
# 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 = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# cnts = imutils.grab_contours(cnts)
# img_copy = image.copy()
# cv2.drawContours(img_copy, cnts, -1, (0,255,0))
# cv2.imwrite("draw_edged.jpg", img_copy)
c = max(cnts, key=cv2.contourArea)
# compute the bounding box of the of the paper region and return it
return cv2.minAreaRect(c)
def distance_to_camera(knownWidth, focalLength, perWidth):
# compute and return the distance from the maker to the camera
return (knownWidth * focalLength) / perWidth
# 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
# 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.BoxPoints(marker) if imutils.is_cv2() else cv2.boxPoints(marker)
box = np.intp(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)
6、参考
- 使用Python和OpenCV查找相机到物体/标记物的距离(1)
- https://pyimagesearch.com/2015/01/19/find-distance-camera-objectmarker-using-python-opencv/
- 【python】OpenCV—findContours(4.1)
- 【python】OpenCV—findContours(4.2)
- 【python】OpenCV—findContours(4.3)
- 【python】OpenCV—findContours(4.4)