findEssentialMat for coplanar points
[this is a copy of a [question I just posted on StackOverflow](http://stackoverflow.com/questions/36844139/opencv-findessentialmat)]
I came to a conclusion that OpenCV's findEssentialMat is not working properly for coplanar points. The documentation specifies that it uses Nister's 5 point algorithm, and the corresponding paper declares that the algorithm works fine for coplanar points.
void main() {
ofstream log;
log.open("errorLog.txt");
srand((time(NULL) % RAND_MAX) * RAND_MAX);
/******* camera properties *******/
Mat camMat = Mat::eye(3, 3, CV_64F);
Mat distCoeffs = Mat::zeros(4, 1, CV_64F);
/******* pose 1 *******/
Mat rVec1 = (Mat_<double>(3, 1) << 0, 0, 0);
Mat tVec1 = (Mat_<double>(3, 1) << 0, 0, 1);
/******* pose 2 *******/
Mat rVec2 = (Mat_<double>(3, 1) << 0.0, 0.0, 0);
Mat tVec2 = (Mat_<double>(3, 1) << 0.2, 0, 1); // 2nd camera pose is just a pose1 translated by 0.2 along the X axis
int iterCount = 50;
int N = 40;
for (int j = 0; j < iterCount; j++)
{
/******* generate 3D points *******/
vector<Point3f> points3d = generatePlanarPoints(N);
/******* project 3D points from pose 1 *******/
vector<Point2f> points2d1;
projectPoints(points3d, rVec1, tVec1, camMat, distCoeffs, points2d1);
/******* project 3D points from pose 2 *******/
vector<Point2f> points2d2;
projectPoints(points3d, rVec2, tVec2, camMat, distCoeffs, points2d2);
/******* add noise to 2D points *******/
std::default_random_engine generator;
double noise = 1.0 / 640;
if (noise > 0.0) {
std::normal_distribution<double> distribution(0.0, noise);
for (int i = 0; i < N; i++)
{
points2d1[i].x += distribution(generator);
points2d1[i].y += distribution(generator);
points2d2[i].x += distribution(generator);
points2d2[i].y += distribution(generator);
}
}
/******* find transformation from 2D - 2D correspondences *******/
double threshold = 2.0 / 640;
Mat essentialMat = findEssentialMat(points2d1, points2d2, 1.0, Point(0,0), RANSAC, 0.999, threshold);
Mat estimatedRMat1, estimatedRMat2, estimatedTVec;
decomposeEssentialMat(essentialMat, estimatedRMat1, estimatedRMat2, estimatedTVec);
Mat estimatedRVec1, estimatedRVec2;
Rodrigues(estimatedRMat1, estimatedRVec1);
Rodrigues(estimatedRMat2, estimatedRVec2);
double minError = min(norm(estimatedRVec1 - rVec2), norm(estimatedRVec2 - rVec2));
log << minError << endl; // logging errors
}
log.flush();
log.close();
return;
}
The points are generated like this:
vector<Point3f> generatePlanarPoints(int N) {
float span = 5.0;
vector<Point3f> points3d;
for (int i = 0; i < N; i++)
{
float x = ((float)rand() / RAND_MAX - 0.5) * span;
float y = ((float)rand() / RAND_MAX - 0.5) * span;
float z = 0;
Point3f point3d(x,y,z);
points3d.push_back(point3d);
}
return points3d;
}
Excerpt from errorLog.txt
file:
0
0.199337
0.199337
0.199337
0.199338
0
0.199337
0
0
0.199337
0.199337
This shows us that algorithm sometimes performs good (error == 0), and sometimes something weird happens (error == 0.199337). Is there any other explanation for this?
Obviously, the algorithm is deterministic and error 0.199337 will appear for a specific configuration of points. What is this configuration, I wasn't able to figure out.
I also experimented with different prob and threshold parameters for findEssentialMat. And I tried using more/less points and different camera poses... same thing is happening.
I'm facing a similar problem with this function. My guess is there is a problem with the RANSAC optimization, but I hope that someone could provide an answer.
I actually tried a different implementation and found the same problem a week or so ago. I think the problem is with the 5 point algorithm.