We did not spend much time in optimising the code for speed because there was no need to. But also this drawback might be overcome by changing the set functions for matrixes to pointer operations.
Lets see what we have. We will start with a function which is able to calculate a line with minimum distance to the given points. What we get from the input points is a base point and a direction of the line.
/** * Calculates a base point and a direction for a given set of input points. */ void cvFindLineFromPoints(CvPoint3D32f **points, int numPoints, CvMat *point, CvMat *direction) { CvMat** inputMatrix = new CvMat*[numPoints]; for (int i = 0; i < numPoints; i++) { inputMatrix[i]=cvCreateMat(3, 1, point->type); cvmSet(inputMatrix[i], 0, 0, points[i]->x); cvmSet(inputMatrix[i], 1, 0, points[i]->y); cvmSet(inputMatrix[i], 2, 0, points[i]->z); } cvFindLineFromPoints(inputMatrix, numPoints, point, direction); } void cvFindLineFromPoints(CvMat **inputMatrix, int numPoints, CvMat *point, CvMat *direction) { //--------------------------------------------------------------------------------------- // Approximate a line through a few points in space //--------------------------------------------------------------------------------------- CvMat *covarMatrix=cvCreateMat(3, 3, point->type); CvMat *avgArray=cvCreateMat(3, 1, point->type); /* Covarianze matrix which is 3x3 actually. */ cvCalcCovarMatrix((const void **)inputMatrix, numPoints, covarMatrix, avgArray, CV_COVAR_NORMAL); /* For results equal to Matlab we have to apply a scaling factor which is always 1/(N - 1). */ cvScale(covarMatrix, covarMatrix, 1/((double)(numPoints - 1))); #ifdef DEBUG_OUTPUT for (int row = 0; row < covarMatrix->rows; row++) { double* ptr = (double*)(covarMatrix->data.ptr + row * covarMatrix->step); for (int col = 0; col < covarMatrix->cols; col++) { std::cout << *ptr++ << " "; } std::cout << std::endl; } std::cout << std::endl; std::cout << std::endl; std::cout << "Point: " << cvmGet(avgArray,0,0) << " " << cvmGet(avgArray,1,0) << " " << cvmGet(avgArray,2,0) << std::endl; #endif cvmSet(point, 0, 0, cvmGet(avgArray, 0, 0)); cvmSet(point, 1, 0, cvmGet(avgArray, 1, 0)); cvmSet(point, 2, 0, cvmGet(avgArray, 2, 0)); //** Eigenvectors and Eigenvalues in decreasing order... */ CvMat* E = cvCreateMat(3,3,point->type); CvMat* l = cvCreateMat(3,1,point->type); cvEigenVV(covarMatrix, E, l); #ifdef DEBUG_OUTPUT for (int row = 0; row < E->rows; row++) { const double* ptr = (const double*)(E->data.ptr + row * E->step); for (int col = 0; col < E->cols; col++) { std::cout << *ptr++ << " "; } std::cout << std::endl; } std::cout << std::endl; std::cout << std::endl; #endif CvMat *start = cvCreateMat(3, 1, point->type); CvMat *end = cvCreateMat(3, 1, point->type); CvMat *_dir = cvCreateMat(3, 1, point->type); cvmSet(start, 0, 0, (cvmGet(avgArray, 0, 0) - sqrt(cvmGet(l, 0, 0)) * cvmGet(E, 0, 0))); cvmSet(start, 1, 0, (cvmGet(avgArray, 1, 0) - sqrt(cvmGet(l, 0, 0)) * cvmGet(E, 0, 1))); cvmSet(start, 2, 0, (cvmGet(avgArray, 2, 0) - sqrt(cvmGet(l, 0, 0)) * cvmGet(E, 0, 2))); cvmSet(end, 0, 0, (cvmGet(avgArray, 0, 0) + sqrt(cvmGet(l, 0, 0)) * cvmGet(E, 0, 0))); cvmSet(end, 1, 0, (cvmGet(avgArray, 1, 0) + sqrt(cvmGet(l, 0, 0)) * cvmGet(E, 0, 1))); cvmSet(end, 2, 0, (cvmGet(avgArray, 2, 0) + sqrt(cvmGet(l, 0, 0)) * cvmGet(E, 0, 2))); cvSub(end, start, _dir); cvNormalize(_dir, _dir); #ifdef DEBUG_OUTPUT std::cout << "Dir: " << cvmGet(_dir,0,0) << " " << cvmGet(_dir,1,0) << " " << cvmGet(_dir,2,0) << std::endl; std::cout << std::endl; std::cout << std::endl; #endif cvmSet(direction, 0, 0, cvmGet(_dir, 0, 0)); cvmSet(direction, 1, 0, cvmGet(_dir, 1, 0)); cvmSet(direction, 2, 0, cvmGet(_dir, 2, 0)); cvFree(&covarMatrix); cvFree(&avgArray); cvFree(&start); cvFree(&end); cvFree(&_dir); cvFree(&E); cvFree(&l); }
As one can see the eigenvector corresponding to the highes eigenvalue gives the direction of the line. The quality of the estimation can also be seen in the difference to the other eigenvalues. If one eigenvalue is significant higher then the others the estimation can be taken as good.