说明,本文章参照来源:
一文讲透鱼眼相机畸变矫正,及目标检测项目应用 值得收藏_verilog鱼眼矫正-CSDN博客
一.标定准备
1.手动拍摄50-80张标定图片。要求标定图片中的棋盘格尽量不要靠近边缘区域,否则成像质量差,容易标定失败。
2.标定棋盘格选择(内角点:9*6,A4大小)
3.工程结构:
4.openCV与python版本:
opencv == 3.4.2.16
python==3.7
二.代码
相机参数与畸变系数根据个人需要提取
'''
func:鱼眼相机单目标定
by openCV方法
注:数据集要求拍摄数量一般在50~80之间,保证棋盘的宽高可以占整个画面1/4以上,并放入inputs文件夹中
提取特征点与计算参数一块执行
# 棋盘标定法;棋盘标定法的缺点,是靠近圆周(外围区域)的区域,会被拉伸的很严重,视觉效果变差。
# 所以一般会进行切除,导致矫正后的图片只保留了原图的中间区域。
'''
import glob
import numpy as np
import cv2
from tqdm import tqdm
class FishEyeSignal:
camName = 'camd'
# 设置超参数
CHECKERBOARD = (9, 6) # W=12 h = 9 内角点大小
# 设置寻找亚像素角点的参数,表示迭代停止条件 迭代30次或位置变换小于0.001停止
criteria = (cv2.TERM_CRITERIA_MAX_ITER + cv2.TERM_CRITERIA_EPS, 30, 0.001)
# subpix搜索半径
radius = (11, 11)
h,w = 0,0
objpoint = [] # 3d点
imgpoint = [] # 2d点
def get_feature(self):
# 获取特征点并可视化
objp = np.zeros((1, self.CHECKERBOARD[0] * self.CHECKERBOARD[1], 3), np.float32)
objp[0,:,:2] = np.mgrid[0:self.CHECKERBOARD[0], 0:self.CHECKERBOARD[1]].T.reshape(-1,2) # 将世界坐标系建在标定板上,所有点的Z坐标全部为0,所以只需要赋值x和y
path_all = glob.glob(r'D:\Project\Python\test01\OpenCVMulti\sources\{}\inputs/*.jpg'.format(self.camName))
for path in tqdm(path_all):
image = cv2.imread(path)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
self.h, self.w = gray.shape[:2]
ret, corners = cv2.findChessboardCorners(gray, self.CHECKERBOARD, None)
if ret:
self.objpoint.append(objp)
corners = cv2.cornerSubPix(image=gray, corners=corners, winSize=self.radius, zeroZone=(-1, -1),
criteria=self.criteria)
self.imgpoint.append(corners)
# 可视化棋盘点
path_out = path.replace('inputs', 'shows')
# 对应世界坐标系 点顺序先X轴后Y轴,右手法则定Z轴
cv2.drawChessboardCorners(image=image, patternSize=self.CHECKERBOARD, corners=corners, patternWasFound=True)
# 提取特征点后的图片会被保存,可手动将提取错误的剔除
cv2.imwrite(path_out, image)
print('获取特征点完成')
def cacu_parameter(self):
# 计算相机内参和畸变系数
flags = 0
flags |= cv2.fisheye.CALIB_RECOMPUTE_EXTRINSIC
flags |= cv2.fisheye.CALIB_CHECK_COND
flags |= cv2.fisheye.CALIB_FIX_SKEW
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 1e-6)
ret, K, D, rvecs, tvecs = cv2.fisheye.calibrate(self.objpoint, self.imgpoint, (self.w, self.h), K=None, D=None, flags=flags, criteria=criteria)
# print('K:',K)
# print('D:',D)
if ret > 5:
print('拟合误差大,棋盘点检测不准确')
map_combine, _ = cv2.fisheye.initUndistortRectifyMap(K, D, np.eye(3), K, (self.w, self.h), cv2.CV_16SC2)
mapy = map_combine[:, :, 1].astype(np.float32)
mapx = map_combine[:, :, 0].astype(np.float32)
xpath = r'sources/npy/mapx_{}.npy'.format(self.camName)
ypath = r'sources/npy/mapy_{}.npy'.format(self.camName)
np.save(xpath, mapx)
np.save(ypath, mapy)
print('计算参数成功')
return K,D,(self.w,self.h)
def calibration(self):
# 加载remap matrix 矫正输入图片
xpath = r'sources/npy/mapx_{}.npy'.format(self.camName)
ypath = r'sources/npy/mapy_{}.npy'.format(self.camName)
mapx = np.load(xpath)
mapy = np.load(ypath)
image = cv2.imread(r'D:\Project\Python\test01\OpenCVMulti\sources\rgb\inputs\1721369260.9120214rgb.jpg')
image_remap = cv2.remap(image, mapx, mapy, interpolation=cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT)
cv2.imshow('ori',image)
cv2.imshow('校准后',image_remap)
cv2.waitKey(0)
cv2.destroyAllWindows()
if __name__ == '__main__':
a = FishEyeSignal()
a.camName = 'rgb'
# # # 获取特征点
# a.get_feature()
# # # # 计算参数
# K,D,DMI = a.cacu_parameter()
# # # 输入图片进行矫正验证
a.calibration()
三.标定结果
原图
校准后
结论:
可以看出棋盘标定法标定鱼眼镜头在边缘处拉伸较为明显
四.标定过程中遇到的问题
1. ret, K, D, rvecs, tvecs = cv2.fisheye.calibrate(self.objpoint, self.imgpoint, (self.w, self.h), K=None, D=None, flags=flags, criteria=criteria)
cv2.error: OpenCV(4.10.0) D:\a\opencv-python\opencv-python\opencv\modules\calib3d\src\fisheye.cpp:1502: error: (-3:Internal error) CALIB_CHECK_COND - Ill-conditioned matrix for input array 0 in function 'cv::internal::CalibrateExtrinsics'
报错:如上,
解决方法:更换openCV版本为3.4.2 2
2.error: (-215:Assertion failed) fabs(norm_u1) > 0 in function 'cv::internal::
解决方法:
重新采集数据集,保证数据集图像质量清晰,棋盘格不靠经边缘点,棋盘格占据图像视野不要太小。