OpenCV: фундаментальная точность матрицы
Я пытаюсь вычислить основную матрицу из 2 изображений (разные фотографии статической сцены, сделанные одной и той же камерой).
Я рассчитал его с помощью findFundamentalMat и использовал результат для вычисления других матриц (Essential, Rotation, ...). Результаты были явно неправильными. Итак, я постарался убедиться в точности рассчитанной фундаментальной матрицы.
Используя уравнение эпиполярного ограничения, I Вычислили фундаментальную матричную ошибку. Ошибка очень высокая (вроде несколько сотен). Я не знаю, что не так с моим кодом. Я действительно ценю любую помощь. В частности: есть ли что-то, чего мне не хватает в фундаментальных матричных вычислениях? и способ, которым я вычисляю ошибку правильно?
Кроме того, я запустил код с очень разным количеством совпадений. Обычно есть много выбросов. например, в случае с более чем 80 совпадениями, было только 10 вкладчиков.
Mat img_1 = imread( "imgl.jpg", CV_LOAD_IMAGE_GRAYSCALE );
Mat img_2 = imread( "imgr.jpg", CV_LOAD_IMAGE_GRAYSCALE );
if( !img_1.data || !img_2.data )
{ return -1; }
//-- Step 1: Detect the keypoints using SURF Detector
int minHessian = 1000;
SurfFeatureDetector detector( minHessian );
std::vector<KeyPoint> keypoints_1, keypoints_2;
detector.detect( img_1, keypoints_1 );
detector.detect( img_2, keypoints_2 );
//-- Step 2: Calculate descriptors (feature vectors)
SurfDescriptorExtractor extractor;
Mat descriptors_1, descriptors_2;
extractor.compute( img_1, keypoints_1, descriptors_1 );
extractor.compute( img_2, keypoints_2, descriptors_2 );
//-- Step 3: Matching descriptor vectors with a brute force matcher
BFMatcher matcher(NORM_L1, true);
std::vector< DMatch > matches;
matcher.match( descriptors_1, descriptors_2, matches );
vector<Point2f>imgpts1,imgpts2;
for( unsigned int i = 0; i<matches.size(); i++ )
{
// queryIdx is the "left" image
imgpts1.push_back(keypoints_1[matches[i].queryIdx].pt);
// trainIdx is the "right" image
imgpts2.push_back(keypoints_2[matches[i].trainIdx].pt);
}
//-- Step 4: Calculate Fundamental matrix
Mat f_mask;
Mat F = findFundamentalMat (imgpts1, imgpts2, FM_RANSAC, 0.5, 0.99, f_mask);
//-- Step 5: Calculate Fundamental matrix error
//Camera intrinsics
double data[] = {1189.46 , 0.0, 805.49,
0.0, 1191.78, 597.44,
0.0, 0.0, 1.0};
Mat K(3, 3, CV_64F, data);
//Camera distortion parameters
double dist[] = { -0.03432, 0.05332, -0.00347, 0.00106, 0.00000};
Mat D(1, 5, CV_64F, dist);
//working with undistorted points
vector<Point2f> undistorted_1,undistorted_2;
vector<Point3f> line_1, line_2;
undistortPoints(imgpts1,undistorted_1,K,D);
undistortPoints(imgpts2,undistorted_2,K,D);
computeCorrespondEpilines(undistorted_1,1,F,line_1);
computeCorrespondEpilines(undistorted_2,2,F,line_2);
double f_err=0.0;
double fx,fy,cx,cy;
fx=K.at<double>(0,0);fy=K.at<double>(1,1);cx=K.at<double>(0,2);cy=K.at<double>(1,2);
Point2f pt1, pt2;
int inliers=0;
//calculation of fundamental matrix error for inliers
for (int i=0; i<f_mask.size().height; i++)
if (f_mask.at<char>(i)==1)
{
inliers++;
//calculate non-normalized values
pt1.x = undistorted_1[i].x * fx + cx;
pt1.y = undistorted_1[i].y * fy + cy;
pt2.x = undistorted_2[i].x * fx + cx;
pt2.y = undistorted_2[i].y * fy + cy;
f_err += = fabs(pt1.x*line_2[i].x +
pt1.y*line_2[i].y + line_2[i].z)
+ fabs(pt2.x*line_1[i].x +
pt2.y*line_1[i].y + line_1[i].z);
}
double AvrErr = f_err/inliers;
3 ответа
Учитывая, что мы снабжены внутренней матрицей K и матрицей искажений D, мы должны не искажать точки изображения перед подачей его в findFundamentalMat и впредь работать с неискаженными координатами изображения (т.е. для вычисления ошибки). Я обнаружил, что это простое изменение уменьшило максимальную погрешность любой пары точек изображения с 176,0f до 0,2, а число подстроек увеличилось с 18 до 77.
Я также поиграл с нормализацией неискаженных точек изображения перед тем, как найти метод FundamentalMat, который уменьшил максимальную ошибку любой пары точек изображения почти до нуля, хотя это больше не увеличивает число подстроек.
const float kEpsilon = 1.0e-6f; float sampsonError(const Mat &dblFMat, const Point2f &pt1, const Point2f &pt2) { Mat m_pt1(3, 1 , CV_64FC1 );//m_pt1(pt1); Mat m_pt2(3, 1 , CV_64FC1 ); m_pt1.at<double>(0,0) = pt1.x; m_pt1.at<double>(1,0) = pt1.y; m_pt1.at<double>(2,0) = 1.0f; m_pt2.at<double>(0,0) = pt2.x; m_pt2.at<double>(1,0) = pt2.y; m_pt2.at<double>(2,0) = 1.0f; assert(dblFMat.rows==3 && dblFMat.cols==3); assert(m_pt1.rows==3 && m_pt1.cols==1); assert(m_pt2.rows==3 && m_pt2.cols==1); Mat dblFMatT(dblFMat.t()); Mat dblFMatp1=(dblFMat * m_pt1); Mat dblFMatTp2=(dblFMatT * m_pt2); assert(dblFMatp1.rows==3 && dblFMatp1.cols==1); assert(dblFMatTp2.rows==3 && dblFMatTp2.cols==1); Mat numerMat=m_pt2.t() * dblFMatp1; double numer=numerMat.at<double>(0,0); if (numer < kEpsilon) { return 0; } else { double denom=dblFMatp1.at<double>(0,0) + dblFMatp1.at<double>(1,0) + dblFMatTp2.at<double>(0,0) + dblFMatTp2.at<double>(1,0); double ret=(numer*numer)/denom; return (numer*numer)/denom; } } #define UNDISTORT_IMG_PTS 1 #define NORMALIZE_IMG_PTS 1 int filter_imgpts_pairs_with_epipolar_constraint( const vector<Point2f> &raw_imgpts_1, const vector<Point2f> &raw_imgpts_2, int imgW, int imgH ) { #if UNDISTORT_IMG_PTS //Camera intrinsics double data[] = {1189.46 , 0.0, 805.49, 0.0, 1191.78, 597.44, 0.0, 0.0, 1.0}; Mat K(3, 3, CV_64F, data); //Camera distortion parameters double dist[] = { -0.03432, 0.05332, -0.00347, 0.00106, 0.00000}; Mat D(1, 5, CV_64F, dist); //working with undistorted points vector<Point2f> unnormalized_imgpts_1,unnormalized_imgpts_2; undistortPoints(raw_imgpts_1,unnormalized_imgpts_1,K,D); undistortPoints(raw_imgpts_2,unnormalized_imgpts_2,K,D); #else vector<Point2f> unnormalized_imgpts_1(raw_imgpts_1); vector<Point2f> unnormalized_imgpts_2(raw_imgpts_2); #endif #if NORMALIZE_IMG_PTS float c_col=imgW/2.0f; float c_row=imgH/2.0f; float multiply_factor= 2.0f/(imgW+imgH); vector<Point2f> final_imgpts_1(unnormalized_imgpts_1); vector<Point2f> final_imgpts_2(unnormalized_imgpts_2); for( auto iit=final_imgpts_1.begin(); iit != final_imgpts_1.end(); ++ iit) { Point2f &imgpt(*iit); imgpt.x=(imgpt.x - c_col)*multiply_factor; imgpt.y=(imgpt.y - c_row)*multiply_factor; } for( auto iit=final_imgpts_2.begin(); iit != final_imgpts_2.end(); ++ iit) { Point2f &imgpt(*iit); imgpt.x=(imgpt.x - c_col)*multiply_factor; imgpt.y=(imgpt.y - c_row)*multiply_factor; } #else vector<Point2f> final_imgpts_1(unnormalized_imgpts_1); vector<Point2f> final_imgpts_2(unnormalized_imgpts_2); #endif int algorithm=FM_RANSAC; //int algorithm=FM_LMEDS; vector<uchar>status; Mat F = findFundamentalMat (final_imgpts_1, final_imgpts_2, algorithm, 0.5, 0.99, status); int n_inliners=std::accumulate(status.begin(), status.end(), 0); assert(final_imgpts_1.size() == final_imgpts_2.size()); vector<float> serr; for( unsigned int i = 0; i< final_imgpts_1.size(); i++ ) { const Point2f &p_1(final_imgpts_1[i]); const Point2f &p_2(final_imgpts_2[i]); float err= sampsonError(F, p_1, p_2); serr.push_back(err); } float max_serr=*max_element(serr.begin(), serr.end()); cout << "found " << raw_imgpts_1.size() << "matches " << endl; cout << " and " << n_inliners << " inliners" << endl; cout << " max sampson err" << max_serr << endl; return 0; }
Кажется, что вы не нормализуете свои очки, прежде чем вычислить фундаментальную матрицу. Может случиться так, что openCV findFundamentalMat не использует нормализованный 8-точечный алгоритм, а только тот, который не нормализован. Если это так, ваши результаты будут неправильными из-за отсутствия нормализации.
Я полагаю, что проблема заключается в том, что, поскольку вы вычислили фундаментальную матрицу только на основе сопоставления грубой силы, вам следует провести дополнительную оптимизацию для этой соответствующей точки, например тест на соотношение и симметричный тест. Я рекомендую вам подготовить страницу 233 из книги "Руководство по программированию приложений OpenCV2 Computer Vision", глава 9. Она очень хорошо объяснена!