reprojectImageTo3D() in OpenCV

j'ai essayé de calculer les coordonnées du monde réel des points à partir d'une carte de disparité en utilisant la fonction reprjectimageto3d() fournie par OpenCV, mais la sortie semble incorrecte.

j'ai les paramètres d'étalonnage, et je calcule la matrice Q en utilisant

stereoRectify(left_cam_matrix, left_dist_coeffs, right_cam_matrix, right_dist_coeffs, frame_size, stereo_params.R, stereo_params.T, R1, R2, P1, P2, Q, CALIB_ZERO_DISPARITY, 0, frame_size, 0, 0);

je crois que cette première étape est correcte, puisque les cadres stéréo sont rectifiés correctement, et la suppression de distorsion que j'effectue semble aussi correcte. La carte des disparités est calculé avec l'algorithme d'appariement de bloc D'OpenCV, et il semble bon aussi.

les points 3D sont calculés comme suit:

cv::Mat XYZ(disparity8U.size(),CV_32FC3); reprojectImageTo3D(disparity8U, XYZ, Q, false, CV_32F);

mais pour une raison quelconque, ils forment une sorte de cône, et ne sont même pas proches de ce à quoi je m'attendais, compte tenu de la carte de disparité. J'ai découvert que d'autres personnes avaient un problème similaire avec cette fonction, et je me demandais si quelqu'un a la solution.

Merci à l'avance!

[EDIT]

stereoRectify(left_cam_matrix, left_dist_coeffs, right_cam_matrix, right_dist_coeffs,frame_size, stereo_params.R, stereo_params.T, R1, R2, P1, P2, Q, CALIB_ZERO_DISPARITY, 0, frame_size, 0, 0);

initUndistortRectifyMap(left_cam_matrix, left_dist_coeffs, R1, P1, frame_size,CV_32FC1, left_undist_rect_map_x, left_undist_rect_map_y);
initUndistortRectifyMap(right_cam_matrix, right_dist_coeffs, R2, P2, frame_size, CV_32FC1, right_undist_rect_map_x, right_undist_rect_map_y);
cv::remap(left_frame, left_undist_rect, left_undist_rect_map_x, left_undist_rect_map_y, CV_INTER_CUBIC, BORDER_CONSTANT, 0);
cv::remap(right_frame, right_undist_rect, right_undist_rect_map_x, right_undist_rect_map_y, CV_INTER_CUBIC, BORDER_CONSTANT, 0);

cv::Mat imgDisparity32F = Mat( left_undist_rect.rows, left_undist_rect.cols, CV_32F );  
StereoBM sbm(StereoBM::BASIC_PRESET,80,5);
sbm.state->preFilterSize  = 15;
sbm.state->preFilterCap   = 20;
sbm.state->SADWindowSize  = 11;
sbm.state->minDisparity   = 0;
sbm.state->numberOfDisparities = 80;
sbm.state->textureThreshold = 0;
sbm.state->uniquenessRatio = 8;
sbm.state->speckleWindowSize = 0;
sbm.state->speckleRange = 0;

// Compute disparity
sbm(left_undist_rect, right_undist_rect, imgDisparity32F, CV_32F );

// Compute world coordinates from the disparity image
cv::Mat XYZ(disparity32F.size(),CV_32FC3);
reprojectImageTo3D(disparity32F, XYZ, Q, false, CV_32F);
print_3D_points(disparity32F, XYZ);

[EDIT]

Ajout d'un code de calcul 3D coords de disparité:

cv::Vec3f *StereoFrame::compute_3D_world_coordinates(int row, int col,
  shared_ptr<StereoParameters> stereo_params_sptr){

 cv::Mat Q_32F;

 stereo_params_sptr->Q_sptr->convertTo(Q_32F,CV_32F);
 cv::Mat_<float> vec(4,1);

 vec(0) = col;
 vec(1) = row;
 vec(2) = this->disparity_sptr->at<float>(row,col);

 // Discard points with 0 disparity    
 if(vec(2)==0) return NULL;
 vec(3)=1;              
 vec = Q_32F*vec;
 vec /= vec(3);
 // Discard points that are too far from the camera, and thus are highly
 // unreliable
 if(abs(vec(0))>10 || abs(vec(1))>10 || abs(vec(2))>10) return NULL;

 cv::Vec3f *point3f = new cv::Vec3f();
 (*point3f)[0] = vec(0);
 (*point3f)[1] = vec(1);
 (*point3f)[2] = vec(2);

    return point3f;
}
17
demandé sur Daniel Lee 2014-03-15 06:36:07

1 réponses

Votre code semble bien pour moi. Il pourrait être un bug avec le reprojectImageTo3D. Essayez de le remplacer par le code suivant (qui a le même rôle):

cv::Mat_<cv::Vec3f> XYZ(disparity32F.rows,disparity32F.cols);   // Output point cloud
cv::Mat_<float> vec_tmp(4,1);
for(int y=0; y<disparity32F.rows; ++y) {
    for(int x=0; x<disparity32F.cols; ++x) {
        vec_tmp(0)=x; vec_tmp(1)=y; vec_tmp(2)=disparity32F.at<float>(y,x); vec_tmp(3)=1;
        vec_tmp = Q*vec_tmp;
        vec_tmp /= vec_tmp(3);
        cv::Vec3f &point = XYZ.at<cv::Vec3f>(y,x);
        point[0] = vec_tmp(0);
        point[1] = vec_tmp(1);
        point[2] = vec_tmp(2);
    }
}

je n'ai jamais utilisé reprojectImageTo3D, cependant j'utilise avec succès un code similaire à l'extrait ci-dessus.

[réponse Initiale]

comme expliqué dans le documentationStereoBM, si vous demandez un CV_16S carte de disparité, vous devez diviser chaque valeur de disparité par 16 avant en les utilisant.

par conséquent, vous devez convertir la carte de disparité comme suit avant de l'utiliser:

imgDisparity16S.convertTo( imgDisparity32F, CV_32F, 1./16);

vous pouvez aussi demander directement un CV_32F carte de Disparité De La structure stéréo, dans ce cas vous obtenez directement les vraies disparités.

10
répondu AldurDisciple 2015-07-15 10:10:35