相机标定理解

一。原理
参考:
camera calibration using opencv
相机标定原理、步骤
opencv-python 摄像头标定

生成黑白棋盘标定图和单目相机标定
相机外参估计

坐标系

坐标系名称 介绍 图形
世界坐标系 world coordinate,测量坐标系,三维直角坐标系,以其为基准可以描述相机和待测物体的空间位置。世界坐标系的位置可以根据实际情况自由确定
相机坐标系 camera coordinate,三维直角坐标系,原点位于镜头光心处,x,y轴分别与相面的两边平行,z轴为镜头光轴,与像平面垂直
像素坐标系 pixel coordinate,二维直角坐标系,反映相机CCD/CMOS芯片中像素的排列情况。原点位于左上角,u,v分别于像面的两边平行,单位是像素(整数)。但是它不利于坐标转换 相机标定理解_第1张图片
图像坐标系 XOY,单位通常是毫米,原点是相机光轴与相面的交点(也叫主点)

4大坐标系理解图:
相机标定理解_第2张图片

坐标系之间转换:

转换
世界坐标系-》相机坐标系 相机标定理解_第3张图片
图像坐标系-》像素坐标系 相机标定理解_第4张图片
相机坐标系-》图像坐标系 相机标定理解_第5张图片
世界坐标系-》像素坐标系 相机标定理解_第6张图片 相机标定理解_第7张图片

过程理解:
要找到3D点在图像平面上的投影,我们首先需要使用外参(旋转和平移)将该点从世界坐标系统转换成相机坐标系,接下来,使用相机的内参将点投影到图像平面上。

相机标定3+要素(内参,外参,畸变参数等):
0.先看下总公式:
在这里插入图片描述
P:3x4投影矩阵
K:内参 3x3矩阵
R:旋转矩阵 3x3
t:平移矩阵 3x1

1.内参K
在这里插入图片描述
fx,fy:x,y焦距,通常值一样
cx,cy:图像平面光学中心的x,y坐标,一般近似图像中心点
gamma:是轴之间的倾斜度,一般是0

2.外参
Rt
R:旋转矩阵 3x3 但opencv标定时3x1矩阵??!!
t: 平移矩阵 3x1

3.畸变参数
在这里插入图片描述
分径向畸变(k1,k2,k3)和切向畸变(p1,p2)
相机标定理解_第8张图片

畸变类型 内容 示例
径向畸变 来自于透镜形状,包括枕型畸变、桶型畸变,光学中心畸变为0,越往边缘移动,畸变也严重 相机标定理解_第9张图片
切向畸变 透镜制造上的缺陷使得透镜本身与图像平面不平行而产生的,包括薄透镜畸变、离心畸变 相机标定理解_第10张图片
二。相机标定步骤
1、打印一张棋盘格,把它贴在一个平面上,作为标定物。
2、通过调整标定物或摄像机的方向,为标定物拍摄一些不同方向的照片。
3、从照片中提取棋盘格角点。
4、估算理想无畸变的情况下,五个内参和六个外参。
5、应用最小二乘法估算实际存在径向畸变下的畸变系数。
6、极大似然法,优化估计,提升估计精度。

三。代码注释
内参,畸变参数的求解:

#!/usr/bin/env python

import cv2
import numpy as np
import os
import glob

# Defining the dimensions of checkerboard
CHECKERBOARD = (6,9)
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)

# Creating vector to store vectors of 3D points for each checkerboard image
objpoints = []
# Creating vector to store vectors of 2D points for each checkerboard image
imgpoints = [] 


# Defining the world coordinates for 3D points
objp = np.zeros((1, CHECKERBOARD[0]*CHECKERBOARD[1], 3), np.float32)
objp[0,:,:2] = np.mgrid[0:CHECKERBOARD[0], 0:CHECKERBOARD[1]].T.reshape(-1, 2)
prev_img_shape = None

# Extracting path of individual image stored in a given directory
images = glob.glob('./images/*.jpg')
for fname in images:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    # Find the chess board corners
    # If desired number of corners are found in the image then ret = true
    ret, corners = cv2.findChessboardCorners(gray, CHECKERBOARD, cv2.CALIB_CB_ADAPTIVE_THRESH+
    	cv2.CALIB_CB_FAST_CHECK+cv2.CALIB_CB_NORMALIZE_IMAGE)
    
    """
    If desired number of corner are detected,
    we refine the pixel coordinates and display 
    them on the images of checker board
    """
    if ret == True:
        objpoints.append(objp)
        # refining pixel coordinates for given 2d points.
        corners2 = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
        
        imgpoints.append(corners2)

        # Draw and display the corners
        img = cv2.drawChessboardCorners(img, CHECKERBOARD, corners2,ret)
    
    cv2.imshow('img',img)
    cv2.waitKey(10)

cv2.destroyAllWindows()

h,w = img.shape[:2]

"""
Performing camera calibration by 
passing the value of known 3D points (objpoints)
and corresponding pixel coordinates of the 
detected corners (imgpoints)
"""
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1],None,None)

print("Camera matrix {}: \n".format(mtx.shape))
print(mtx)
print("dist {}: \n".format(dist.shape))
print(dist)
print("rvecs {}: \n".format(np.array(rvecs).shape))
print(rvecs)
print("tvecs {}: \n".format(np.array(tvecs).shape))
print(tvecs)

# Using the derived camera parameters to undistort the image

