攝像頭單目測距原理及實現
一.測距原理
空間的深度或距離等數據的攝像頭。
人的眼睛長在頭部的前方,兩只眼的視野范圍重疊,兩眼同時看某一物體時,產生的視覺稱為雙眼視覺。
雙眼視覺的優點是可以彌補單眼視野中的盲區缺損,擴大視野,並產生立體視覺。
也就是說,假如只有一只眼睛,失去立體視覺后,人判斷距離的能力將會下降。
這也就是單目失明的人不能考取駕照的原因。
單純的單目視覺測距,必須已知一個確定的長度。
f為攝像頭的焦距,c為鏡頭光心。物體發出的光經過相機的光心,然后成像於圖像傳感器或者也可以說是像平面上,如果設物體所在平面與相機平面的距離為d,物體實際高度為H,在傳感器上的高度為h,H一定要是已知的,我們才能求得距離d。
假設我們有一個寬度為 W 的目標或者物體。然后我們將這個目標放在距離我們的相機為 D 的位置。我們用相機對物體進行拍照並且測量物體的像素寬度 P 。這樣我們就得出了相機焦距的公式:
F = (P x D) / W
例如,假設現在我們有一張A4紙(8.27in x 11.69in), in代表英寸,1in = 25.4mm。紙張寬度W=11.69in,相機距離紙張的距離D = 32in,此時拍下的照片中A4紙的像素寬度為P=192px(我的相機實際測量得到的值)。
此時我們可以算出焦距F=(192x30)/11.69。
當我們將攝像頭遠離或者靠近A4紙時,就可以用相似三角形得到相機距離物體的距離。
此時的距離: D’ = (W’ x F ) / P’。
(注意:這里測量的距離是相機到物體的垂直距離,產生夾角,測量的結果就不准確了。)
二.測距步驟:
1. 使用攝像機采集道路前方的圖像;
2. 在道路區域對物體進行檢測,通過矩形框將物體形狀框出來。
3. 結合矩形框信息,找到該矩形框底邊的兩個像平面坐標,分別記為(u1,v1)和(u2,v2);
4. 使用幾何關系推導法,由像平面坐標點(u1, v1)、(u2, v2)推導出道路平面坐標(x1,y1)、(x2, y2);(投影到地面上,z軸為0)
5. 通過歐氏距離公式計算出d。
三.難點整理:
1.圖像畸變矯正模型的理解;
(標定參數,內參矩陣,畸變矩陣,外參矩陣(平移、旋轉向量矩陣))
2.像素坐標與世界坐標公式的推導及驗證;
3.測距方法,對於檢測物體在攝像頭前方、左側、右側的判別思路;
4.弄清反畸變;對於畸變矯正后的圖像中的檢測框中的點進行反畸變處理。
四.相機鏡頭畸變矯正-->得到相機的內外參數、畸變參數矩陣
1. 外參數矩陣。世界坐標經過旋轉和平移,然后落到另一個現實世界點(攝像機坐標)上。
2. 內參數矩陣。告訴你上述那個點在1的基礎上,是如何繼續經過攝像機的鏡頭、並通過針孔成像和電子轉化而成為像素點的。
3. 畸變矩陣。告訴你為什么上面那個像素點並沒有落在理論計算該落在的位置上,還產生了一定的偏移和變形.
五.實現代碼
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Date: 18-10-29
import numpy as np # 導入numpy庫
import cv2 # 導入Opencv庫
NOWN_DISTANCE = 32 # 這個距離自己實際測量一下
KNOWN_WIDTH = 11.69 # A4紙的寬度
KNOWN_HEIGHT = 8.27
IMAGE_PATHS = ["Picture1.jpg", "Picture2.jpg", "Picture3.jpg"] # 將用到的圖片放到了一個列表中
# 定義目標函數
def find_marker(image):
gray_img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 將彩色圖轉化為灰度圖
gray_img = cv2.GaussianBlur(gray_img, (5, 5), 0) # 高斯平滑去噪
edged_img = cv2.Canny(gray_img, 35, 125) # Canny算子閾值化
cv2.imshow("降噪效果圖", edged_img) # 顯示降噪后的圖片
# 獲取紙張的輪廓數據
img, countours, hierarchy = cv2.findContours(edged_img.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# print(len(countours))
c = max(countours, key=cv2.contourArea) # 獲取最大面積對應的點集
rect = cv2.minAreaRect(c) # 最小外接矩形
return rect
# 定義距離函數
def distance_to_camera(knownWidth, focalLength, perWidth):
return (knownWidth * focalLength) / perWidth
# 計算攝像頭的焦距(內參)
def calculate_focalDistance(img_path):
first_image = cv2.imread(img_path) # 這里根據准備的第一張圖片,計算焦距
# cv2.imshow('first image', first_image)
marker = find_marker(first_image) # 獲取矩形的中心點坐標,長度,寬度和旋轉角度
focalLength = (marker[1][0] * KNOWN_DISTANCE) / KNOWN_WIDTH # 獲取攝像頭的焦距
# print(marker[1][0])
print('焦距(focalLength) = ', focalLength) # 打印焦距的值
return focalLength
# 計算攝像頭到物體的距離
def calculate_Distance(image_path, focalLength_value):
image = cv2.imread(image_path)
# cv2.imshow("原圖", image)
marker = find_marker(image) # 獲取矩形的中心點坐標,長度,寬度和旋轉角度, marke[1][0]代表寬度
distance_inches = distance_to_camera(KNOWN_WIDTH, focalLength_value, marker[1][0])
box = cv2.boxPoints(marker)
# print("Box = ", box)
box = np.int0(box)
print("Box = ", box)
cv2.drawContours(image, [box], -1, (0, 255, 0), 2) # 繪制物體輪廓
cv2.putText(image, "%.2fcm" % (distance_inches * 2.54), (image.shape[1] - 300, image.shape[0] - 20),
cv2.FONT_HERSHEY_SIMPLEX, 2.0, (0, 255, 0), 3)
cv2.imshow("單目測距", image)
if __name__ == "__main__":
img_path = "Picture1.jpg"
focalLength = calculate_focalDistance(img_path)
for image_path in IMAGE_PATHS:
calculate_Distance(image_path, focalLength)
cv2.waitKey(0)
cv2.destroyAllWindows()