Калибровка камеры на OpenCV

При создании мобильных роботов иногда возникает соблазн поставить на них систему технического зрения. Но как выяснилось заставить СТЗ работать задача не из легких, поэтому мы начнём с простого – построим математическую модель камеры и определим значение всех параметров, входящих в модель, при помощи OpenCV. Эта модель оказывается очень полезной, когда мы хотим производить какие-нибудь измерения при помощи камеры.

 
Начнём с модели камеры.

Image plane – ПЗС матрица камеры;

Pinhole plane – плоскость тонкой линзы;

X – наблюдаемая точка;

x – её проекция;

f – фокусное расстояния;

Optical axis – главная оптическая ось.

 

Если кто помнит школьный курс геометрической оптики, уравнение перспективной проекции выглядит так:

 

 

Теперь учтём тот факт, что обычно главная оптическая ось не проходит через точку ПЗС матрицы с координатами (0, 0).

 

 

Здесь c – координата точки пересечения главной оптической осью ПЗС матрицы.

Запишем данные уравнения для координат X и Y, учитывая факт, что из-за технологической погрешности изготовления матрицы пиксели имеют слегка прямоугольную форму.

 

 

 

 

 

 

su и sv – коэффициенты формы пикселя.

Запишем уравнения перспективной проекции в матричном виде, чтобы с ними было удобнее работать. Да и OpenCV дружит в основном с матрицами.

 
 
 
- координаты точки во внешней системе координат

 

 

- координаты проекции точки

 

 

- матрица проекции

 

- матричное уравнение перспективной проекции

 

 

 

Теперь учтём факт, что линза не тонкая, и она вносит искажение в изображение.

 

 

Данный вид искажения называется радиальным. Он учитывается следующим образом:

 

 

 

Здесь r – расстояние от точки пересечения главной оптической осью ПЗС матрицы до точки проекции; k1, k2 – коэффициенты радиального искажения.

Введём функцию:

 

 

Теперь матричное уравнение перспективной проекции имеет вид:

 

 

 

Кроме радиального искажения существует ещё тангенциальное. Оно возникает из-за того, что плоскость ПЗС матрицы не перпендикулярна главной оптической оси. Его в модели я учитывать не буду: оно очень мало. OpenCV определяет и коэффициенты тангенциального искажения.

При установке камеры тоже появляются погрешности – учтём в модели и их.

Погрешности ориентации:

 

 

 

 

 

 

 

 

Результирующая матрица вращения камеры вокруг требуемой системы координат:

 

 

- углы Эйлера

 

Вектор смещения камеры относительно требуемой системы координат:

 

 

 

 

Теперь модель камеры с учётом радиального искажения и погрешностей установки выглядит так:

 

 

 

 

Перейдём к однородным координатам:

 

 

 

 

 

Здесь Rij - соответствующие компоненты результирующей матрицы вращения камеры вокруг требуемой системы координат.

tx, ty, tz - компоненты вектора смещения камеры относительно требуемой системы координат.

Вот мы и получили модель камеры.

В неё входят внутренние параметры:

fu, fv – фокусные расстояния

cu, cv – положение оптического центра

Внешние параметры: 3 угла Эйлера, 3 компонента вектора трансляции.

А теперь перейдём к определению этих параметров на OpenCV, т.е. к калибровке камеры.

Сам принцип калибровки очень прост – мы наблюдаем несколько изображений шахматной доски, распознаём её углы. И зная как в действительности расположены углы шахматной доски, численно подбираем параметры камеры. OpenCV имеет набор встроенных средств для поддержки такой модели калибровки. Я буду использовать интерфейсы OpenCV на C++.

cv::VideoCapture m_camera;
cv::Mat m_currentFrame;
cv::Size m_cameraResolution;
std::vector<std::vector<cv::Point2f> > m_detectedPoints;
cv::Mat m_cameraMatrix( 3, 3, CV_64FC1 );
cv::Mat m_distortionCoefficients( 5,1,CV_64FC1 );
cv::Mat m_R;
cv::Mat m_T;
cv::Mat m_grayImage;
std::vector<std::vector<cv::Point3f> > m_realPoints;
size_t m_framesGrabed( 0 );
const cv::Size patternSize( 7,7 );
m_camera.open( CV_CAP_ANY );

Сохраняем разрешение камеры.

m_cameraResolution  = cv::Size( ( int ) m_camera.get( CV_CAP_PROP_FRAME_WIDTH ), ( int ) m_camera.get( CV_CAP_PROP_FRAME_HEIGHT ) );

Получаем текущую картинку с камеры.

m_camera >> m_currentFrame;

Ищем шахматную доску на изображении

if( cv::findChessboardCorners( m_currentFrame, patternSize, m_detectedPoints[m_framesGrabed] ) ) {

Переводим изображение из RGB в градации серого

      cv::cvtColor( m_currentFrame, m_grayImage, CV_BGR2GRAY );

Уточняем координаты углов шахматной доски

      cv::cornerSubPix( m_grayImage, m_detectedPoints[m_framesGrabed],
                        cv::Size( 11,11 ), cv::Size( -1, -1 ), cv::TermCriteria(cv::TermCriteria::EPS | cv::TermCriteria::MAX_ITER, 30, 0.1));
      cv::drawChessboardCorners( m_currentFrame, patternSize, m_detectedPoints[m_framesGrabed], true );
      ++ m_framesGrabed;
}
std::vector<cv::Mat> rvecs(m_realPoints.size());
std::vector<cv::Mat> tvecs(m_realPoints.size());

Шестую степень в полиноме, учитывающем радиальное искажение, откинем.

m_distortionCoefficients = cv::Scalar( 0 );
m_cameraMatrix=cv::Scalar( 0 );
int flags = cv::CALIB_FIX_K3;

Задаём начальные значения параметрам камеры.

m_cameraMatrix.at( 0, 0 ) = m_initialFocusValue;
m_cameraMatrix.at( 0, 2 ) = m_cameraResolution.width >> 1;
m_cameraMatrix.at( 1, 1 ) = m_initialFocusValue;
m_cameraMatrix.at( 1, 2 ) = m_cameraResolution.height >> 1;
m_cameraMatrix.at( 2, 2 ) = 1.0;
flags |= cv::CALIB_USE_INTRINSIC_GUESS;

Выполним калибровку.

cv::calibrateCamera( m_realPoints, m_detectedPoints, m_cameraResolution, m_cameraMatrix, m_distortionCoefficients, rvecs, tvecs, flags );
m_R = rvecs[0];
m_T = tvecs[0];

Скачать программу для калибровки камеры(OpenCV + Qt).

Leave a Reply

You must be logged in to post a comment.