赞
踩
前言:虽然计算三维坐标已经很多大佬研究过了,但是网上能用的好少啊。原理不多解释了,直接上程序。
- import numpy as np
- import cv2
包括矩阵处理和图像处理的两个基本库
由于本人太穷,根本搞不起较好的双目相机,于是借用opencv自带的图片。安装opencv后,在opencv\sources\samples\data下,有官方自带的图片,包括左右相机01-14(没有10)共26张双目相机照片,就以他们为例。
打开matlab进行双目相机标定(或者用opencv进行标定),由于未知标定板大小,据官方文件,其标定板可能是10mm*10mm(因为他默认大小参数是1.0,所以我猜是10mm,如有错误请大佬指出;其次,标定板大小只影响旋转和平移矩阵,对内参没影响)标定结果如下:
- # 左/右相机内参数、旋转、平移矩阵
- leftIntrinsic = np.array([[533.9634, 0, 342.6315],
- [0, 533.9448, 234.3696],
- [0, 0, 1]])
- leftRotation = np.array([[1, 0, 0],
- [0, 1, 0],
- [0, 0, 1]])
- leftTranslation = np.array([[0],
- [0],
- [0]])
- rightIntrinsic = np.array([[537.6219, 0, 326.7537],
- [0, 537.2332, 250.4047],
- [0, 0, 1]])
- rightRotation = np.array([[1, -0.0033, -0.0055],
- [0.0033, 1, 0.0094],
- [0.0054, -0.0094, 0.9999]])
- rightTranslation = np.array([[-33.2347],
- [0.4254],
- [0.0085]])
matlab中,我只设置了三参数模型,标定板大小为10mm,其他默认,点击Calibrate。matlab操作不多介绍。
以左相片建立空间坐标系,所以左相机旋转和平移矩阵为单位矩阵和零矩阵。右相机的旋转和平移矩阵使用两相机间的旋转和平移矩阵。
- # 函数参数为左右相片同名点的像素坐标,获取方式后面介绍
- # lx,ly为左相机某点像素坐标,rx,ry为右相机对应点像素坐标
- def uvToXYZ(lx, ly, rx, ry):
- mLeft = np.hstack([leftRotation, leftTranslation])
- mLeftM = np.dot(leftIntrinsic, mLeft)
- mRight = np.hstack([rightRotation, rightTranslation])
- mRightM = np.dot(rightIntrinsic, mRight)
- A = np.zeros(shape=(4, 3))
- for i in range(0, 3):
- A[0][i] = lx * mLeftM[2, i] - mLeftM[0][i]
- for i in range(0, 3):
- A[1][i] = ly * mLeftM[2][i] - mLeftM[1][i]
- for i in range(0, 3):
- A[2][i] = rx * mRightM[2][i] - mRightM[0][i]
- for i in range(0, 3):
- A[3][i] = ry * mRightM[2][i] - mRightM[1][i]
- B = np.zeros(shape=(4, 1))
- for i in range(0, 2):
- B[i][0] = mLeftM[i][3] - lx * mLeftM[2][3]
- for i in range(2, 4):
- B[i][0] = mRightM[i - 2][3] - rx * mRightM[2][3]
- XYZ = np.zeros(shape=(3, 1))
- # 根据大佬的方法,采用最小二乘法求其空间坐标
- cv2.solve(A, B, XYZ, cv2.DECOMP_SVD)
- print(XYZ)
- return XYZ
同名点是指左右相片对应的匹配点,在空间坐标中指的是同一个点,下面用opencv相机标定的方法输出并保存标定板上各个点的坐标。
- # 导入库,包括opencv和openpyxl(excel操作库,安装方法:pip install openpyxl)
- import cv2
- import openpyxl
- import os
-
- # 两张图片位置,这里选择left01和right01两张,位置自行更改
- img1 = cv2.imread('D:/SA/2021.3/6.1/l/left01.jpg')
- img2 = cv2.imread('D:/SA/2021.3/6.1/r/right01.jpg')
- criteria = (cv2.TERM_CRITERIA_MAX_ITER | cv2.TERM_CRITERIA_EPS, 30, 0.001)
- gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
- gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
- # 标定板角点检测
- ret1, corners1 = cv2.findChessboardCorners(gray1, (6, 9), None)
- ret2, corners2 = cv2.findChessboardCorners(gray2, (6, 9), None)
- # 亚像素角点检测,使其角点精度更高(反正我觉得没高多少)
- corners11 = cv2.cornerSubPix(gray1, corners1, (5, 5), (-1, -1), criteria)
- corners21 = cv2.cornerSubPix(gray2, corners2, (5, 5), (-1, -1), criteria)
- # 将数据保存到excel中,excel路径自行修改,程序自动创建,无需手动操作
- excel_path = 'D:/SA/AngularPointCoordinates.xlsx'
- if os.path.exists(excel_path) is False:
- workbook = openpyxl.Workbook()
- worksheet = workbook.active
- worksheet.title = 'data'
- workbook.save(excel_path)
- workbook = openpyxl.load_workbook(excel_path)
- sheet = workbook['data']
- for i in range(0, len(corners1)):
- sheet.cell(i+1, 1, corners11[i][0][0])
- sheet.cell(i+1, 2, corners11[i][0][1])
- sheet.cell(i+1, 3, corners21[i][0][0])
- sheet.cell(i+1, 4, corners21[i][0][1])
- workbook.save(excel_path)
运行程序,完成后,打开excel文件,其中有54组角点坐标,如下图所示:
其并没有进行排列,所以我们要自己判断点的位置。其中,最左上角是xy都最小的(opencv像素坐标在左上,以右为x轴、下为y轴的正方向),可以得知第六行是最左上角点坐标,前两列为左相片,后两列为右相片坐标。所以坐标为
244.4273987, 94.16474152; 127.9022751, 110.3449249
此时我们找最左上角向下10mm的那个点,位于第五行,x与其大致相同,y稍大,其坐标为:244.9057465, 126.2445526;128.8562469, 142.0508575
以上面两组数据测试,添加代码
- p1 = uvToXYZ(244.4273987, 94.16474152, 127.9022751, 110.3449249)
- p2 = uvToXYZ(244.9057465, 126.2445526, 128.8562469, 142.0508575)
- dp = p1-p2
- print(dp)
结果如下
前两个为p1,p2点空间坐标(我们选的两个点),第三个为坐标差值,单位mm,除了在z方向有1.2mm误差较大外,其他方向误差较小。
经多次测试,单方向最大误差小于2mm,造成这种误差可能是因为没有畸变校正。emm,下次再加上校正后在进行测试。
ps完整主代码如上,同名点确定属于另一个py文件中。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。