img = cv2.imread(images[0])
# Refining the camera matrix using parameters obtained by calibration
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 1, (w, h))

# Method 1 to undistort the image
dst = cv2.undistort(img, mtx, dist, None, newcameramtx)

# Method 2 to undistort the image
mapx,mapy=cv2.initUndistortRectifyMap(mtx, dist, None, newcameramtx, (w, h), 5)

dst = cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR)
print("dst shape is ", dst.shape)

x, y, w, h = roi
cropped_frame = dst[y:y+h, x:x+w]
print("cropped_frame shape is ", cropped_frame.shape)

# Displaying the undistorted image
cv2.imshow("distorted image", img)
cv2.imshow("undistorted image", dst)
cv2.imshow("cropped image", cropped_frame)
cv2.waitKey(0)

#计算误差
mean_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)
    mean_error += error

print("mean error: ", mean_error/len(objpoints))

外参的求解:

import cv2
import numpy
import numpy as np

twod_point = "1189 254 1216 254 1242 255 1268 255 1294 256 1322 254 1347 255 1375 253 1187 279 1214 " \
           "278 1243 281 1269 279 1296 280 1321 279 1346 281 1374 280"
twod_point = np.array(twod_point.split( ),dtype=np.float)
print(len(twod_point)//2)
twod_point =  twod_point.reshape((16,2))
print(twod_point.shape)

threed_point = np.zeros(shape=(16,3))
for i in range(len(twod_point)):
    x = twod_point[i][0]
    y = twod_point[i][1]
    threed_point[i][0] = (x -1189)/27 *4
    threed_point[i][1] = (y - 254)/25 *4
newcameramtx=[[1.67276660e+03,0.00000000e+00,6.22925147e+02],[0.00000000e+00,1.67104004e+03,3.44706162e+02],[0.00000000e+00,0.00000000e+00,1.00000000e+00]]
dist=[[-6.11401280e-01 ,4.08011840e-01 ,-5.83043580e-05 ,-1.16998902e-03,-7.99593012e-01]]
newcameramtx = np.array(newcameramtx,dtype=np.uint8)
dist = np.array(dist,dtype=np.uint8)
_,r,t = cv2.solvePnP(threed_point,twod_point,newcameramtx,dist)
np.set_printoptions(suppress=True)

def Pix2World(point2D, rVec, tVec, cameraMat, height):
    """
       Function used to convert given 2D points back to real-world 3D points
       point2D  : An array of 2D points
       rVec     : Rotation vector
       tVec     : Translation vector
       cameraMat: Camera Matrix used in solvePnP
       height   : Height in real-world 3D space
       Return   : output_array: Output array of 3D points
    """
    point3D = []
    point2D = (np.array(point2D, dtype='float32')).reshape(-1, 2)
    numPts = point2D.shape[0]
    point2D_op = np.hstack((point2D, np.ones((numPts, 1))))

    rMat = cv2.Rodrigues(rVec)[0]
    # print(rMat)

    rMat_inv = np.linalg.inv(rMat)
    kMat_inv = np.linalg.inv(cameraMat)

    for point in range(numPts):
        uvPoint = point2D_op[point, :].reshape(3, 1)
        tempMat = np.matmul(rMat_inv, kMat_inv)
        tempMat1 = np.matmul(tempMat, uvPoint)
        tempMat2 = np.matmul(rMat_inv, tVec)
        s = (height + tempMat2[2]) / tempMat1[2]
        p = tempMat1 * s - tempMat2
        point3D.append(p)

    point3D = (np.array(point3D, dtype='float32')).reshape([-1, 1, 3])
    return point3D

frame = cv2.imread("source.jpg")
# cap = cv2.VideoCapture("rtsp://admin:[email protected]:554/h264/ch1/main/av_stream")
# ret, frame = cap.read()
drawing = False #鼠标按下为真
mode = True #如果为真,画矩形,按m切换为曲线
px,py,ix,iy=-1,-1,-1,-1
def draw_circle(event,x,y,flags,param):
    global ix, iy, drawing, mode,px,py,source
    if event == cv2.EVENT_LBUTTONDOWN:
        drawing = True
        ix, iy = x, y
        p3d = Pix2World([ix, iy], r, t, newcameramtx, 0)
        source = p3d
    elif event == cv2.EVENT_LBUTTONUP:
        if drawing == True:
            if mode == True:
                px,py = x,y
                cv2.line(frame, (ix, iy), (x, y), (0, 255, 0), 10)
                p3d2 = Pix2World([px, py], r, t, newcameramtx, 0)
                coord = p3d2-source
                length = round(np.sqrt(np.sum((p3d2-source) ** 2))*10,3)
                cv2.putText(frame,"L:"+str(length)+'mm',(int((px+ix)/2)-10,int((py+iy)/2)-10),cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
                print("-"*20,'\n',"实际长度",length,'\n','-'*20,)
                cv2.imshow("frame",frame)
                cv2.waitKey(0)


cv2.namedWindow('frame')
cv2.setMouseCallback("frame",draw_circle)
cv2.imshow("frame", frame)
k = cv2.waitKey(0)


# while ret:
#     ret, frame = cap.read()
#     cv2.imshow("frame",frame)
#     k = cv2.waitKey(0)
#     # if cv2.waitKey(1) & 0xFF == ord('q'):
#     #     break
#     if k == ord('s'):
#         break
cv2.destroyAllWindows()
# cap.release()

你可能感兴趣的:(计算机视觉,opencv,人工智能)