双目相机三维定位踩坑实录

调用双目工业相机

import numpy as np
import time
import os
os.environ["OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS"] = "0" #参见https://github.com/opencv/opencv/issues/17687,另外环境变量设置要在import cv2之前
import cv2
cap = cv2.VideoCapture(1,cv2.CAP_MSMF)#打开内置摄像机,CAP_DSHOW很慢,CAP_MSMF在未设置OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS时打开很慢
cap.set(cv2.CAP_PROP_FRAME_HEIGHT,1080)
cap.set(cv2.CAP_PROP_FRAME_WIDTH,3840)
flag = 1#播放视频
count = 0;#记录照相的次数
while cap.isOpened():#当摄像头打开时
    ret,frame=cap.read()#读取当前摄像头画面
    L=frame[:,0:1920]
    R=frame[:,1921:3840]
    cv2.imshow('l',L)#显示当前摄像头画面
    cv2.imshow('r',R)
    if cv2.waitKey(flag)==ord(' '):#按下空格键拍照
        c = str(count)#将countint转换成str型
        cv2.imwrite('L'+c+'.jpg',L)#保存图片
        cv2.imwrite('R'+c+'.jpg',R)#保存图片
        count=count+1#计数加一
    if cv2.waitKey(flag)==ord('q'):#退出循环
        break;
cv2.destroyAllWindows()#关闭所有窗口
cap.release()#释放摄像头

相机标定

import cv2
import numpy as np
import glob

# 找棋盘格角点
# 阈值
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
#棋盘格模板规格
w = 6   #内角点个数,内角点是和其他格子连着的点
h = 4

# 世界坐标系中的棋盘格点,例如(0,0,0), (1,0,0), (2,0,0) ....,(8,5,0),去掉Z坐标,记为二维矩阵
objp = np.zeros((w*h,3), np.float32)
objp[:,:2] = np.mgrid[0:w,0:h].T.reshape(-1,2)
# 储存棋盘格角点的世界坐标和图像坐标对
objpoints = [] # 在世界坐标系中的三维点
imgpoints = [] # 在图像平面的二维点

images = glob.glob('img/*.jpg')
for fname in images:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    ret, corners = cv2.findChessboardCorners(gray, (w,h),None)
    if ret == True:
        cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
        objpoints.append(objp)
        imgpoints.append(corners)
        cv2.drawChessboardCorners(img, (w,h), corners, ret)
        cv2.imshow('findCorners',img)
        cv2.waitKey(100)
cv2.destroyAllWindows()
#标定、去畸变
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
# mtx:内参数矩阵
# dist:畸变系数
# rvecs:旋转向量 (外参数)
# tvecs :平移向量 (外参数)
print (("ret:"),ret)
print (("mtx:\n"),mtx)
print (("dist:\n"),dist)
print (("rvecs:\n"),rvecs)
print (("tvecs:\n"),tvecs)
# 去畸变
img2 = cv2.imread('img/0.jpg')
h,w = img2.shape[:2]
newcameramtx, roi=cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),0,(w,h))
dst = cv2.undistort(img2, mtx, dist, None, newcameramtx)
x,y,w,h = roi
dst = dst[y:y+h, x:x+w]
cv2.imwrite('calibresult.jpg',dst)

# 反投影误差
total_error = 0
for i in range(len(objpoints)):
    imgpoints2, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
    error = cv2.norm(imgpoints[i],imgpoints2, cv2.NORM_L2)/len(imgpoints2)
    total_error += error
print (("total error: "), total_error/len(objpoints))

注意:根据张正友的论文原文翻译,标定板所在平面和相机的相对位置需要改变,否则视为同一组约束

calibrateCamera浅析

