OpenCV - 校准鱼眼镜头误差(病态矩阵)
Posted
技术标签:
【中文标题】OpenCV - 校准鱼眼镜头误差(病态矩阵)【英文标题】:OpenCV - Calibrate fisheye lens error (Ill-conditioned matrix) 【发布时间】:2018-02-28 21:00:34 【问题描述】:我正在尝试按照这些说明校准鱼眼镜头 https://medium.com/@kennethjiang/calibrate-fisheye-lens-using-opencv-333b05afa0b0 在哪里可以找到我用于校准部分的完整代码。
我到达了这一点:
N_OK = len(objpoints)
K = np.zeros((3, 3))
D = np.zeros((4, 1))
rvecs = [np.zeros((1, 1, 3), dtype=np.float64) for i in range(N_OK)]
tvecs = [np.zeros((1, 1, 3), dtype=np.float64) for i in range(N_OK)]
rms, _, _, _, _ = \
cv2.fisheye.calibrate(
objpoints,
imgpoints,
gray.shape[::-1],
K,
D,
rvecs,
tvecs,
calibration_flags,
(cv2.TERM_CRITERIA_EPS+cv2.TERM_CRITERIA_MAX_ITER, 30, 1e-3)
)
print("Found " + str(N_OK) + " valid images for calibration")
print("DIM=" + str(_img_shape[::-1]))
print("K=np.array(" + str(K.tolist()) + ")")
print("D=np.array(" + str(D.tolist()) + ")")
我收到此错误:
Traceback (most recent call last)
<ipython-input-10-deaca9981fe4> in <module>()
13 tvecs,
14 calibration_flags,
---> 15 (cv2.TERM_CRITERIA_EPS+cv2.TERM_CRITERIA_MAX_ITER, 30, 1e-3)
16 )
17 print("Found " + str(N_OK) + " valid images for calibration")
error: C:\ci\opencv_1512688052760\work\modules\calib3d\src\fisheye.cpp:1414:
error: (-3) CALIB_CHECK_COND - Ill-conditioned matrix for input array 0 in
function cv::internal::CalibrateExtrinsics
我不明白发生了什么,我只能在互联网上找到这么少的信息,有没有人遇到过类似的事情并知道如何解决这个问题?
谢谢
这些是我正在使用的棋盘的图像:
https://i.stack.imgur.com/goHIg.jpg https://i.stack.imgur.com/kja3O.jpg https://i.stack.imgur.com/XbIzh.jpg https://i.stack.imgur.com/uQ9Gr.jpg https://i.stack.imgur.com/N8aLJ.jpg https://i.stack.imgur.com/JMmmZ.jpg https://i.stack.imgur.com/QJ8wn.jpg https://i.stack.imgur.com/NmiFQ.jpg https://i.stack.imgur.com/sQHdd.jpg https://i.stack.imgur.com/DNBuD.jpg https://i.stack.imgur.com/vFtOo.jpg https://i.stack.imgur.com/lhkF5.jpg【问题讨论】:
【参考方案1】:我认为这是因为您的变量calibration_flags 设置了CALIB_CHECK_COND。 尝试禁用此标志。没有它,我可以不扭曲您的图像(请参阅下面的链接)。
我不确定此检查的用途(documentation 不是很明确)。即使棋盘可见和检测到,此标志也会拒绝我的 gopro hero 3 的一些图像¹。在我的例子中,20 张图片中的一张没有通过这个测试。此图像的棋盘靠近左边框。
https://i.stack.imgur.com/m2WF6.jpg https://i.stack.imgur.com/KiTRz.jpg https://i.stack.imgur.com/MhHyN.jpg https://i.stack.imgur.com/pSiyG.jpg https://i.stack.imgur.com/drXSL.jpg https://i.stack.imgur.com/DDze1.jpg https://i.stack.imgur.com/b6l8f.jpg https://i.stack.imgur.com/9MrAk.jpg https://i.stack.imgur.com/WYmg5.jpg https://i.stack.imgur.com/rmJ5Q.jpg https://i.stack.imgur.com/K8k8Y.jpg https://i.stack.imgur.com/MiBER.jpg¹ 在 OpenCV 版本 >= 3.4.1 中,error message 告诉您哪个图像未通过测试
【讨论】:
太好了,谢谢!您是否能够保留裁剪区域之外的所有像素? 是的,您可以通过在函数fisheye::initUndistortRectify 中选择适当的参数P
和size
来做到这一点。您应该能够使用函数getOptimalNewCameraMatrix 计算它们。见this QA。但正如用户 alexisrozhkov 所说,可能无法保留所有像素。
我按照相同的说明删除了 CALIB_CHECK_COND 标志,正如 Gabriel 建议的那样。我使用与 Francesco 提供的相同图像来获得 K 和 D。但使用 K 和 D 时我仍然看到一些失真。在 Gabriel 提供的未失真图像上我没有看到这种失真。所以我想知道您是否使用相同的代码,或者您进行了一些修改。下面是一个例子: 失真图像:i.stack.imgur.com/DNBuD.jpg 我的未失真图像:i.stack.imgur.com/GTAE0.jpg Gabriel 的未失真图像:i.stack.imgur.com/rmJ5Q.jpg
@huisinro 恐怕我找不到用于生成这些图像的代码,抱歉。但我怀疑它与网上找到的示例很接近。
加布里埃尔,感谢您的更新。我们现在真的想通了。【参考方案2】:
我没有在python中找到代码,所以我手动检查边缘有棋盘的图像,并把它们一个一个删除,直到错误消失。
【讨论】:
【参考方案3】:正如@Ahmadiah 所提到的,当棋盘落在图像边缘附近时,可能会发生“病态”的事情。处理此问题的一种方法是一张一张地删除图像,并在它们导致校准失败时重试。这是我们这样做的一个例子:
def calibrate_fisheye(all_image_points, all_true_points, image_size):
""" Calibrate a fisheye camera from matching points.
:param all_image_points: Sequence[Array(N, 2)[float32]] of (x, y) image coordinates of the points. (see cv2.findChessboardCorners)
:param all_true_points: Sequence[Array(N, 3)[float32]] of (x,y,z) points. (If from a grid, just put (x,y) on a regular grid and z=0)
Note that each of these sets of points can be in its own reference frame,
:param image_size: The (size_y, size_x) of the image.
:return: (rms, mtx, dist, rvecs, tvecs) where
rms: float - The root-mean-squared error
mtx: array[3x3] A 3x3 camera intrinsics matrix
dst: array[4x1] A (4x1) array of distortion coefficients
rvecs: Sequence[array[N,3,1]] of estimated rotation vectors for each set of true points
tvecs: Sequence[array[N,3,1]] of estimated translation vectors for each set of true points
"""
assert len(all_true_points) == len(all_image_points)
all_true_points = list(all_true_points) # Because we'll modify it in place
all_image_points = list(all_image_points)
while True:
assert len(all_true_points) > 0, "There are no valid images from which to calibrate."
try:
rms, mtx, dist, rvecs, tvecs = cv2.fisheye.calibrate(
objectPoints=[p[None, :, :] for p in all_true_points],
imagePoints=[p[:, None, :] for p in all_image_points],
image_size=image_size,
K=np.zeros((3, 3)),
D=np.zeros((4, 1)),
flags=cv2.fisheye.CALIB_RECOMPUTE_EXTRINSIC + cv2.fisheye.CALIB_CHECK_COND + cv2.fisheye.CALIB_FIX_SKEW,
criteria=(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 1e-6),
)
print('Found a calibration based on well-conditioned images.'.format(len(all_true_points)))
return rms, mtx, dist, rvecs, tvecs
except cv2.error as err:
try:
idx = int(err.message.split('array ')[1][0]) # Parse index of invalid image from error message
all_true_points.pop(idx)
all_image_points.pop(idx)
print("Removed ill-conditioned image from the data. Trying again...".format(idx))
except IndexError:
raise err
【讨论】:
对于python 3,将err.message
替换为str(err)
err.message.split('array ')[1][0]
不只适用于 1 位索引吗? int(err.message.split()[-4])
(对于 python 2)或 int(str(err).split()[-4])
(对于 python 3)应该适用于任何索引【参考方案4】:
我的opencv版本是4.0+,我用这个方法slove它
rms, _, _, _, _ = \
cv2.fisheye.calibrate(
objpoints,
imgpoints,
#gray.shape[::-1],
gray.shape,
K,
D,
rvecs,
tvecs,
calibration_flags,
(cv2.TERM_CRITERIA_EPS+cv2.TERM_CRITERIA_MAX_ITER, 30, 1e-3)
)
【讨论】:
以上是关于OpenCV - 校准鱼眼镜头误差(病态矩阵)的主要内容,如果未能解决你的问题,请参考以下文章