Today's low-cost single-hole camera(照相機)It will bring a lot of distortion to the image.There are two main types of distortion
種:Radial distortion and tangential distortion.如下圖所示,Mark the two sides of the chessboard with red lines,
But you will find that the border of the chessboard does not coincide with the red line.Everything we think should be straight is also convex
出來了.
在 3D 相關應用中,These distortions must be corrected first.in order to find these correction parameters,我們必
Some sample images containing obvious pattern patterns are required(For example a chessboard).We can find it there
to some special point(such as the four corners of a chessboard).We play where these special points are in the picture as well
Their real location.有了這些信息,We can then solve for the distortion coefficient mathematically.這
That's the summary of the whole story.為了得到更好的結果,我們至少需要 10 個這樣的圖
case mode.
import cv2
import numpy as np
# 定義棋盤格的尺寸
size = 140
# 定義標定板尺寸
boardx = size * 10
boardy = size * 10
canvas = np.zeros((boardy, boardx, 1), np.uint8) # 創建畫布
for i in range(0, boardx):
for j in range(0, boardy):
if (int(i/size) + int(j/size)) % 2 != 0: # 判定是否為奇數格
canvas[j, i] = 255
cv2.imwrite("./chessboard.png", canvas)
生成結果如下:
Use a chessboard diagramA4紙打印,並將將A4The paper is attached to a very flat board and fixed
例子如下:
Rich big man,You can buy the calibration board directly.
注意,If it is a printed checkerboard, it must be flat,Otherwise, the calibration will be inaccurate
Use the camera from different angles,Shot in different locations(15-20)Zhang calibration diagram.類似這樣的:
python調用opencvCamera photo code(例):
import cv2
camera = cv2.VideoCapture(0)
i = 0
ret, img = camera.read()
print('輸入j,Download the current picture')
print('輸入q,終止程序')
while ret:
cv2.imshow('img', img)
ret, img = camera.read()
if cv2.waitKey(1) & 0xFF == ord('j'): # 按j保存一張圖片
i += 1
firename = str('./img' + str(i) + '.jpg')
cv2.imwrite(firename, img)
print('寫入:', firename)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.release()
cv2.destroyAllWindows()
按j拍攝圖片,will be saved in batches in order,按q退出程序.
注:This part is for explanation only and no practical operation,Formal operations can skip the fourth step directly
To find the pattern of the checkerboard,我們要使用函數 cv2.findChessboardCorners().
We also need to pass in the type of pattern,比如說 8x8 Grid or 5x5 grid, etc.在本例中
我們使用的9×6 的格子.(Usually the chessboard is 8x8 或者 7x7).它會返
Back to the corner,Return value type if image is obtained(Retval)就會是 True.These corners will
按順序排列(從左到右,從上到下)
This function may not find the pattern it should in all images.So a good way is to edit
寫代碼,Start the camera and check every frame for what it should be.After we get the pattern we want to find the corners and save them as a list.Set a certain interval before reading the next frame of image,This gives us enough time to adjust the direction of the board.Continue this process until we get enough good patterns.Even if we take this example,在所有的14 I don't know how many of the images are good.So we have to read each image and find the good ones from it.
除 了 使 用 棋 盤 之 外, 我 們 還 可 以 使 用 環 形 格 子, 但 是 要 使 用 函 數
cv2.findCirclesGrid() to find patterns.It is said that very few images are required to use the annular lattice 就可以了.
After finding these corners we can use the function cv2.cornerSubPix() Increase accuracy
度.我們使用函數 cv2.drawChessboardCorners() 繪制圖案.所有的這
These steps are included in the code below:
After getting these object points and image points,We are ready to do camera calibration.
The function we are going to use is cv2.calibrateCamera().It returns the camera matrix,畸
變系數,Rotate and transform vectors, etc.
Now we found what we were looking for,We can find an image to correct him
了.OpenCV 提供了兩種方法,Let's all learn.But until then we can use it
從函數 cv2.getOptimalNewCameraMatrix() The resulting free zoom factor is photographed
The camera matrix is optimized.如果縮放系數 alpha = 0,The undistorted image is returned with a minimal amount
of unwanted pixels.It's even possible to remove some pixels at the corners of the image.如果 alpha = 1,所
Some pixels will be returned,There are also some black images.It will also return one ROI 圖像,我們可以
Used to crop the result.
函數:cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),1,(w,h))中參數1是個坑,
這裡我們使用cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),0,(w,h))參數設置為0
4 、Distorted to undistorted
下面代碼中
dst1Image used is cv2.undistort() 這是最簡單的方法.Just use this function and what you got above ROI Crop the result
dst2Image used isremapping 這應該屬於“曲線救國”了.First we need to find the mapping equation from the distorted image to the undistorted image.Use the remapping equation again.(There are detailed usages in the code)
The two effects can be compared by themselves
Correct before and after comparison:
We can use the backprojection error to estimate the accuracy of the parameters we found.得到的
結果越接近 0 越好.With internal parameters,Distortion parameters and rotation transformation matrix,我們就可以使
用 cv2.projectPoints() Convert object points to image points.Then the transformation can be calculated
The absolute difference between the image and the corner detection algorithm is bad.Then we compute the mean of the errors over all the calibration images.
(But this article doesn't need it,So it is not written)
目的:Get the camera's internal parameters after correcting the distortion
pip install opencv-python -i https://pypi.tuna.tsinghua.edu.cn/simple/
pip install glob2 -i https://pypi.tuna.tsinghua.edu.cn/simple/
import cv2
import numpy as np
import glob
# 找棋盤格角點
# 設置尋找亞像素角點的參數,采用的停止准則是最大循環次數30和最大誤差容限0.001
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) # 阈值
#棋盤格模板規格
w = 9 # 10 - 1
h = 9 # 10 - 1
# 世界坐標系中的棋盤格點,例如(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)
objp = objp*18.1 # 18.1 mm
# 儲存棋盤格角點的世界坐標和圖像坐標對
objpoints = [] # 在世界坐標系中的三維點
imgpoints = [] # 在圖像平面的二維點
#加載pic文件夾下所有的jpg圖像
images = glob.glob('./*.jpg') # 拍攝的十幾張棋盤圖片所在目錄
i=0
for fname in images:
img = cv2.imread(fname)
# 獲取畫面中心點
#獲取圖像的長寬
h1, w1 = img.shape[0], img.shape[1]
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
u, v = img.shape[:2]
# 找到棋盤格角點
ret, corners = cv2.findChessboardCorners(gray, (w,h),None)
# 如果找到足夠點對,將其存儲起來
if ret == True:
print("i:", i)
i = i+1
# 在原角點的基礎上尋找亞像素角點
cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
#Append to world 3D points and plane 2D points
objpoints.append(objp)
imgpoints.append(corners)
# 將角點在圖像上顯示
cv2.drawChessboardCorners(img, (w,h), corners, ret)
cv2.namedWindow('findCorners', cv2.WINDOW_NORMAL)
cv2.resizeWindow('findCorners', 640, 480)
cv2.imshow('findCorners',img)
cv2.waitKey(200)
cv2.destroyAllWindows()
#%% 標定
print('正在計算')
#標定
ret, mtx, dist, rvecs, tvecs = \
cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
print("ret:",ret )
print("mtx:\n",mtx) # 內參數矩陣
print("dist畸變值:\n",dist ) # 畸變系數 distortion cofficients = (k_1,k_2,p_1,p_2,k_3)
print("rvecs旋轉(向量)外參:\n",rvecs) # 旋轉向量 # 外參數
print("tvecs平移(向量)外參:\n",tvecs ) # 平移向量 # 外參數
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (u, v), 0, (u, v))
print('newcameramtx外參',newcameramtx)
#打開攝像機
camera=cv2.VideoCapture(0)
while True:
(grabbed,frame)=camera.read()
h1, w1 = frame.shape[:2]
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (u, v), 0, (u, v))
# 糾正畸變
dst1 = cv2.undistort(frame, mtx, dist, None, newcameramtx)
#dst2 = cv2.undistort(frame, mtx, dist, None, newcameramtx)
mapx,mapy=cv2.initUndistortRectifyMap(mtx,dist,None,newcameramtx,(w1,h1),5)
dst2=cv2.remap(frame,mapx,mapy,cv2.INTER_LINEAR)
# 裁剪圖像,輸出糾正畸變以後的圖片
x, y, w1, h1 = roi
dst1 = dst1[y:y + h1, x:x + w1]
#cv2.imshow('frame',dst2)
#cv2.imshow('dst1',dst1)
cv2.imshow('dst2', dst2)
if cv2.waitKey(1) & 0xFF == ord('q'): # 按q保存一張圖片
cv2.imwrite("../u4/frame.jpg", dst1)
break
camera.release()
cv2.destroyAllWindows()
Put the code in the same folder as the picture and run it directly
運行結果如下:
The previous program can be run to get the distortion internal parameters,將mtx值保存在k,
將dist保存在d
注:復制的時候,You need to manually add a comma inside the array
import cv2 as cv
import numpy as np
def undistort(frame):
k=np.array( [[408.96873567 , 0. ,329.01126845],
[ 0. , 409.20308599 ,244.73617469],
[ 0. , 0. , 1. ]])
d=np.array([-0.33880708 , 0.16416173 ,-0.00039069 ,-0.00056267 ,-0.056967 ])
h,w=frame.shape[:2]
mapx,mapy=cv.initUndistortRectifyMap(k,d,None,k,(w,h),5)
return cv.remap(frame,mapx,mapy,cv.INTER_LINEAR)
cap=cv.VideoCapture(0)# Replace with the camera number to be turned on
ret,frame=cap.read()
while ret:
cv.imshow('later',frame)
cv.imshow('img',undistort(frame))
ret,frame=cap.read()
if cv.waitKey(1)&0xff==27:
break
cap.release()
cv.destroyAllWindows()
效果對比
Before and after correction:
可以看到,The distortion is almost corrected.
Distortion This program can be run once,之後的話,You can add the above one every time the camera acquires an image