2012-06-07 12 views
8

Dato un insieme di punti 2D, come posso applicare l'opposto di undistortPoints?Calibrazione telecamera OpenCV: ri-distorcere i punti con intrinseca/estrinseca della telecamera

Ho le caratteristiche intrinseche della fotocamera e distCoeffs e vorrei (ad esempio) creare un quadrato e distorcerlo come se la fotocamera l'avesse visto attraverso l'obiettivo.

Ho trovato una patch di 'distorsione' qui: http://code.opencv.org/issues/1387 ma sembrerebbe che questo sia solo un bene per le immagini, voglio lavorare su punti sparsi.

Grazie

risposta

1

Ho avuto esattamente lo stesso bisogno. Ecco una possibile soluzione:

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

Ciao Pascal, ho provato il tuo codice ma sembra non funzionare correttamente. Vedi qui la mia domanda: http://stackoverflow.com/questions/21615298/opencv-distort-back – nkint

5

Questa domanda è un po 'vecchia, ma da quando ho finito qui da una ricerca su Google senza vedere una risposta ordinata ho deciso di rispondere comunque.

C'è una funzione chiamata projectPoints che fa esattamente questo. La versione C viene utilizzato internamente da OpenCV nello stimare i parametri della telecamera con funzioni come calibrateCamera e stereoCalibrate

EDIT: Per utilizzare punti 2D come input, possiamo impostare tutti coordinate z a 1 con convertPointsToHomogeneous Utilizzando projectPoints senza rotazione e nessuna traduzione.

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' in realtà proietta punti 3D su punti 2D tenendo conto della calibrazione, non si ottengono punti distorti da punti 2D non distorti . – ChronoTrigger

+1

Ah, è vero, scusa! – morotspaj

+0

Questo dovrebbe funzionare, ma è necessario il passaggio aggiuntivo di conversione di ciascuno dei 2d punti in coordinate della telecamera non distorte con camera_intrinsics: x2 = (x - cx)/fx ecc. (Lavorando sulla conferma di questo, si potrebbero usare undistortPoints con zero distorsione credo) –

5

Una soluzione semplice è quella di utilizzare initUndistortRectifyMap per ottenere una mappa da coordinate non distorte a quelle distorte:

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); 

modifico per chiarire il codice è corretto:

Cito la documentazione di initUndistortRectifyMap:

per ogni pixel (u, v) nell'immagine di destinazione (corretta e rettificata) , la funzione calcola le coordinate corrispondenti nell'immagine sorgente (ovvero, nell'immagine originale dalla fotocamera.

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

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

+0

Non è questo il problema inverso? –

+0

Mm Io non la penso così. La funzione 'initUndistortRectifyMap' viene solitamente utilizzata insieme a' rimappa' per non visualizzare le immagini complete. Secondo la documentazione di 'rimappa ', il codice dovrebbe essere corretto (lo uso anche io in questo modo). – ChronoTrigger

+0

Ma la mappa dice come passare dall'immagine distorta a quella non distorta. E la domanda era come ottenere una distorsione da non distorta. –

0

undistortPoint è semplice versione inversa di punti di progetto

Nel mio caso mi piacerebbe fare queste:

punti 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; 
} 

Ciò non distorsione i punti per la coordinata molto simile all'immagine di origine, ma senza distorsione. È il comportamento predefinito della funzione cv :: undistort().

punti 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; 
} 

La piccola cosa difficile ecco primo progetto i punti per z = 1 aereo con un modello di telecamera lineare. Dopo di che lo proietti con il modello di fotocamera originale.

Ho trovato questi utili, spero che funzioni anche per voi.

Problemi correlati