opencv calibrateCamera функция, дающая плохие результаты - PullRequest
1 голос
/ 05 марта 2020

Я пытаюсь настроить калибровку камеры opencv, но у меня возникают проблемы с выводом правильных данных. У меня есть некалиброванная камера, которую я хотел бы откалибровать, но для проверки своего кода я использую камеру Azure Kinect (цветная камера), поскольку SDK предоставляет для нее правильные характеристики, и я могу их проверить. Я собрал 30 изображений шахматной доски с немного разных углов, которые, как я понимаю, должно быть достаточно, и запустил функцию калибровки, но независимо от того, какие флаги я передаю, я получаю значения для fx и fy, которые довольно сильно отличаются от правильного fx и FY, и коэффициенты искажения, которые дико отличаются. Я делаю что-то неправильно? Мне нужно больше или лучше данных?

Образец изображений, которые я использую, можно найти здесь: https://www.dropbox.com/sh/9pa94uedoe5mlxz/AABisSvgWwBT-bY65lfzp2N3a?dl=0

Сохраните их в c: \ calib__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 (три радиальных и два тангенциальных).

1 Ответ

2 голосов
/ 06 марта 2020

Вам необходимо делать снимки со всего поля зрения камеры, чтобы правильно фиксировать характеристики искажения объектива. Изображения, которые вы предоставляете, показывают шахматную доску только в одной позиции, под небольшим углом.

В идеале у вас должны быть изображения шахматной доски, равномерно распределенные по осям x и y плоскости изображения, вплоть до краев изображения. , Убедитесь, что достаточное количество белой границы вокруг доски всегда видно для надежности обнаружения.

Вы должны также попытаться захватывать изображения, где шахматная доска находится ближе к камере и дальше, а не только на одинаковом расстоянии. С другой стороны, различные углы, которые вы предоставляете, выглядят хорошо.

В этом ответе вы найдете подробное руководство, как обеспечить хорошие результаты калибровки: Как проверить правильность калибровки веб-камеры?

Если сравнить матрицу камеры с Azure Kinect API, она выглядит не так уж плохо. Принципиальная точка довольно хороша, а фокусное расстояние находится в разумных пределах. Если вы улучшите качество ввода с помощью моих советов и SO ответа, который я предоставил, результаты должны быть еще ближе. Сравнение наборов коэффициентов искажения по их расстоянию не очень хорошо работает, функция ошибок не является выпуклой, поэтому вы можете иметь множество локальных минимумов, которые дают относительно хорошие результаты, но они далеки от глобального минимума, который даст лучшие результаты. Если это объяснение имеет смысл для вас.

Относительно вашего бонусного вопроса: я вижу только 8 значений, заполненных в возвращаемом вами результате, остальные равны 0, поэтому не имеют никакого влияния. Я не уверен, будет ли вывод отличаться от этой функции.

...