利用aruco进行动态检测时,需要先矫正摄像机带来的图形畸变。为了找到这些纠正参数,我们必须要提供一些包含明显图案模式的样本图片(比如说棋盘)。我们可以在上面找到一些特殊点(如棋盘的四个角点)。我们找到这些特殊点在图片中的位置以及它们的真实位置。有了这些信息,我们就可以使用数学方法求解畸变系数。
将棋盘图像固定到一个平面上,使用相机从不同角度,不同位置拍摄10-20张标定图。
''' python 调用opencv相机拍照代码'''
import cv2
camera=cv2.VideoCapture(0)
i = 0
while 1:
(grabbed, img) = camera.read()
cv2.imshow('img',img)
if cv2.waitKey(1) & 0xFF == ord('j'): # 按j保存一张图片
i += 1
u = str(i)
firename=str('./img'+u+'.jpg')
cv2.imwrite(firename, img)
print('写入:',firename)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
注:棋盘的w,和h,是在图中棋盘行和列的小格数减去一得到的结果
1.第一个参数是输入图像,
2.第二个参数是 cv2.findChessboardCorners() 检测到的角点,即是输入也是输出。(该函数无返回值,会直接在上一步的输出结果 corners 上进行修改)
3.第三个参数是计算亚像素角点时考虑的区域的大小,大小为NXN; N=(winSize*2+1)。
4.第四个参数作用类似于winSize,但是总是具有较小的范围,通常忽略即 (-1, -1)。
5.第五个参数用于表示计算亚像素时停止迭代的标准,可选的值有cv2.TermCriteria_MAX_ITER 、cv.TermCriteria_EPS(可以是两者其一,或两者均选),前者表示迭代次数达到了最大次数时停止,后者表示角点位置变化的最小值已经达到最小时停止迭代。具体形式见代码。
————————————————
原文链接:https://blog.csdn.net/qq_28584889/article/details/103356855
# 检测棋盘格角点
ret , coners = cv2.findChessboardCorners(gray,(w,h),None)
'''[output]:
True
[[[213.42604 143.87164]]
[[234.48198 142.49297]]
...
[[394.42282 302.6433 ]]
[[416.57324 301.3984 ]]
[[437.30234 299.3177 ]]]'''
# 寻找亚像素点
# critria 为寻找亚像素点的参数,采用的停止准则是最大循环次数30和最大误差容限0.001
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
# 将角点在图像上表示出来
#(w,h)
cv2.drawChessboardCorners(img, (11,8),corners,True)
cv2.namedWindow('findCorners',cv2.WINDOW_NORMAL)
cv2.resizeWindow('findCorners',640,480)
cv2.imshow('findCorners',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
再得到对象点和图像点之后,就可以进行摄像机标定。
# 创建世界坐标系中的棋盘格点,使z坐标为0
w = 11
h = 8
objp = np.zeros((w*h,3),np.float32)
objp[:,:2] = np.mgrid[0:w,0:h].T.reshape(-1,2)
# 世界坐标要乘上棋盘每个小格子的尺寸 例如本例采用20mm
objp = objp*20
# 储存棋盘格角点的世界坐标和图像坐标对
objpoints = [] # 在世界坐标系中的三维点
imgpoints = [] # 在图像平面的二维点
objpoints.append(objp)
imgpoints.append(corners)
#### 计算标定
ret, mtx ,dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1],None,None)
u, v = img.shape[:2]
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)
'''[output]:
ret: 0.30404563059120104
mtx:
[[1.14680270e+03 0.00000000e+00 3.04927127e+02]
[0.00000000e+00 1.14497089e+03 2.18227227e+02]
[0.00000000e+00 0.00000000e+00 1.00000000e+00]]
dist畸变值:
[[-5.90292637e+00 1.28873057e+02 2.09194579e-02 5.44015360e-02
-2.83669301e+03]]
rvecs旋转(向量)外参:
[array([[-0.06720353],
[ 0.0355854 ],
[ 0.00079517]])]
tvecs平移(向量)外参:
[array([[-563.20786475],
[-456.5025334 ],
[6513.25636228]])]
newcameramtx外参 [[1.14441345e+03 0.00000000e+00 3.04291847e+02]
[0.00000000e+00 1.14318188e+03 2.17886252e+02]
[0.00000000e+00 0.00000000e+00 1.00000000e+00]]
'''
得到相机的相关参数后,就可以对一幅图像进行矫正了。不过在那之前我们可以使用从函数 cv2.getOptimalNewCameraMatrix() 得到的自由缩放系数对摄像机矩阵进行优化。如果缩放系数 alpha = 0,返回的非畸变图像会带有最少量的不想要的像素。它甚至有可能在图像角点去除一些像素。如果 alpha = 1,所有的像素都会被返回,还有一些黑图像。它还会返回一个 ROI 图像,我们可以用来对结果进行裁剪。
# 返回一个优化后的内参数矩阵
# (u,v) 为初始棋盘图像的img.shape[:2]
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (u, v), 0, (u, v))
opencv中提供了两种畸变矫正的方法,以下将依次叙述:
# 纠正畸变
# frame 需要处理的图像
dst1 = cv2.undistort(frame, mtx, dist, None, newcameramtx)
x,y,w1,h1 = roi
dst1 = dst1[y:y+h1,x:x+w1]
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 = 6 # 7 - 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)
#追加进入世界三维点和平面二维点中
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()
声明:内容大部分来自 https://blog.dgut.top/2020/07/20/opencv-biaoding/,本文是对其中知识的一些扩展总结和重新整理。