m t x = [ f x 0 c x 0 f y c y 0 0 1 ] (2) mtx=\left[ \begin{matrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{matrix} \right] \tag{2} mtx= fx000fy0cxcy1 (2)
f x , f y f_x,f_y fx,fy 是焦距,单位为像素,理论上是相等的, c x , c y c_x,c_y cx,cy 是光轴的像素坐标
r 1 ⃗ = [ z x 1 z y 1 z ] = [ z ( f x x z + c x ) z ( f y y z + c y ) z ] = m t x × r ⃗ \vec{r_1}=\left[ \begin{matrix} zx_1 \\ zy_1 \\ z \end{matrix} \right]= \left[ \begin{matrix} z(\frac{f_xx}{z}+c_x) \\ z(\frac{f_yy}{z}+c_y) \\ z \end{matrix} \right] =mtx\times \vec{r} r1 = zx1zy1z = z(zfxx+cx)z(zfyy+cy)z =mtx×r

r ⃗ \vec{r} r 是摄像机坐标系下的坐标 ( x , y , z ) T (x,y,z)^T (x,y,z)T或齐次坐标 ( k x , k y , k z ) T (kx,ky,kz)^T (kx,ky,kz)T r 1 ⃗ \vec{r_1} r1 是图像中(并非图像坐标系,因为加上了中心点)的二维齐次坐标 ( z x , z y , z ) T (zx,zy,z)^T (zx,zy,z)T

两相机光轴 z z z,相机坐标系 x , y x,y x,y轴都平行的情况

计算z

理论上两个相机的焦距应当相同,但是实践中无法做到,设其为 f 1 , f 2 f_1,f_2 f1,f2. 像点在两个图像坐标系中的 x x x坐标为 x 1 , x 2 x_1,x_2 x1,x2
在这里插入图片描述

x 1 f 1 = x z \frac{x_1}{f_1}=\frac{x}{z} f1x1=zx
− x 2 f 2 = B − x z -\frac{x_2}{f_2}=\frac{B-x}{z} f2x2=zBx
联立可得
z = B x 1 f 1 − x 2 f 2 z=\frac{B}{\frac{x_1}{f_1}-\frac{x_2}{f_2}} z=f1x1f2x2B

import numpy as np
import time
import os
os.environ["OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS"] = "0"
import cv2
cap = cv2.VideoCapture(1,cv2.CAP_MSMF)#打开内置摄像机
cap.set(cv2.CAP_PROP_FRAME_HEIGHT,1080)
cap.set(cv2.CAP_PROP_FRAME_WIDTH,3840)

class camera:
    def __init__(self,filename):
        self.imtx=np.loadtxt(filename)
        self.fx=self.imtx[0,0]
        self.fy=self.imtx[1,1]
        self.cx=self.imtx[0,2]
        self.cy=self.imtx[1,2]
        self.f=(self.fx+self.fy)/2
def corner(img):
    w=6
    h=4
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
    ret, corners = cv2.findChessboardCorners(gray, (w,h),None)
    if ret == True:
        cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
    return corners
L=camera("L.txt")
R=camera("R.txt")
print(L.f,R.f)
while cap.isOpened():#当摄像头打开时
    try:
        ret,frame=cap.read()#读取当前摄像头画面
        l=frame[:,0:1920]
        r=frame[:,1921:3840]
        #cv2.imshow('r',r)#显示当前摄像头画面q
        #cv2.waitKey(1)
        l_corners=corner(l)
        cv2.drawChessboardCorners(l, (6,4), l_corners, True)
        cv2.imshow('l',l)
        r_corners=corner(r)
        cv2.drawChessboardCorners(r, (6,4), r_corners, True)
        cv2.imshow('r',r)
        cv2.waitKey(1)
        a=(l_corners[0,0,0]-L.cx,l_corners[0,0,1]-L.cy)
        b=(r_corners[0,0,0]-R.cx,l_corners[0,0,1]-R.cy)
        print('red',a,b)
        print(120/(a[0]/L.f-b[0]/R.f))

        a=(l_corners[23,0,0]-L.cx,l_corners[23,0,1]-L.cy)
        b=(r_corners[23,0,0]-R.cx,l_corners[23,0,1]-R.cy)
        print('green',a,b)
        print(120/(a[0]/L.f-b[0]/R.f))
    except:
        pass

测量了两个点的 z z z,精度还可以

计算xy

import numpy as np
import time
import os
os.environ["OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS"] = "0"
import cv2
import math
cap = cv2.VideoCapture(1,cv2.CAP_MSMF)#打开内置摄像机
cap.set(cv2.CAP_PROP_FRAME_HEIGHT,1080)
cap.set(cv2.CAP_PROP_FRAME_WIDTH,3840)

class camera:
    def __init__(self,filename):
        self.imtx=np.loadtxt(filename)
        self.fx=self.imtx[0,0]
        self.fy=self.imtx[1,1]
        self.cx=self.imtx[0,2]
        self.cy=self.imtx[1,2]
        self.f=(self.fx+self.fy)/2
def corner(img):
    w=6
    h=4
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
    ret, corners = cv2.findChessboardCorners(gray, (w,h),None)
    if ret == True:
        cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
    return corners
L=camera("L.txt")
R=camera("R.txt")
print(L.f,R.f)
while cap.isOpened():#当摄像头打开时
    try:
        ret,frame=cap.read()#读取当前摄像头画面
        l=frame[:,0:1920]
        r=frame[:,1921:3840]
        #cv2.imshow('r',r)#显示当前摄像头画面q
        #cv2.waitKey(1)
        l_corners=corner(l)
        cv2.drawChessboardCorners(l, (6,4), l_corners, True)
        #cv2.imshow('l',l)
        r_corners=corner(r)
        cv2.drawChessboardCorners(r, (6,4), r_corners, True)
        #cv2.imshow('r',r)
        #cv2.waitKey(1)
        pos=[]
        for i in range(24):
            a=(l_corners[i,0,0]-L.cx,l_corners[i,0,1]-L.cy)
            b=(r_corners[i,0,0]-R.cx,r_corners[i,0,1]-R.cy)
            z=120/(a[0]/L.f-b[0]/R.f)
            x=z*a[0]/L.f
            y=z*a[1]/L.f
            if i!=0:
                print(math.sqrt((x-pos[-1][0])**2+(y-pos[-1][1])**2+(z-pos[-1][2])**2))
            pos.append((x,y,z))
            x=round(x,1)
            y=round(y,1)
            z=round(z,1)
            print(i,a,b,x,y,z)
            cv2.putText(l,str(x)+' '+str(y)+' '+str(z),(int(l_corners[i,0,0]),int(l_corners[i,0,1])),cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1)
        cv2.imshow('l',l)
        if cv2.waitKey(1)==ord('q'):
            break
    except:
        pass    

一般情况

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值