基于 python+OpenCV 超大图片的畸变矫正
适用情况
网上大部分的镜头畸变矫正的例子都是给的小图片的测试效果,照搬他们的代码测试的话会发现,在他们给的小图片上效果良好,但是应用到自己的 超大图片(4000×3000像素) 上后会发现边缘效果很不理想!效果如下:
原图(4000×3000像素)(实验室镜头较好,看不出明显的畸变)
矫正后的效果(4000×3000像素)(仔细对比观察中间4×4的方格可以发现更加垂直了,但边缘部分形变严重)
接下来将先从理论方面为大家分析后,再附上代码,如果对理论不感兴趣也可以直接看代码
简略的理论分析
镜头畸变分为径向畸变和切向畸变两部分
径向畸变
其中径向畸变的修正采用主点周围的泰勒级数展开式的前几项进行描述,下面的公式中采用了 k1,k2,k3 三项来进行描述 注意:使用的展开项越多,镜头畸变矫正越准确!
切向畸变
畸变模型可以用两个额外的参数p1和p2来描述
问题所在
使用cv2.calibrateCamera函数默认只返回 k1,k2,k3!!!
这将导致当 r 增大时,矫正时的误差会越来越大,对于100×100像素的小图片来说 r 最大也只有
50
2
50\sqrt2
502
? ,而对于4000×3000的超大图片而言,r 最大可以达到 2500,公式中当取到
r
3
r^3
r3 时,误差将呈几何式增长
解决办法
查看OpenCV官网中cv2.calibrateCamera函数说明文档,发现该函数有一个参数flags,可以通过该参数控制返回的畸变系数的数量等高级设置 可以通过设置 flags=cv2.CALIB_RATIONAL_MODEL来控制最后返回的畸变系数数量,除了返回 k1,k2,k3,p1,p2 外,多返回 k4,k5,k6 的值
通过这样一个小的修改即可解决超大图片畸变矫正边缘形变的问题
完整代码
import cv2
import numpy as np
import glob
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
w = 5
h = 5
dir = 'train_image/*.jpg'
re_im = 'test_image/Image.jpg'
sign_im = 'sign_image/' + 'sign.jpg'
sv_im = 'adjust_image/' + 'adjust.jpg'
objp = np.zeros((w*h, 3), np.float32)
objp[:, :2] = np.mgrid[0:w, 0:h].T.reshape(-1, 2)
objpoints = []
imgpoints = []
images = glob.glob(dir)
for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, corners = cv2.findChessboardCorners(gray, (w, h))
if ret is True:
corners = cv2.cornerSubPix(gray, corners, (3, 3), (-1, -1), criteria)
objpoints.append(objp)
imgpoints.append(corners)
cv2.drawChessboardCorners(img, (w, h), corners, ret)
cv2.imwrite(sign_im, img)
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None, flags=cv2.CALIB_RATIONAL_MODEL)
img2 = cv2.imread(re_im)
h, w = img2.shape[:2]
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 1, (w, h))
dst = cv2.undistort(img2, mtx, dist, None, newcameramtx)
cv2.imwrite(sv_im, dst)
total_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)
total_error += error
print("total error: ", total_error/len(objpoints))
在超大图片上的畸变矫正的效果
原图(4000×3000像素)
矫正后的效果(4000×3000像素)(仔细对比观察可以看到矫正后的图片左侧有一条黑线,图像也略微向右聚拢了一些)
|