Как правильно откалибровать мою камеру с широкоугольным объективом с помощью openCV?

Я пытаюсь откалибровать камеру с объективом «рыбий глаз». Поэтому я использовал модуль объектива «рыбий глаз», но получаю странные результаты независимо от того, какие параметры искажения я исправляю. Я использую это входное изображение: https://i.imgur.com/apBuAwF.png

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

Это лучшее, что я мог получить, вывод: https://imgur.com/a/XeXk5

В настоящее время я не знаю наизусть, каковы размеры сенсора камеры, но на основе фокусного расстояния в пикселях, которое рассчитывается в моей азотной матрице, я делаю вывод, что размер моего сенсора составляет примерно 3,3 мм (при условии, что мое физическое фокусное расстояние составляет 1,8 мм. ), что мне кажется реалистичным. Тем не менее, неискаживая входное изображение, я получаю ерунду. Может ли кто-нибудь сказать мне, что я делаю неправильно?

матрицы и среднеквадратичные значения, полученные при калибровке:

K:[263.7291703200009, 0, 395.1618975493187;
 0, 144.3800397321767, 188.9308218101271;
 0, 0, 1]

D:[0, 0, 0, 0]

rms: 9.27628

мой код:

#include <opencv2/opencv.hpp>
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/ccalib/omnidir.hpp"

using namespace std;
using namespace cv;

vector<vector<Point2d> > points2D;
vector<vector<Point3d> > objectPoints;

Mat src;

//so that I don't have to select them manually every time
void initializePoints2D()
{
    points2D[0].push_back(Point2d(234, 128));
    points2D[0].push_back(Point2d(300, 124));
    points2D[0].push_back(Point2d(381, 126));
    points2D[0].push_back(Point2d(460, 127));
    points2D[0].push_back(Point2d(529, 137));
    points2D[0].push_back(Point2d(207, 147));
    points2D[0].push_back(Point2d(280, 147));
    points2D[0].push_back(Point2d(379, 146));
    points2D[0].push_back(Point2d(478, 153));
    points2D[0].push_back(Point2d(551, 165));
    points2D[0].push_back(Point2d(175, 180));
    points2D[0].push_back(Point2d(254, 182));
    points2D[0].push_back(Point2d(377, 185));
    points2D[0].push_back(Point2d(502, 191));
    points2D[0].push_back(Point2d(586, 191));
    points2D[0].push_back(Point2d(136, 223));
    points2D[0].push_back(Point2d(216, 239));
    points2D[0].push_back(Point2d(373, 253));
    points2D[0].push_back(Point2d(534, 248));
    points2D[0].push_back(Point2d(624, 239));
    points2D[0].push_back(Point2d(97, 281));
    points2D[0].push_back(Point2d(175, 322));
    points2D[0].push_back(Point2d(370, 371));
    points2D[0].push_back(Point2d(578, 339));
    points2D[0].push_back(Point2d(662, 298));


    for(int j=0; j<25;j++)
    {   
        circle(src, points2D[0].at(j), 5, Scalar(0, 0, 255), 1, 8, 0);
    }

    imshow("src with circles", src);
    waitKey(0);
}

int main(int argc, char** argv)
{
    Mat srcSaved;

    src = imread("images/frontCar.png");
    resize(src, src, Size(), 0.5, 0.5);
    src.copyTo(srcSaved);

    vector<Point3d> objectPointsRow;
    vector<Point2d> points2DRow;
    objectPoints.push_back(objectPointsRow);
    points2D.push_back(points2DRow);

    for(int i=0; i<5;i++)
    {

        for(int j=0; j<5;j++)
        {
            objectPoints[0].push_back(Point3d(5*j,5*i,1));        
        }
    }

    initializePoints2D();
    cv::Matx33d K;
    cv::Vec4d D;
    std::vector<cv::Vec3d> rvec;
    std::vector<cv::Vec3d> tvec;


    int flag = 0;
    flag |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC;
    flag |= cv::fisheye::CALIB_CHECK_COND;
    flag |= cv::fisheye::CALIB_FIX_SKEW; 
    flag |= cv::fisheye::CALIB_FIX_K1; 
    flag |= cv::fisheye::CALIB_FIX_K2; 
    flag |= cv::fisheye::CALIB_FIX_K3; 
    flag |= cv::fisheye::CALIB_FIX_K4; 


    double rms =cv::fisheye::calibrate(
 objectPoints, points2D, src.size(), 
 K, D, rvec, tvec, flag, cv::TermCriteria(3, 20, 1e-6)     
 );

    Mat output;
    cerr<<"K:"<<K<<endl;
    cerr<<"D:"<<D<<endl;
    cv::fisheye::undistortImage(srcSaved, output, K, D);
    cerr<<"rms: "<<rms<<endl;
    imshow("output", output);
    waitKey(0);

    cerr<<"image .size: "<<srcSaved.size()<<endl;

}

