2012-06-07 21 views
8

Biorąc pod uwagę zestaw punktów 2D, w jaki sposób zastosować odwrotność punktów undistortPoints?Kalibracja kamery OpenCV: Ponowne zniekształcanie punktów za pomocą elementów wewnętrznych kamery/elementów zewnętrznych

Mam wewnętrzną kamerę i distcoofy i chciałbym (na przykład) utworzyć kwadrat i zniekształcić go tak, jakby kamera oglądała go przez obiektyw.

Znalazłem łatkę "zniekształcenia" tutaj: http://code.opencv.org/issues/1387, ale wydaje się, że to jest dobre tylko dla obrazów, chcę pracować na rzadkich punktach.

Dzięki

Odpowiedz

1

Miałem dokładnie tę samą potrzebę. Oto możliwe rozwiązanie:

void MyDistortPoints(const std::vector<cv::Point2d> & src, std::vector<cv::Point2d> & dst, 
        const cv::Mat & cameraMatrix, const cv::Mat & distorsionMatrix) 
{ 
    dst.clear(); 
    double fx = cameraMatrix.at<double>(0,0); 
    double fy = cameraMatrix.at<double>(1,1); 
    double ux = cameraMatrix.at<double>(0,2); 
    double uy = cameraMatrix.at<double>(1,2); 

    double k1 = distorsionMatrix.at<double>(0, 0); 
    double k2 = distorsionMatrix.at<double>(0, 1); 
    double p1 = distorsionMatrix.at<double>(0, 2); 
    double p2 = distorsionMatrix.at<double>(0, 3); 
    double k3 = distorsionMatrix.at<double>(0, 4); 
    //BOOST_FOREACH(const cv::Point2d &p, src) 
    for (unsigned int i = 0; i < src.size(); i++) 
    { 
    const cv::Point2d &p = src[i]; 
    double x = p.x; 
    double y = p.y; 
    double xCorrected, yCorrected; 
    //Step 1 : correct distorsion 
    {  
     double r2 = x*x + y*y; 
     //radial distorsion 
     xCorrected = x * (1. + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2 * r2); 
     yCorrected = y * (1. + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2 * r2); 

     //tangential distorsion 
     //The "Learning OpenCV" book is wrong here !!! 
     //False equations from the "Learning OpenCv" book 
     //xCorrected = xCorrected + (2. * p1 * y + p2 * (r2 + 2. * x * x)); 
     //yCorrected = yCorrected + (p1 * (r2 + 2. * y * y) + 2. * p2 * x); 
     //Correct formulae found at : http://www.vision.caltech.edu/bouguetj/calib_doc/htmls/parameters.html 
     xCorrected = xCorrected + (2. * p1 * x * y + p2 * (r2 + 2. * x * x)); 
     yCorrected = yCorrected + (p1 * (r2 + 2. * y * y) + 2. * p2 * x * y); 
    } 
    //Step 2 : ideal coordinates => actual coordinates 
    { 
     xCorrected = xCorrected * fx + ux; 
     yCorrected = yCorrected * fy + uy; 
    } 
    dst.push_back(cv::Point2d(xCorrected, yCorrected)); 
    } 


} 

void MyDistortPoints(const std::vector<cv::Point2d> & src, std::vector<cv::Point2d> & dst, 
        const cv::Matx33d & cameraMatrix, const cv::Matx<double, 1, 5> & distorsionMatrix) 
{ 
    cv::Mat cameraMatrix2(cameraMatrix); 
    cv::Mat distorsionMatrix2(distorsionMatrix); 
    return MyDistortPoints(src, dst, cameraMatrix2, distorsionMatrix2); 
} 

void TestDistort() 
{ 
    cv::Matx33d cameraMatrix = 0.; 
    { 
    //cameraMatrix Init 
    double fx = 1000., fy = 950.; 
    double ux = 324., uy = 249.; 
    cameraMatrix(0, 0) = fx; 
    cameraMatrix(1, 1) = fy; 
    cameraMatrix(0, 2) = ux; 
    cameraMatrix(1, 2) = uy; 
    cameraMatrix(2, 2) = 1.; 
    } 


    cv::Matx<double, 1, 5> distorsionMatrix; 
    { 
    //distorsion Init 
    const double k1 = 0.5, k2 = -0.5, k3 = 0.000005, p1 = 0.07, p2 = -0.05; 

    distorsionMatrix(0, 0) = k1; 
    distorsionMatrix(0, 1) = k2; 
    distorsionMatrix(0, 2) = p1; 
    distorsionMatrix(0, 3) = p2; 
    distorsionMatrix(0, 4) = k3; 
    } 


    std::vector<cv::Point2d> distortedPoints; 
    std::vector<cv::Point2d> undistortedPoints; 
    std::vector<cv::Point2d> redistortedPoints; 
    distortedPoints.push_back(cv::Point2d(324., 249.));// equals to optical center 
    distortedPoints.push_back(cv::Point2d(340., 200)); 
    distortedPoints.push_back(cv::Point2d(785., 345.)); 
    distortedPoints.push_back(cv::Point2d(0., 0.)); 
    cv::undistortPoints(distortedPoints, undistortedPoints, cameraMatrix, distorsionMatrix); 
    MyDistortPoints(undistortedPoints, redistortedPoints, cameraMatrix, distorsionMatrix); 
    cv::undistortPoints(redistortedPoints, undistortedPoints, cameraMatrix, distorsionMatrix); 

    //Poor man's unit test ensuring we have an accuracy that is better than 0.001 pixel 
    for (unsigned int i = 0; i < undistortedPoints.size(); i++) 
    { 
    cv::Point2d dist = redistortedPoints[i] - distortedPoints[i]; 
    double norm = sqrt(dist.dot(dist)); 
    std::cout << "norm = " << norm << std::endl; 
    assert(norm < 1E-3); 
    } 
} 
+0

