opencv calibrateCamera函数产生不良结果

问题描述 投票:1回答:1

我正在尝试使opencv摄像机校准工作,但无法使其输出有效数据。我有一个要校准的未经校准的相机,但是为了测试代码,我使用的是Azure Kinect相机(彩色相机),因为SDK为它提供了正确的内在函数,并且我可以对其进行验证。我从略有不同的角度收集了30张国际象棋棋盘的图像,我认为这应该足够了,并运行校准功能,但是无论我传入什么标志,我得到的fx和fy值都与正确的fx完全不同和fy,失真系数完全不同。难道我做错了什么?我需要更多还是更好的数据?

我正在使用的图像样本可以在这里找到:https://www.dropbox.com/sh/9pa94uedoe5mlxz/AABisSvgWwBT-bY65lfzp2N3a?dl=0

将它们保存在c:\ calibration_test中以运行下面的代码。

#include <filesystem>
#include <iostream>

#include <opencv2/core.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>

using namespace std;

namespace fs = experimental::filesystem;

static bool extractCorners(cv::Mat colorImage, vector<cv::Point3f>& corners3d, vector<cv::Point2f>& corners)
{
  // Each square is 20x20mm
  const float kSquareSize = 0.020f;
  const cv::Size boardSize(7, 9);
  const cv::Point3f kCenterOffset((float)(boardSize.width - 1) * kSquareSize, (float)(boardSize.height - 1) * kSquareSize, 0.f);

  cv::Mat image;
  cv::cvtColor(colorImage, image, cv::COLOR_BGRA2GRAY);

  int chessBoardFlags = cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_NORMALIZE_IMAGE;
  if (!cv::findChessboardCorners(image, boardSize, corners, chessBoardFlags))
  {
    return false;
  }

  cv::cornerSubPix(image, corners, cv::Size(11, 11), cv::Size(-1, -1),
    cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 30, 0.1));

  // Construct the corners
  for (int i = 0; i < boardSize.height; ++i)
    for (int j = 0; j < boardSize.width; ++j)
      corners3d.push_back(cv::Point3f(j * kSquareSize, i * kSquareSize, 0) - kCenterOffset);

  return true;
}

int main()
{
  vector<cv::Mat> frames;
  for (const auto& p : fs::directory_iterator("c:\\calibration_test\\"))
  {
    frames.push_back(cv::imread(p.path().string()));
  }


  int numFrames = (int)frames.size();
  vector<vector<cv::Point2f>> corners(numFrames);
  vector<vector<cv::Point3f>> corners3d(numFrames);

  int framesWithCorners = 0;
  for (int i = 0; i < numFrames; ++i)
  {
    if (extractCorners(frames[i], corners3d[framesWithCorners], corners[framesWithCorners]))
    {
      ++framesWithCorners;
    }
  }

  numFrames = framesWithCorners;
  corners.resize(numFrames);
  corners3d.resize(numFrames);

  // Camera intrinsics come from the Azure Kinect API
  cv::Matx33d cameraMatrix(
    914.111755f, 0.f, 960.887390f,
    0.f, 913.880615f, 551.566528f,
    0.f, 0.f, 1.f);
  vector<float> distCoeffs = { 0.576340079f, -2.71203661f, 0.000563957903f, -0.000239689150f, 1.54344523f, 0.454746544f, -2.53860712f, 1.47272563f };

  cv::Size imageSize = frames[0].size();
  vector<cv::Point3d> rotations;
  vector<cv::Point3d> translations;
  int flags = cv::CALIB_USE_INTRINSIC_GUESS | cv::CALIB_FIX_PRINCIPAL_POINT | cv::CALIB_RATIONAL_MODEL;
  double result = cv::calibrateCamera(corners3d, corners, imageSize, cameraMatrix, distCoeffs, rotations, translations,
    flags);

  // After this call, cameraMatrix has different values for fx and fy, and WILDLY different distortion coefficients.

  cout << "fx: " << cameraMatrix(0, 0) << endl;
  cout << "fy: " << cameraMatrix(1, 1) << endl;
  cout << "cx: " << cameraMatrix(0, 2) << endl;
  cout << "cy: " << cameraMatrix(1, 2) << endl;
  for (size_t i = 0; i < distCoeffs.size(); ++i)
  {
    cout << "d" << i << ": " << distCoeffs[i] << endl;
  }

  return 0;
}

一些示例输出是:

fx: 913.143
fy: 917.965
cx: 960.887
cy: 551.567
d0: 0.327596
d1: -73.1837
d2: -0.00125972
d3: 0.002805
d4: -7.93086
d5: 0.295437
d6: -73.481
d7: -3.25043
d8: 0
d9: 0
d10: 0
d11: 0
d12: 0
d13: 0

知道我在做什么错吗?

奖金问题:为什么我会得到14个失真系数而不是8个?如果我放弃CALIB_RATIONAL_MODEL,那么我只会得到5(三个径向和两个切线)。

opencv computer-vision camera-calibration
1个回答
2
投票

您需要从相机的整个视野​​拍摄图像,以正确捕获镜头畸变特征。您提供的图像仅在一个位置显示国际象棋棋盘,稍微倾斜。

理想情况下,棋盘图像应均匀分布在图像平面的x和y轴上,一直到图像的边缘。为了确保检测的鲁棒性,请确保始终能看到板上有足够的白色边界。

您还应尝试在棋盘距离相机更近且距离更远的位置而不是均匀距离拍摄图像。另一方面,您提供的不同角度看起来不错。

您可以在此答案中找到如何确保良好校准结果的详尽指南:How to verify the correctness of calibration of a webcam?

将您的相机矩阵与来自Azure Kinect API的相机矩阵进行比较,看起来还不错。原理点很漂亮,焦距在合理范围内。如果您通过我的技巧和SO答案提高了输入的质量,则我提供的结果应该更加接近。将失真系数集之间的距离进行比较并不能很好地工作,误差函数不是凸的,因此您可以有很多局部最小值产生相对较好的结果,但它们与全局最小值相差甚远,而最小值将产生最佳结果。如果该说明对您有意义。

关于您的红利问题:我只看到您返回的输出中填写了8个值,其余为0,因此没有任何影响。我不确定输出是否与该函数不同。

© www.soinside.com 2019 - 2024. All rights reserved.