一。原理
参考:
camera calibration using opencv
相机标定原理、步骤
opencv-python 摄像头标定
生成黑白棋盘标定图和单目相机标定
相机外参估计
坐标系
坐标系之间转换:
转换 | ||
---|---|---|
世界坐标系-》相机坐标系 | ||
图像坐标系-》像素坐标系 | ||
相机坐标系-》图像坐标系 | ||
世界坐标系-》像素坐标系 |
过程理解:
要找到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)
三。代码注释
内参,畸变参数的求解:
#!/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()