Если у кого-то есть идея, не стесняйтесь поделиться кодом на Python или на C ++. Все, что плывет на вашей лодке.

РЕДАКТИРОВАТЬ:

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

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

В итоге я использовал это изображение с шахматной доской: предоставлено https://imgur.com/a/WlLBR на этом веб-сайте: https://sites.google.com/site/scarabotix/ocamcalib-toolbox/ocamcalib-toolbox-download-page Но результаты по-прежнему очень плохие: диагональные линии, как на другом выходном изображении, которое я опубликовал.

Спасибо


person privetDruzia    schedule 14.09.2017    source источник
comment
Разве нельзя просто распечатать изображение формата А4? Вот так его откалибровали.   -  person karlphillip    schedule 14.09.2017
comment
@karlphillip В настоящее время у меня нет физического доступа к камере. Также на основе того, что я нашел в Интернете. Использование шахматной доски не обязательно.   -  person privetDruzia    schedule 14.09.2017
comment
Да, я имел в виду, что это был более простой способ сделать это. Удачи!   -  person karlphillip    schedule 15.09.2017
comment
@privetDruzia единственное, что я могу придумать, это изменить флаги, а также termCriteria (поставить больше раундов, например, 100).   -  person api55    schedule 15.09.2017


Ответы (1)


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

Вам понадобится как минимум два изображения в разных 3D-ориентациях или установка для 3D-калибровки, где точки не копланарны. Конечно, на практике для точной калибровки требуется не менее 20 изображений.

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

Удачи!

person Dima    schedule 15.09.2017
comment
Большое спасибо за ваш ответ, я займусь этим! Не могли бы вы подробнее остановиться на субпиксельной точности? AFAIK углы ковра или шахматной доски обнаруживаются в пространстве изображения, например, с помощью детектора углов Харриса. Whcih означает, что местоположения в пикселях углов, возвращаемые этой функцией, являются целочисленными значениями. - person privetDruzia; 15.09.2017
comment
В итоге я использовал это изображение с шахматной доской: imgur.com/a/WlLBR, которое я нашел на этом веб-сайт: sites.google.com/site/scarabotix/ ocamcalib-toolbox / Но результаты по-прежнему очень плохие: диагональные линии, как на другом выходном изображении, которое я опубликовал. - person privetDruzia; 15.09.2017
comment
@privetDruzia, да, алгоритм Харриса сам по себе дает углы в целочисленных координатах. Затем вы берете пиксельную окрестность с центром в углу и подгоняете двухмерную квадратичную функцию к функции Харриса в этой окрестности. Затем вы вычисляете минимум параболоида и используете его как уточненное положение угла. Большинство функций, которые обнаруживают точки интереса, сделают это за вас. - person Dima; 15.09.2017
comment
@privetDruzia Когда вы использовали изображение шахматной доски, вы все еще использовали только одно изображение? Не говоря уже о том, что вам нужно делать снимки собственной камерой, если это то, что вы хотите откалибровать. - person Dima; 15.09.2017
comment
Мне удалось отменить искажение изображения, используя только одно изображение без шахматной доски :). Этот ввод: imgur.com/a/ZmpmX дал мне следующий результат: imgur.com/a/ZmpmX Теперь у меня почти неискаженный ковер. Я мог бы получить лучшие результаты, используя больше изображений, например, с шахматной доской повсюду, но это намного ближе к тому, что я ожидал. Что вы честно думаете об этом? - person privetDruzia; 16.09.2017
comment
Думаю, вы дважды публиковали одну и ту же ссылку. Кроме того, независимо от того, что я думаю о ваших результатах, вы не можете откалибровать только одно изображение плоских точек. - person Dima; 16.09.2017