相机标定理解
一。原理
參考:
camera calibration using opencv
相機標定原理、步驟
opencv-python 攝像頭標定
生成黑白棋盤標定圖和單目相機標定
相機外參估計
坐標系
| 世界坐標系 | world coordinate,測量坐標系,三維直角坐標系,以其為基準可以描述相機和待測物體的空間位置。世界坐標系的位置可以根據實際情況自由確定 | |
| 相機坐標系 | camera coordinate,三維直角坐標系,原點位于鏡頭光心處,x,y軸分別與相面的兩邊平行,z軸為鏡頭光軸,與像平面垂直 | |
| 像素坐標系 | pixel coordinate,二維直角坐標系,反映相機CCD/CMOS芯片中像素的排列情況。原點位于左上角,u,v分別于像面的兩邊平行,單位是像素(整數)。但是它不利于坐標轉換 | |
| 圖像坐標系 | XOY,單位通常是毫米,原點是相機光軸與相面的交點(也叫主點) |
4大坐標系理解圖:
坐標系之間轉換:
| 世界坐標系-》相機坐標系 | ||
| 圖像坐標系-》像素坐標系 | ||
| 相機坐標系-》圖像坐標系 | ||
| 世界坐標系-》像素坐標系 |
過程理解:
要找到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)
| 徑向畸變 | 來自于透鏡形狀,包括枕型畸變、桶型畸變,光學中心畸變為0,越往邊緣移動,畸變也嚴重 | |
| 切向畸變 | 透鏡制造上的缺陷使得透鏡本身與圖像平面不平行而產生的,包括薄透鏡畸變、離心畸變 | |
| 二。相機標定步驟 | ||
| 1、打印一張棋盤格,把它貼在一個平面上,作為標定物。 | ||
| 2、通過調整標定物或攝像機的方向,為標定物拍攝一些不同方向的照片。 | ||
| 3、從照片中提取棋盤格角點。 | ||
| 4、估算理想無畸變的情況下,五個內參和六個外參。 | ||
| 5、應用最小二乘法估算實際存在徑向畸變下的畸變系數。 | ||
| 6、極大似然法,優化估計,提升估計精度。 |
三。代碼注釋
內參,畸變參數的求解:
外參的求解:
import cv2 import numpy import numpy as nptwod_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 *4threed_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 pointspoint2D : An array of 2D pointsrVec : Rotation vectortVec : Translation vectorcameraMat: Camera Matrix used in solvePnPheight : Height in real-world 3D spaceReturn : 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 - tempMat2point3D.append(p)point3D = (np.array(point3D, dtype='float32')).reshape([-1, 1, 3])return point3Dframe = cv2.imread("source.jpg") # cap = cv2.VideoCapture("rtsp://admin:MLLaaa100848@192.168.1.64: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,sourceif event == cv2.EVENT_LBUTTONDOWN:drawing = Trueix, iy = x, yp3d = Pix2World([ix, iy], r, t, newcameramtx, 0)source = p3delif event == cv2.EVENT_LBUTTONUP:if drawing == True:if mode == True:px,py = x,ycv2.line(frame, (ix, iy), (x, y), (0, 255, 0), 10)p3d2 = Pix2World([px, py], r, t, newcameramtx, 0)coord = p3d2-sourcelength = 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()總結
- 上一篇: HALO:用于MR扫描器中实时头部对准的
- 下一篇: Ubuntu16.04 n次装机血泪史(