Искажение камеры, внутренние и внешние параметры, неискаженное изображение, калибровка камеры в OpenCV
Этот блог расскажет об искажении камеры, внутренних и внешних параметрах камеры и т. д., а также научится находить эти параметры, устранять искажения изображения и выполнять калибровку камеры, стереоизображение и т. д.
1. Рендеринг
Рендеринг исходного изображения в режиме шахматной доски выглядит следующим образом:
На исходном изображении можно увидеть искажение (радиальное искажение, когда прямые линии кажутся изогнутыми; и тангенциальное искажение, поскольку объектив захвата изображения не выровнен точно параллельно плоскости изображения. Поэтому некоторые области изображения могут выглядеть больше, чем ожидалось, при приближении .) Исходное изображение VS изображение эффекта шахматной доски 2 выглядит следующим образом: Исходное изображение VS изображение после калибровки выглядит следующим образом:
На картинке справа после калибровки видно, что линии рамки шахматной доски стали очень прямыми;
2. Принцип
2.1 Калибровка камеры
Вы узнаете об искажении камеры, внутренних и внешних параметрах камеры и многом другом. И научиться находить эти параметры, не искажая изображение и т.д.
искажение, внутренние параметры, внешние параметры радиальное искажение радиальное искажение, также известное как радиальное искажение или радиальное искажение; Тангенциальное искажение Тангенциальное искажение, также известное как тангенциальное искажение или тангенциальное искажение;
Дешевые камеры-обскуры вносят много искажений в изображение, два основных типа искажения — это радиальное искажение и тангенциальное искажение. Прямые линии кажутся искривленными из-за радиального искажения, и это оказывает большее влияние при удалении от центра изображения. Другим типом искажения является тангенциальное искажение, которое возникает, когда объектив захвата изображения не выровнен идеально параллельно плоскости изображения. Поэтому некоторые области изображения могут оказаться ближе, чем ожидалось.
- Необходимо найти пять параметров, называемых коэффициентами искажения, а также внутренние и внешние параметры камеры.
- Внутренние параметры зависят от камеры и включают в себя такую информацию, как фокусное расстояние (f_x, f_y), оптический центр (c_x, c_y), также известный как матрица камеры.
- Внешние параметры соответствуют векторам вращения и перемещения, которые преобразуют координаты трехмерной точки в систему координат.
Для стерео приложений эти искажения должны быть исправлены в первую очередь.Некоторые определенные точки (квадратные углы на шахматной доске) находятся на шахматной доске. Зная его координаты в пространстве реального мира, мы также знаем его координаты на изображении. Используя эти данные, в фоновом режиме рассчитываются коэффициенты искажения.
Рассмотрим только одно изображение шахматной доски. Важными входными данными, необходимыми для калибровки камеры, является набор точек трехмерного реального мира и соответствующих им точек двумерного изображения.
Точки 2D-изображения можно легко найти на изображениях. (Эти точки изображения находятся там, где два черных квадрата касаются друг друга на шахматной доске)
Трехмерная точка предполагает z=0, длину стороны квадратной сетки, и точка также может быть получена;
Точки 3D называются точками объекта, а точки 2D-изображения называются точками изображения.
2.2 Используемые методы
- cv2.findChessboardCorners() находит шаблон сетки прямоугольника, возвращает углы и retval (найти шаблон возвращает True, углы расположены слева направо, сверху вниз);
- cv2.findCirclesGrid() находит модель круговой сетки, говорят, что при использовании круговой сетки достаточно меньшего количества изображений.
- Найдя углы, используйте cv2.cornerSubPix(), чтобы улучшить их точность.
- cv2.drawChessboardCorners() использует этот метод для рисования шаблонов
3. Исходный код
# 相机的内外参,失真系数,校准等。
# 拿到一组3D点对象,2D图像点,可以使用相应的方法进行校准;
import numpy as np
import cv2
import glob
import json
# 将相机矩阵、失真系数写入文件
def write2Npz(ret, mtx, dist, rvecs, tvecs):
# print(ret)
# print(mtx)
# print(dist)
# print(rvecs)
# print(tvecs)
np.savez('qpimgs/B.npz', mtx=mtx, dist=dist, rvecs=rvecs, tvecs=tvecs)
# 校准的终止准则
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 准备3D的对象点,如(0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((6 * 7, 3), np.float32)
objp[:, :2] = np.mgrid[0:7, 0:6].T.reshape(-1, 2)
# 存储3D对象点、2D图像点
objpoints = [] # 现实世界3D点
imgpoints = [] # 图像平面2D点
images = glob.glob('qpimgs/*.jpg')
for fname in images:
origin = cv2.imread(fname)
img = origin.copy()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 查找网格模式,寻找角点
ret, corners = cv2.findChessboardCorners(gray, (7, 6), None)
# 如果找到了,添加对象点,以及经过细化后的图像点
if ret == True:
objpoints.append(objp)
# 提高角点的准确度
cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
imgpoints.append(corners)
# 绘制模式角点和显示
cv2.drawChessboardCorners(img, (7, 6), corners, ret)
cv2.imshow('origin VS pattern', np.hstack([origin, img]))
cv2.waitKey(0)
cv2.destroyAllWindows()
# 有了目标点和图像点可以开始校准了。为此使用函数 cv2.calibrateCamera()。它返回相机矩阵、失真系数、旋转和平移向量等。
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
write2Npz(ret, mtx, dist, rvecs, tvecs)
# cv2.getOptimalNewCameraMatrix() 获取基于自由缩放参数细化相机矩阵。如果缩放参数 alpha=0,则返回具有最少不需要像素的未失真图像。所以它甚至可能会去除图像角落的一些像素。
# 如果 alpha=1,所有像素都会保留一些额外的黑色图像。它还返回一个图像 ROI,可用于裁剪结果。
img = cv2.imread('qpimgs/left12.jpg')
h, w = img.shape[:2]
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 1, (w, h))
# 法一:
# 不失真
dst = cv2.undistort(img, mtx, dist, None, newcameramtx)
# 剪裁ROI图像
x, y, w, h = roi
dst = dst[y:y + h, x:x + w]
cv2.imwrite('qpimgs/calibresult1.jpg', dst)
cv2.imshow("origin", img)
cv2.imshow("caliresult", dst)
# 法二:
# 首先找到从失真图像到未失真图像的映射函数。然后使用重映射功能。
# 不失真
mapx, mapy = cv2.initUndistortRectifyMap(mtx, dist, None, newcameramtx, (w, h), 5)
dst = cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR)
# 剪裁ROI
x, y, w, h = roi
dst = dst[y:y + h, x:x + w]
cv2.imwrite('qpimgs/calibresult2.jpg', dst)
cv2.imshow("caliresult2", dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 校准后可以看到棋盘的所有边缘都是直的。可以使用 Numpy 中的写入函数(np.savez、np.savetxt 等)存储相机矩阵和失真系数以备将来使用。
# 重投影误差
# 重新投影误差可以很好地估计找到的参数的精确程度,应该尽可能接近于零。
# 给定内在、扭曲、旋转和平移矩阵,首先使用 cv2.projectPoints() 将对象点转换为图像点。然后计算变换得到的和角点寻找算法之间的绝对范数。
# 为了找到平均误差,计算了为所有校准图像计算的误差的算术平均值。
mean_error = 0
tot_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)
tot_error += error
print("total error: ", mean_error / len(objpoints))