00001 #include "aruco/markerdetector.h"
00002 #include <opencv/cv.h>
00003 #include <opencv/highgui.h>
00004 #include <iostream>
00005 #include <fstream>
00006
00007 using namespace std;
00008 using namespace cv;
00009
00010 namespace aruco
00011 {
00012
00013
00014
00015
00016
00017
00018 MarkerDetector::MarkerDetector()
00019 {
00020 _thresMethod=ADPT_THRES;
00021 _thresParam1=_thresParam2=7;
00022 }
00023
00024
00025
00026
00027
00028
00029
00030 MarkerDetector::~MarkerDetector()
00031 {
00032
00033 }
00034
00035 void MarkerDetector::detect(cv::Mat &input,std::vector<Marker> &detectedMarkers, CameraParameters camParams ,float markerSizeMeters ) throw (cv::Exception)
00036 {
00037 detect( input, detectedMarkers,camParams.CameraMatrix ,camParams.Distorsion, markerSizeMeters );
00038
00039 }
00040
00041
00042
00043
00044
00045
00046
00047 void MarkerDetector::detect(Mat &input,vector<Marker> &detectedMarkers,Mat camMatrix ,Mat distCoeff ,float markerSizeMeters ) throw (cv::Exception)
00048 {
00049
00050 if (input.type()!=CV_8UC3) throw cv::Exception(9000,"input.type()!=CV_8UC3","MarkerDetector::detect",__FILE__,__LINE__);
00051 vector<Marker> MarkerCanditates;
00052 detectedMarkers.clear();
00053
00054
00056 cv::cvtColor(input,grey,CV_BGR2GRAY);
00057 thresHold(_thresMethod,grey,thres);
00058
00059 thres.copyTo(thres2);
00060 cv::findContours( thres2 , contours2, hierarchy2,CV_RETR_TREE, CV_CHAIN_APPROX_NONE );
00061 vector<Point> approxCurve;
00063 for (unsigned int i=0;i<contours2.size();i++)
00064 {
00065
00066
00067
00068
00069 {
00070
00071 approxPolyDP( Mat (contours2[i]),approxCurve , double(contours2[i].size())*0.05 , true);
00072
00073
00074 if (approxCurve.size()==4)
00075 {
00076
00077
00078
00079
00080
00081
00082 if (isContourConvex(Mat (approxCurve)))
00083 {
00084
00085
00086 float minDist=1e10;
00087 for (int i=0;i<4;i++)
00088 {
00089 float d= sqrt( (approxCurve[i].x-approxCurve[(i+1)%4].x)*(approxCurve[i].x-approxCurve[(i+1)%4].x) +
00090 (approxCurve[i].y-approxCurve[(i+1)%4].y)*(approxCurve[i].y-approxCurve[(i+1)%4].y));
00091
00092 if (d<minDist) minDist=d;
00093 }
00094
00095 if (minDist>10)
00096 {
00097
00098
00099 MarkerCanditates.push_back(Marker());
00100 for (int i=0;i<4;i++)
00101 {
00102 MarkerCanditates.back().push_back( Point2f(approxCurve[i].x,approxCurve[i].y));
00103 }
00104 }
00105 }
00106 }
00107 }
00108 }
00109
00110
00111
00113 for (unsigned int i=0;i<MarkerCanditates.size();i++)
00114 {
00115
00116
00117
00118 double dx1 = MarkerCanditates[i][1].x - MarkerCanditates[i][0].x;
00119 double dy1 = MarkerCanditates[i][1].y - MarkerCanditates[i][0].y;
00120 double dx2 = MarkerCanditates[i][2].x - MarkerCanditates[i][0].x;
00121 double dy2 = MarkerCanditates[i][2].y - MarkerCanditates[i][0].y;
00122 double o = (dx1*dy2)-(dy1*dx2);
00123
00124 if (o < 0.0)
00125 {
00126 swap(MarkerCanditates[i][1],MarkerCanditates[i][3]);
00127
00128 }
00129 }
00131
00132
00133 vector<pair<int,int> > TooNearCandidates;
00134 for (unsigned int i=0;i<MarkerCanditates.size();i++)
00135 {
00136
00137
00138 for (unsigned int j=i+1;j<MarkerCanditates.size();j++)
00139 {
00140 float dist=0;
00141 for (int c=0;c<4;c++)
00142 dist+= sqrt( (MarkerCanditates[i][c].x-MarkerCanditates[j][c].x)*(MarkerCanditates[i][c].x-MarkerCanditates[j][c].x)+(MarkerCanditates[i][c].y-MarkerCanditates[j][c].y)*(MarkerCanditates[i][c].y-MarkerCanditates[j][c].y));
00143 dist/=4;
00144
00145 if (dist< 10) {
00146 TooNearCandidates.push_back(pair<int,int>(i,j));
00147 }
00148 }
00149 }
00150
00151 vector<bool> toRemove (MarkerCanditates.size());
00152 for (unsigned int i=0;i<toRemove.size();i++) toRemove[i]=false;
00153
00154 for (unsigned int i=0;i<TooNearCandidates.size();i++) {
00155 if ( perimeter(MarkerCanditates[TooNearCandidates[i].first ])>perimeter(MarkerCanditates[ TooNearCandidates[i].second] ))
00156 toRemove[TooNearCandidates[i].second]=true;
00157 else toRemove[TooNearCandidates[i].first]=true;
00158 }
00159
00161 for (unsigned int i=0;i<MarkerCanditates.size();i++)
00162 {
00163 if (!toRemove[i])
00164 {
00165
00166
00167 vector<Point2f> corners;
00168 corners.push_back(MarkerCanditates[i][0]);
00169 corners.push_back(MarkerCanditates[i][1]);
00170 corners.push_back(MarkerCanditates[i][2]);
00171 corners.push_back(MarkerCanditates[i][3]);
00172 cornerSubPix(grey, corners, cvSize(3, 3), cvSize(-1, -1), cvTermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 15, 0.05));
00173 MarkerCanditates[i][0] = corners[0];
00174 MarkerCanditates[i][1] = corners[1];
00175 MarkerCanditates[i][2] = corners[2];
00176 MarkerCanditates[i][3] = corners[3];
00177 Mat canonicalMarker;
00178 warp(input,canonicalMarker,Size(50,50),MarkerCanditates[i]);
00179 int nRotations;
00180 int id=getMarkerId(canonicalMarker,nRotations);
00181 if (id!=-1)
00182 {
00183 detectedMarkers.push_back(MarkerCanditates[i]);
00184 detectedMarkers.back().id=id;
00185
00186 std::rotate(detectedMarkers.back().begin(),detectedMarkers.back().begin()+4-nRotations,detectedMarkers.back().end());
00187 }
00188 }
00189 }
00190
00191
00192
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208 std::sort(detectedMarkers.begin(),detectedMarkers.end());
00209
00210
00211 toRemove.resize(detectedMarkers.size());
00212 for (unsigned int i=0;i<detectedMarkers.size();i++) toRemove[i]=false;
00213
00214 for (int i=0;i<int(detectedMarkers.size())-1;i++) {
00215 if (detectedMarkers[i].id==detectedMarkers[i+1].id && !toRemove[i+1] ) {
00216
00217 if (perimeter(detectedMarkers[i])>perimeter(detectedMarkers[i+1])) toRemove[i+1]=true;
00218 else toRemove[i]=true;
00219 }
00220 }
00221
00222 vector<Marker>::iterator it=detectedMarkers.begin();
00223 for (unsigned int i=0;i<toRemove.size();i++) {
00224 if (toRemove[i]) it=detectedMarkers.erase(it);
00225 else it++;
00226 }
00228
00229 if (camMatrix.rows!=0 && markerSizeMeters>0)
00230 {
00231 for (unsigned int i=0;i<detectedMarkers.size();i++)
00232 detectedMarkers[i].calculateExtrinsics(markerSizeMeters,camMatrix,distCoeff);
00233 }
00234
00235
00236 }
00237
00238
00239
00240
00241
00242
00243
00244 void MarkerDetector::thresHold(int method,Mat &grey,Mat &out)
00245 {
00246 switch (method)
00247 {
00248 case FIXED_THRES:
00249 cv::threshold(grey, out, _thresParam1,255, CV_THRESH_BINARY_INV );
00250 break;
00251 case ADPT_THRES:
00252 cv::adaptiveThreshold(grey,out,255,ADAPTIVE_THRESH_GAUSSIAN_C,THRESH_BINARY_INV,_thresParam1,_thresParam2);
00253
00254
00255 break;
00256 case CANNY:
00257 {
00258
00259
00260
00261
00262 cv::Canny(grey, out, 10, 220);
00263
00264
00265
00266
00267
00268 }break;
00269 }
00270 }
00271
00272
00273
00274
00275
00276
00277 void MarkerDetector::drawAllContours(Mat input)
00278 {
00279 drawContours( input, contours2, -1,Scalar(255,0,255));
00280 }
00281
00282
00283
00284
00285
00286
00287
00288 void MarkerDetector:: drawContour(Mat &in,vector<Point> &contour,Scalar color )
00289 {
00290 for (unsigned int i=0;i<contour.size();i++)
00291 {
00292 cv::rectangle(in,contour[i],contour[i],color);
00293 }
00294 }
00295
00296 void MarkerDetector:: drawApproxCurve(Mat &in,vector<Point> &contour,Scalar color )
00297 {
00298 for (unsigned int i=0;i<contour.size();i++)
00299 {
00300 cv::line( in,contour[i],contour[(i+1)%contour.size()],color);
00301 }
00302 }
00303
00304
00305
00306
00307
00308
00309
00310 void MarkerDetector::draw(Mat out,const vector<Marker> &markers )
00311 {
00312 for (unsigned int i=0;i<markers.size();i++)
00313 {
00314 cv::line( out,markers[i][0],markers[i][1],cvScalar(255,0,0),2,CV_AA);
00315 cv::line( out,markers[i][1],markers[i][2],cvScalar(255,0,0),2,CV_AA);
00316 cv::line( out,markers[i][2],markers[i][3],cvScalar(255,0,0),2,CV_AA);
00317 cv::line( out,markers[i][3],markers[i][0],cvScalar(255,0,0),2,CV_AA);
00318 }
00319 }
00320
00321
00322
00323
00324
00325
00326
00327 void MarkerDetector::warp(Mat &in,Mat &out,Size size, vector<Point2f> points)throw (cv::Exception)
00328 {
00329
00330 if (points.size()!=4) throw cv::Exception(9001,"point.size()!=4","PerpectiveWarper::warp",__FILE__,__LINE__);
00331
00332 Point2f pointsRes[4],pointsIn[4];
00333 for (int i=0;i<4;i++) pointsIn[i]=points[i];
00334 pointsRes[0]=(Point2f(0,0));
00335 pointsRes[1]= Point2f(size.width-1,0);
00336 pointsRes[2]= Point2f(size.width-1,size.height-1);
00337 pointsRes[3]= Point2f(0,size.height-1);
00338
00339 Mat M=getPerspectiveTransform(pointsIn,pointsRes);
00340 cv::warpPerspective(in, out, M, size);
00341
00342 }
00343
00344
00345
00346
00347
00348
00349 int MarkerDetector::hammDistMarker(Mat bits)
00350 {
00351 int ids[4][5]=
00352 {
00353 {
00354 1,0,0,0,0
00355 }
00356 ,
00357 {
00358 1,0,1,1,1
00359 }
00360 ,
00361 {
00362 0,1,0,0,1
00363 }
00364 ,
00365 {
00366 0, 1, 1, 1, 0
00367 }
00368 };
00369 int dist=0;
00370
00371 for (int y=0;y<5;y++)
00372 {
00373 int minSum=1e5;
00374
00375 for (int p=0;p<4;p++)
00376 {
00377 int sum=0;
00378
00379 for (int x=0;x<5;x++)
00380 sum+= bits.at<uchar>(y,x) == ids[p][x]?0:1;
00381 if (minSum>sum) minSum=sum;
00382 }
00383
00384 dist+=minSum;
00385 }
00386
00387 return dist;
00388 }
00389
00390
00391
00392
00393
00394
00395
00396 int MarkerDetector::mat2id(Mat &bits)
00397 {
00398 int val=0;
00399 for (int y=0;y<5;y++)
00400 {
00401 val<<=1;
00402 if ( bits.at<uchar>(y,1)) val|=1;
00403 val<<=1;
00404 if ( bits.at<uchar>(y,3)) val|=1;
00405 }
00406 return val;
00407 }
00408
00409
00410
00411
00412
00413
00414 bool MarkerDetector::correctHammMarker(Mat &bits)
00415 {
00416
00417 bool errors[4];
00418 int ids[4][5]=
00419 {
00420 {
00421 0,0,0,0,0
00422 }
00423 ,
00424 {
00425 0,0,1,1,1
00426 }
00427 ,
00428 {
00429 1,1,0,0,1
00430 }
00431 ,
00432 {
00433 1, 1, 1, 1, 0
00434 }
00435 };
00436
00437 for (int y=0;y<5;y++)
00438 {
00439 int minSum=1e5;
00440
00441 for (int p=0;p<4;p++)
00442 {
00443 int sum=0;
00444
00445 for (int x=0;x<5;x++)
00446 sum+= bits.at<uchar>(y,x) == ids[p][x]?0:1;
00447 if (minSum>sum) minSum=sum;
00448 }
00449 if (minSum!=0) errors[y]=true;
00450 else errors[y]=false;
00451 }
00452
00453 return true;
00454 }
00455
00456
00457
00458
00459
00460
00461
00462 Mat MarkerDetector::rotate(Mat in)
00463 {
00464 Mat out;
00465 in.copyTo(out);
00466 for (int i=0;i<in.rows;i++)
00467 {
00468 for (int j=0;j<in.cols;j++)
00469 {
00470 out.at<uchar>(i,j)=in.at<uchar>(in.cols-j-1,i);
00471 }
00472 }
00473 return out;
00474 }
00475
00476
00477
00478
00479
00480
00481
00482 int MarkerDetector::getMarkerId(Mat &in,int &nRotations)
00483 {
00484 assert(in.rows==in.cols);
00485 Mat grey;
00486 if ( in.type()==CV_8UC1) grey=in;
00487 else cv::cvtColor(in,grey,CV_BGR2GRAY);
00488
00489 threshold(grey, grey,125, 255, THRESH_BINARY|THRESH_OTSU);
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502 int swidth=in.rows/7;
00503 for (int y=0;y<7;y++)
00504 {
00505 int inc=6;
00506 if (y==0 || y==6) inc=1;
00507 for (int x=0;x<7;x+=inc)
00508 {
00509 int Xstart=(x)*(swidth);
00510 int Ystart=(y)*(swidth);
00511 Mat square=grey(Rect(Xstart,Ystart,swidth,swidth));
00512 int nZ=countNonZero(square);
00513 if (nZ> (swidth*swidth) /2) {
00514 return -1;
00515 }
00516 }
00517 }
00518
00519
00520 vector<int> markerInfo(5);
00521 Mat _bits=Mat::zeros(5,5,CV_8UC1);
00522
00523
00524 for (int y=0;y<5;y++)
00525 {
00526
00527 for (int x=0;x<5;x++)
00528 {
00529 int Xstart=(x+1)*(swidth);
00530 int Ystart=(y+1)*(swidth);
00531 Mat square=grey(Rect(Xstart,Ystart,swidth,swidth));
00532 int nZ=countNonZero(square);
00533 if (nZ> (swidth*swidth) /2) _bits.at<uchar>( y,x)=1;
00534 }
00535 }
00536
00537
00538
00539 Mat _bitsFlip;
00540 Mat Rotations[4];
00541 Rotations[0]=_bits;
00542 int dists[4];
00543 dists[0]=hammDistMarker( Rotations[0]) ;
00544 pair<int,int> minDist( dists[0],0);
00545 for (int i=1;i<4;i++)
00546 {
00547
00548 Rotations[i]=rotate(Rotations[i-1]);
00549
00550 dists[i]=hammDistMarker( Rotations[i]) ;
00551 if (dists[i]<minDist.first)
00552 {
00553 minDist.first= dists[i];
00554 minDist.second=i;
00555 }
00556 }
00557
00558
00559
00560 nRotations=minDist.second;
00561 if (minDist.first!=0)
00562 return -1;
00563 else return mat2id(Rotations [ minDist.second]);
00564
00566
00567
00568
00569
00570 }
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596 bool MarkerDetector::isInto(Mat &contour,vector<Point2f> &b)
00597 {
00598
00599 for (unsigned int i=0;i<b.size();i++)
00600 if ( pointPolygonTest( contour,b[i],false)>0) return true;
00601 return false;
00602 }
00603
00604
00605
00606
00607
00608
00609 int MarkerDetector:: perimeter(vector<Point2f> &a)
00610 {
00611 int sum=0;
00612 for (unsigned int i=0;i<a.size();i++) {
00613 int i2=(i+1)%a.size();
00614 sum+= sqrt ( (a[i].x-a[i2].x)*(a[i].x-a[i2].x)+(a[i].y-a[i2].y)*(a[i].y-a[i2].y) ) ;
00615 }
00616 return sum;
00617 }
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637
00641 void MarkerDetector::glGetProjectionMatrix(CameraParameters & CP,Size orgImgSize,Size size,double proj_matrix[16],double gnear,double gfar,bool invert )throw(cv::Exception)
00642 {
00643 if (CP.isValid()==false) throw cv::Exception(9100,"invalid camera parameters","MarkerDetector::glGetProjectionMatrix",__FILE__,__LINE__);
00644
00645 double Ax=double(size.width)/double(orgImgSize.width);
00646 double Ay=double(size.height)/double(orgImgSize.height);
00647 double _fx=CP.CameraMatrix.at<float>(0,0)*Ax;
00648 double _cx=CP.CameraMatrix.at<float>(0,2)*Ax;
00649 double _fy=CP.CameraMatrix.at<float>(1,1)*Ay;
00650 double _cy=CP.CameraMatrix.at<float>(1,2)*Ay;
00651 double cparam[3][4] =
00652 {
00653 {
00654 _fx, 0, _cx, 0
00655 },
00656 {0, _fy, _cy, 0},
00657 {0, 0, 1, 0}
00658 };
00659
00660 argConvGLcpara2( cparam, size.width, size.height, gnear, gfar, proj_matrix, invert );
00661
00662 }
00663
00664 void MarkerDetector::argConvGLcpara2( double cparam[3][4], int width, int height, double gnear, double gfar, double m[16], bool invert )throw(cv::Exception)
00665 {
00666
00667 double icpara[3][4];
00668 double trans[3][4];
00669 double p[3][3], q[4][4];
00670 int i, j;
00671
00672 cparam[0][2] *= -1.0;
00673 cparam[1][2] *= -1.0;
00674 cparam[2][2] *= -1.0;
00675
00676 if ( arParamDecompMat(cparam, icpara, trans) < 0 )
00677 throw cv::Exception(9002,"parameter error","MarkerDetector::argConvGLcpara2",__FILE__,__LINE__);
00678
00679 for ( i = 0; i < 3; i++ )
00680 {
00681 for ( j = 0; j < 3; j++ )
00682 {
00683 p[i][j] = icpara[i][j] / icpara[2][2];
00684 }
00685 }
00686 q[0][0] = (2.0 * p[0][0] / width);
00687 q[0][1] = (2.0 * p[0][1] / width);
00688 q[0][2] = ((2.0 * p[0][2] / width) - 1.0);
00689 q[0][3] = 0.0;
00690
00691 q[1][0] = 0.0;
00692 q[1][1] = (2.0 * p[1][1] / height);
00693 q[1][2] = ((2.0 * p[1][2] / height) - 1.0);
00694 q[1][3] = 0.0;
00695
00696 q[2][0] = 0.0;
00697 q[2][1] = 0.0;
00698 q[2][2] = (gfar + gnear)/(gfar - gnear);
00699 q[2][3] = -2.0 * gfar * gnear / (gfar - gnear);
00700
00701 q[3][0] = 0.0;
00702 q[3][1] = 0.0;
00703 q[3][2] = 1.0;
00704 q[3][3] = 0.0;
00705
00706 for ( i = 0; i < 4; i++ )
00707 {
00708 for ( j = 0; j < 3; j++ )
00709 {
00710 m[i+j*4] = q[i][0] * trans[0][j]
00711 + q[i][1] * trans[1][j]
00712 + q[i][2] * trans[2][j];
00713 }
00714 m[i+3*4] = q[i][0] * trans[0][3]
00715 + q[i][1] * trans[1][3]
00716 + q[i][2] * trans[2][3]
00717 + q[i][3];
00718 }
00719
00720 if (!invert)
00721 {
00722 m[13]=-m[13] ;
00723 m[1]=-m[1];
00724 m[5]=-m[5];
00725 m[9]=-m[9];
00726 }
00727
00728 }
00729
00730 int MarkerDetector::arParamDecompMat( double source[3][4], double cpara[3][4], double trans[3][4] )throw(cv::Exception)
00731 {
00732 int r, c;
00733 double Cpara[3][4];
00734 double rem1, rem2, rem3;
00735
00736 if ( source[2][3] >= 0 )
00737 {
00738 for ( r = 0; r < 3; r++ )
00739 {
00740 for ( c = 0; c < 4; c++ )
00741 {
00742 Cpara[r][c] = source[r][c];
00743 }
00744 }
00745 }
00746 else
00747 {
00748 for ( r = 0; r < 3; r++ )
00749 {
00750 for ( c = 0; c < 4; c++ )
00751 {
00752 Cpara[r][c] = -(source[r][c]);
00753 }
00754 }
00755 }
00756
00757 for ( r = 0; r < 3; r++ )
00758 {
00759 for ( c = 0; c < 4; c++ )
00760 {
00761 cpara[r][c] = 0.0;
00762 }
00763 }
00764 cpara[2][2] = norm( Cpara[2][0], Cpara[2][1], Cpara[2][2] );
00765 trans[2][0] = Cpara[2][0] / cpara[2][2];
00766 trans[2][1] = Cpara[2][1] / cpara[2][2];
00767 trans[2][2] = Cpara[2][2] / cpara[2][2];
00768 trans[2][3] = Cpara[2][3] / cpara[2][2];
00769
00770 cpara[1][2] = dot( trans[2][0], trans[2][1], trans[2][2],
00771 Cpara[1][0], Cpara[1][1], Cpara[1][2] );
00772 rem1 = Cpara[1][0] - cpara[1][2] * trans[2][0];
00773 rem2 = Cpara[1][1] - cpara[1][2] * trans[2][1];
00774 rem3 = Cpara[1][2] - cpara[1][2] * trans[2][2];
00775 cpara[1][1] = norm( rem1, rem2, rem3 );
00776 trans[1][0] = rem1 / cpara[1][1];
00777 trans[1][1] = rem2 / cpara[1][1];
00778 trans[1][2] = rem3 / cpara[1][1];
00779
00780 cpara[0][2] = dot( trans[2][0], trans[2][1], trans[2][2],
00781 Cpara[0][0], Cpara[0][1], Cpara[0][2] );
00782 cpara[0][1] = dot( trans[1][0], trans[1][1], trans[1][2],
00783 Cpara[0][0], Cpara[0][1], Cpara[0][2] );
00784 rem1 = Cpara[0][0] - cpara[0][1]*trans[1][0] - cpara[0][2]*trans[2][0];
00785 rem2 = Cpara[0][1] - cpara[0][1]*trans[1][1] - cpara[0][2]*trans[2][1];
00786 rem3 = Cpara[0][2] - cpara[0][1]*trans[1][2] - cpara[0][2]*trans[2][2];
00787 cpara[0][0] = norm( rem1, rem2, rem3 );
00788 trans[0][0] = rem1 / cpara[0][0];
00789 trans[0][1] = rem2 / cpara[0][0];
00790 trans[0][2] = rem3 / cpara[0][0];
00791
00792 trans[1][3] = (Cpara[1][3] - cpara[1][2]*trans[2][3]) / cpara[1][1];
00793 trans[0][3] = (Cpara[0][3] - cpara[0][1]*trans[1][3]
00794 - cpara[0][2]*trans[2][3]) / cpara[0][0];
00795
00796 for ( r = 0; r < 3; r++ )
00797 {
00798 for ( c = 0; c < 3; c++ )
00799 {
00800 cpara[r][c] /= cpara[2][2];
00801 }
00802 }
00803
00804 return 0;
00805 }
00806
00807 double MarkerDetector::norm( double a, double b, double c )
00808 {
00809 return( sqrt( a*a + b*b + c*c ) );
00810 }
00811
00812 double MarkerDetector::dot( double a1, double a2, double a3,
00813 double b1, double b2, double b3 )
00814 {
00815 return( a1 * b1 + a2 * b2 + a3 * b3 );
00816 }
00817
00818
00819
00820
00821
00822
00823
00824
00825
00826
00827
00828
00829
00830
00831
00832
00833
00834
00835
00836
00837
00838
00839
00840
00841
00842
00843
00844
00845 };