使用开放 CV 进行畸变校正

use*_*412 5 python opencv calibration distortion camera-calibration

我正在尝试使用开放式 CV 来校正图像失真。我试图纠正的失真理论是桶形和枕形失真的组合,如下所示:

组合桶形失真和枕形失真

我在这里不使用普通相机,而是使用检流计扫描系统(如下所示: http: //www.chinagalvo.com/Content/uploads/2019361893/201912161511117936592.gif),所以我不能只记录像这样的棋盘图案所有 OpenCV 指南都建议。

但我可以将扫描仪移动到目标位置并测量激光束在图像平面中的实际位置,例如绘制为:

实际图像与目标图像叠加

所以我将这些值放入此脚本中的 OpenCVcalibrateCamera函数中:

import numpy as np
import cv2

targetPosX = np.array([-4., -2., 0., 2., 4., -4., -2., 0., 2., 4., -4., -2., 2., 4., -4., -2., 0., 2., 4., -4., -2., 0., 2., 4.])
targetPosY = np.array([-4., -4., -4., -4., -4., -2., -2., -2., -2., -2., 0., 0., 0., 0., 2., 2., 2., 2., 2., 4., 4., 4., 4., 4.])
actualPosX = np.array([-4.21765834, -2.14708042, -0.07755157, 1.9910175, 4.05941744, -4.17816164, -2.10614537, -0.03775821, 2.02883123, 4.09875409, -4.13937186, -2.07079973, 2.07072068, 4.1377518, -4.10200901, -2.03229052, 0.0367603, 2.10655379, 4.17627114, -4.06449305, -1.99426964, 0.07737988, 2.14365487, 4.21625359])
actualPosY = np.array([-4.04808315, -4.08681247, -4.12545265, -4.16807799, -4.20657896, -1.98568911, -2.0217478, -2.06356789, -2.10326313, -2.14456442, 0.07567631, 0.03889721, -0.04043382, -0.08069954, 2.14054726, 2.09940048, 2.05965315, 2.02167639, 1.9800822, 4.20167787, 4.16215278, 4.12334605, 4.08099448, 4.04376011])

scale = 100 # px / mm
height = 9 * scale # range of measured points is -4 to 4mm --> show area from -4.5 to 4.5 with 100 px / mm
width = 9 * scale

def scale_and_shift(array, scl, shift):
    array *= scl
    array += shift
    return array

# shift recorded positon into image coordinate system
targetPosX = scale_and_shift(targetPosX, scale, width / 2.)
targetPosY = scale_and_shift(targetPosY, scale, height / 2.)
actualPosX = scale_and_shift(actualPosX, scale, width / 2.)
actualPosY = scale_and_shift(actualPosY, scale, height / 2.)

# create images
target_image = np.full((height,width), 255)
combined_image = np.full((height,width), 255)
actual_image = np.full((height,width), 255) 
for i in range(len(targetPosX)):
    cv2.circle(target_image, (int(targetPosX[i]), int(targetPosY[i])), 20, 0, -1)
    
    # circle in combined image is target position, full point is actual position
    cv2.circle(combined_image, (int(targetPosX[i]), int(targetPosY[i])), 20, 0, 5)
    cv2.circle(combined_image, (int(actualPosX[i]), int(actualPosY[i])), 20, 0, -1)

    cv2.circle(actual_image, (int(actualPosX[i]), int(actualPosY[i])), 20, 0, -1)

cv2.imwrite("combined_before.png", combined_image)

# create point lists for calibrateCamera function. set 3rd dimension to zero.
targetPoints = np.array([np.vstack([targetPosX, targetPosY]).T]).astype("float32")
targetPoints_zero = np.array([np.vstack([targetPosX, targetPosY, list(np.zeros(len(targetPosX)))]).T]).astype("float32")
imagePoints = np.array([np.vstack([actualPosX, actualPosY]).T]).astype("float32")
imagePoints_zero = np.array([np.vstack([actualPosX, actualPosY, np.zeros(len(actualPosX))]).T]).astype("float32")

# read image to apply to
# saving and reading because just passing the actual_image somehow didn't work
cv2.imwrite("image.png", actual_image)
img = cv2.imread("image.png", cv2.IMREAD_GRAYSCALE)
h, w = img.shape[:2]

# calulate distortion matrix
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(targetPoints_zero, imagePoints, (h,w), None, None)

# refine distortion matrix to avoid cut-off
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w,h), 1, (w,h))

# undistort
dst = cv2.undistort(img, mtx, dist, None, newcameramtx)

cv2.imwrite('calibresult.png', dst)
cv2.imwrite("correction.png", dst - actual_image)

for i in range(len(targetPosX)):
    # circle in combined image is target position, full point is actual position
    cv2.circle(dst, (int(targetPosX[i]), int(targetPosY[i])), 20, 0, 5)

cv2.imwrite('combined_result.png', dst)

Run Code Online (Sandbox Code Playgroud)

然而,结果并不符合预期 - 校正后的图像与目标图像不对齐(实心点 - 校正后的实际点。圆圈 - 目标点): 校正后的实际图像(实心点)与目标图像(圆圈)重叠

比较之前和之后显示仅应用了最小的校正/失真补偿(仅计算了实际图像之前和之后的差异:

实际图像校正前后的差异

有什么办法可以调整calibrateCamera吗?或者这只是不适合这项工作的工具?

Eri*_*mák 2

我没有足够的声誉来发表评论,所以我必须发布答案。

对于难以进行相机校准的用户来说,记住一点可能会有所帮助:并不总是能够完美地拟合镜头畸变模型。如果您不确定自己的镜头,可以尝试直线畸变和鱼眼畸变。关联

在深入研究 OpenCV 校准调整之前,您应该阅读校准良好实践

如果您的设置足够精确,您可以考虑使用更高的特征数。优选使用精细图案。

尝试模拟人们如何使用棋盘。从不同区域和倾斜角度收集图像

  1. 尝试将整个网格移动到不同的方向。尝试尽可能靠近边缘。(失真最强)
  2. 如果您可以选择倾斜标记区域,请这样做。然后您可以使用基本几何形状计算“未扭曲”点。(参见齐次坐标

最后,您可以尝试模拟不同的校准模式。例如不对称的圆。如果您的设置足够精确,您也许能够对每个圆的边界进行采样。