Cześć Pascal, próbowałem twój kod, ale wydaje się, że nie działa poprawnie. Zobacz tutaj moje pytanie: http://stackoverflow.com/questions/21615298/opencv-distort-back – nkint

5

To pytanie jest dość stary, ale ponieważ skończyło się tutaj z google nie widząc wymienia odpowiedź postanowiłem odpowiedzieć tak.

Jest to funkcja o nazwie projectPoints, która robi dokładnie to. Wersja C jest stosowany wewnętrznie OpenCV przy określaniu parametrów kamery z funkcjami takimi jak calibrateCamera i stereoCalibrate

Edycja: Aby wykorzystać punkty 2D jako wejście, można ustawić wszystkie Ż współrzędne do 1 convertPointsToHomogeneous i używać projectPoints bez obrotu i brak tłumaczenia.

cv::Mat points2d = ...; 
cv::Mat points3d; 
cv::Mat distorted_points2d; 
convertPointsToHomogeneous(points2d, points3d); 
projectPoints(points3d, cv::Vec3f(0,0,0), cv::Vec3f(0,0,0), camera_matrix, dist_coeffs, distorted_points2d); 
+1

'projectPoints' faktycznie rzutuje punkty 3D na punkty 2D, biorąc pod uwagę kalibrację, nie dostaniesz zniekształconych punktów z niezniekształconych punktów 2D . – ChronoTrigger

+1

Ah, to prawda, przepraszam! – morotspaj

+0

To powinno zadziałać, ale dodatkowy krok konwersji każdego z punktów 2d w niezniekształcone współrzędne kamery z camera_intrinsics jest wymagany: x2 = (x-cx)/fx itp. (Praca nad potwierdzeniem tego, może również użyć undistortPoints z zerową zniekształcenie, które myślę) –

5

Prostym rozwiązaniem jest użycie initUndistortRectifyMap aby otrzymać mapę z niezakłóconych współrzędnych do zniekształconych nich:

cv::Mat K = ...; // 3x3 intrinsic parameters 
cv::Mat D = ...; // 4x1 or similar distortion parameters 
int W = 640; // image width 
int H = 480; // image height 

cv::Mat mapx, mapy; 
cv::initUndistortRectifyMap(K, D, cv::Mat(), K, cv::Size(W, H), 
    CV_32F, mapx, mapy); 

float distorted_x = mapx.at<float>(y, x); 
float distorted_y = mapy.at<float>(y, x); 

edytować wyjaśnienie kod jest poprawny:

Przytoczę dokumentacja initUndistortRectifyMap:

dla każdego piksela (u, v) w miejscu docelowym (poprawione i wyprostowane) obraz, funkcja oblicza odpowiednie współrzędne w obrazie źródłowym (czyli w oryginalnym obrazie z kamery.

map_x (u, v) = x''f_x + c_x

map_y (u, v) = y''f_y + c_y

+0

Czy to nie jest odwrotny problem? –

+0

Mm Nie sądzę. Funkcja 'initUndistortRectifyMap' jest zwykle używana razem z' remap', aby rozróżnić pełne obrazy. Zgodnie z dokumentacją 'remap', kod powinien mieć rację (ja też tego używam). – ChronoTrigger

+0

Ale mapa mówi, jak przejść od zniekształconego do niezniekształconego obrazu. A pytanie brzmiało, jak zostać zniekształconym z niezniekształconego. –

0

undistortPoint jest prosta odwróconej wersji punktów projektu

W moim przypadku chciałbym zrobić to:

punkty undistort: ​​

int undisortPoints(const vector<cv::Point2f> &uv, vector<cv::Point2f> &xy, const cv::Mat &M, const cv::Mat &d) 
{ 
    cv::undistortPoints(uv, xy, M, d, cv::Mat(), M); 
    return 0; 
} 

Spowoduje to niezakłócenie punktów do bardzo podobnej współrzędnej do obrazu początkowego, ale bez zakłóceń. Jest to domyślne zachowanie funkcji cv :: undistort().

punkty redistort: ​​

int distortPoints(const vector<cv::Point2f> &xy, vector<cv::Point2f> &uv, const cv::Mat &M, const cv::Mat &d) 
{ 
    vector<cv::Point2f> xy2; 
    vector<cv::Point3f> xyz; 
    cv::undistortPoints(xy, xy2, M, cv::Mat()); 
    for (cv::Point2f p : xy2)xyz.push_back(cv::Point3f(p.x, p.y, 1)); 
    cv::Mat rvec = cv::Mat::zeros(3, 1, CV_64FC1); 
    cv::Mat tvec = cv::Mat::zeros(3, 1, CV_64FC1); 
    cv::projectPoints(xyz, rvec, tvec, M, d, uv); 
    return 0; 
} 

Trochę kłopotliwe rzeczą jest to pierwszy projekt punkty do Z = 1 samolot z liniowym modelu aparatu. Następnie wyświetlasz go z oryginalnym modelem aparatu.

Znalazłem te przydatne, mam nadzieję, że to również działa dla Ciebie.

Powiązane problemy