00001
00030 #include <aruco/arucofidmarkers.h>
00031 #include <cstdio>
00032 #include <opencv2/imgproc/imgproc.hpp>
00033 using namespace cv;
00034 using namespace std;
00035 namespace aruco {
00036
00037
00038
00039
00040
00041
00042
00045 Mat FiducidalMarkers::createMarkerImage(int id,int size,bool addWaterMark) throw (cv::Exception)
00046 {
00047 Mat marker(size,size, CV_8UC1);
00048 marker.setTo(Scalar(0));
00049 if (0<=id && id<1024) {
00050
00051 int swidth=size/7;
00052 int ids[4]={0x10,0x17,0x09,0x0e};
00053 for (int y=0;y<5;y++) {
00054 int index=(id>>2*(4-y)) & 0x0003;
00055 int val=ids[index];
00056 for (int x=0;x<5;x++) {
00057 Mat roi=marker(Rect((x+1)* swidth,(y+1)* swidth,swidth,swidth));
00058 if ( ( val>>(4-x) ) & 0x0001 ) roi.setTo(Scalar(255));
00059 else roi.setTo(Scalar(0));
00060 }
00061 }
00062 }
00063 else throw cv::Exception(9004,"id invalid","createMarker",__FILE__,__LINE__);
00064
00065 if (addWaterMark){
00066 char idcad[30];
00067 sprintf(idcad,"#%d",id);
00068 float ax=float(size)/100.;
00069 cv::putText(marker,idcad,cv::Point( 0,marker.rows - marker.rows/40),FONT_HERSHEY_TRIPLEX,ax*0.15f,cv::Scalar::all(30));
00070 }
00071 return marker;
00072 }
00076 cv::Mat FiducidalMarkers::getMarkerMat(int id) throw (cv::Exception)
00077 {
00078 Mat marker(5,5, CV_8UC1);
00079 marker.setTo(Scalar(0));
00080 if (0<=id && id<1024) {
00081
00082 int ids[4]={0x10,0x17,0x09,0x0e};
00083 for (int y=0;y<5;y++) {
00084 int index=(id>>2*(4-y)) & 0x0003;
00085 int val=ids[index];
00086 for (int x=0;x<5;x++) {
00087 if ( ( val>>(4-x) ) & 0x0001 ) marker.at<uchar>(y,x)=1;
00088 else marker.at<uchar>(y,x)=0;
00089 }
00090 }
00091 }
00092 else throw cv::Exception (9189,"Invalid marker id","aruco::fiducidal::createMarkerMat",__FILE__,__LINE__);
00093 return marker;
00094 }
00095
00096
00097
00098
00099
00100
00101
00102 cv::Mat FiducidalMarkers::createBoardImage( Size gridSize,int MarkerSize,int MarkerDistance, BoardConfiguration& TInfo ,vector<int> *excludedIds) throw (cv::Exception)
00103 {
00104
00105
00106
00107 srand(cv::getTickCount());
00108 int nMarkers=gridSize.height*gridSize.width;
00109 TInfo.resize(nMarkers);
00110 vector<int> ids=getListOfValidMarkersIds_random(nMarkers,excludedIds);
00111 for (int i=0;i<nMarkers;i++)
00112 TInfo[i].id=ids[i];
00113
00114 int sizeY=gridSize.height*MarkerSize+(gridSize.height-1)*MarkerDistance;
00115 int sizeX=gridSize.width*MarkerSize+(gridSize.width-1)*MarkerDistance;
00116
00117 int centerX=sizeX/2;
00118 int centerY=sizeY/2;
00119
00120
00121 TInfo.mInfoType=BoardConfiguration::PIX;
00122 Mat tableImage(sizeY,sizeX,CV_8UC1);
00123 tableImage.setTo(Scalar(255));
00124 int idp=0;
00125 for (int y=0;y<gridSize.height;y++)
00126 for (int x=0;x<gridSize.width;x++,idp++) {
00127 Mat subrect(tableImage,Rect( x*(MarkerDistance+MarkerSize),y*(MarkerDistance+MarkerSize),MarkerSize,MarkerSize));
00128 Mat marker=createMarkerImage( TInfo[idp].id,MarkerSize);
00129
00130 TInfo[idp].resize(4);
00131 TInfo[idp][0]=cv::Point3f( x*(MarkerDistance+MarkerSize),y*(MarkerDistance+MarkerSize),0);
00132 TInfo[idp][1]=cv::Point3f( x*(MarkerDistance+MarkerSize)+MarkerSize,y*(MarkerDistance+MarkerSize),0);
00133 TInfo[idp][2]=cv::Point3f( x*(MarkerDistance+MarkerSize)+MarkerSize,y*(MarkerDistance+MarkerSize)+MarkerSize,0);
00134 TInfo[idp][3]=cv::Point3f( x*(MarkerDistance+MarkerSize),y*(MarkerDistance+MarkerSize)+MarkerSize,0);
00135 for (int i=0;i<4;i++) TInfo[idp][i]-=cv::Point3f(centerX,centerY,0);
00136 marker.copyTo(subrect);
00137 }
00138
00139 return tableImage;
00140 }
00141
00142
00143
00144
00145
00146
00147
00148 cv::Mat FiducidalMarkers::createBoardImage_ChessBoard( Size gridSize,int MarkerSize, BoardConfiguration& TInfo ,bool centerData ,vector<int> *excludedIds) throw (cv::Exception)
00149 {
00150
00151
00152 srand(cv::getTickCount());
00153
00154
00155 int nMarkers= 3*(gridSize.width*gridSize.height)/4;
00156 vector<int> idsVector=getListOfValidMarkersIds_random(nMarkers,excludedIds);
00157
00158
00159 int sizeY=gridSize.height*MarkerSize;
00160 int sizeX=gridSize.width*MarkerSize;
00161
00162 int centerX=sizeX/2;
00163 int centerY=sizeY/2;
00164
00165 Mat tableImage(sizeY,sizeX,CV_8UC1);
00166 tableImage.setTo(Scalar(255));
00167 TInfo.mInfoType=BoardConfiguration::PIX;
00168 int CurMarkerIdx=0;
00169 for (int y=0;y<gridSize.height;y++) {
00170
00171 bool toWrite;
00172 if (y%2==0) toWrite=false;
00173 else toWrite=true;
00174 for (int x=0;x<gridSize.width;x++) {
00175 toWrite=!toWrite;
00176 if (toWrite) {
00177 if (CurMarkerIdx>=idsVector.size()) throw cv::Exception(999," FiducidalMarkers::createBoardImage_ChessBoard","INTERNAL ERROR. REWRITE THIS!!",__FILE__,__LINE__);
00178 TInfo.push_back( MarkerInfo(idsVector[CurMarkerIdx++]));
00179
00180 Mat subrect(tableImage,Rect( x*MarkerSize,y*MarkerSize,MarkerSize,MarkerSize));
00181 Mat marker=createMarkerImage( TInfo.back().id,MarkerSize);
00182
00183 TInfo.back().resize(4);
00184 TInfo.back()[0]=cv::Point3f( x*(MarkerSize),y*(MarkerSize),0);
00185 TInfo.back()[1]=cv::Point3f( x*(MarkerSize)+MarkerSize,y*(MarkerSize),0);
00186 TInfo.back()[2]=cv::Point3f( x*(MarkerSize)+MarkerSize,y*(MarkerSize)+MarkerSize,0);
00187 TInfo.back()[3]=cv::Point3f( x*(MarkerSize),y*(MarkerSize)+MarkerSize,0);
00188 if (centerData) {
00189 for (int i=0;i<4;i++)
00190 TInfo.back()[i]-=cv::Point3f(centerX,centerY,0);
00191 }
00192 marker.copyTo(subrect);
00193 }
00194 }
00195 }
00196
00197 return tableImage;
00198 }
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208 cv::Mat FiducidalMarkers::createBoardImage_Frame( Size gridSize,int MarkerSize,int MarkerDistance, BoardConfiguration& TInfo ,bool centerData,vector<int> *excludedIds ) throw (cv::Exception)
00209 {
00210
00211
00212
00213 srand(cv::getTickCount());
00214 int nMarkers=2*gridSize.height*2*gridSize.width;
00215 vector<int> idsVector=getListOfValidMarkersIds_random(nMarkers,excludedIds);
00216
00217 int sizeY=gridSize.height*MarkerSize+MarkerDistance*(gridSize.height-1);
00218 int sizeX=gridSize.width*MarkerSize+MarkerDistance*(gridSize.width-1);
00219
00220 int centerX=sizeX/2;
00221 int centerY=sizeY/2;
00222
00223 Mat tableImage(sizeY,sizeX,CV_8UC1);
00224 tableImage.setTo(Scalar(255));
00225 TInfo.mInfoType=BoardConfiguration::PIX;
00226 int CurMarkerIdx=0;
00227 int mSize=MarkerSize+MarkerDistance;
00228 for (int y=0;y<gridSize.height;y++) {
00229 for (int x=0;x<gridSize.width;x++) {
00230 if (y==0 || y==gridSize.height-1 || x==0 || x==gridSize.width-1) {
00231 TInfo.push_back( MarkerInfo(idsVector[CurMarkerIdx++]));
00232 Mat subrect(tableImage,Rect( x*mSize,y*mSize,MarkerSize,MarkerSize));
00233 Mat marker=createMarkerImage( TInfo.back().id,MarkerSize);
00234 marker.copyTo(subrect);
00235
00236 TInfo.back().resize(4);
00237 TInfo.back()[0]=cv::Point3f( x*(mSize),y*(mSize),0);
00238 TInfo.back()[1]=cv::Point3f( x*(mSize)+MarkerSize,y*(mSize),0);
00239 TInfo.back()[2]=cv::Point3f( x*(mSize)+MarkerSize,y*(mSize)+MarkerSize,0);
00240 TInfo.back()[3]=cv::Point3f( x*(mSize),y*(mSize)+MarkerSize,0);
00241 if (centerData) {
00242 for (int i=0;i<4;i++)
00243 TInfo.back()[i]-=cv::Point3f(centerX,centerY,0);
00244 }
00245
00246 }
00247 }
00248 }
00249
00250 return tableImage;
00251 }
00252
00253
00254
00255
00256
00257
00258 Mat FiducidalMarkers::rotate(const Mat &in)
00259 {
00260 Mat out;
00261 in.copyTo(out);
00262 for (int i=0;i<in.rows;i++)
00263 {
00264 for (int j=0;j<in.cols;j++)
00265 {
00266 out.at<uchar>(i,j)=in.at<uchar>(in.cols-j-1,i);
00267 }
00268 }
00269 return out;
00270 }
00271
00272
00273
00274
00275
00276
00277
00278
00279 int FiducidalMarkers::hammDistMarker(Mat bits)
00280 {
00281 int ids[4][5]=
00282 {
00283 {
00284 1,0,0,0,0
00285 }
00286 ,
00287 {
00288 1,0,1,1,1
00289 }
00290 ,
00291 {
00292 0,1,0,0,1
00293 }
00294 ,
00295 {
00296 0, 1, 1, 1, 0
00297 }
00298 };
00299 int dist=0;
00300
00301 for (int y=0;y<5;y++)
00302 {
00303 int minSum=1e5;
00304
00305 for (int p=0;p<4;p++)
00306 {
00307 int sum=0;
00308
00309 for (int x=0;x<5;x++)
00310 sum+= bits.at<uchar>(y,x) == ids[p][x]?0:1;
00311 if (minSum>sum) minSum=sum;
00312 }
00313
00314 dist+=minSum;
00315 }
00316
00317 return dist;
00318 }
00319
00320
00321
00322
00323
00324
00325
00326 int FiducidalMarkers::analyzeMarkerImage(Mat &grey,int &nRotations)
00327 {
00328
00329
00330
00331
00332 int swidth=grey.rows/7;
00333 for (int y=0;y<7;y++)
00334 {
00335 int inc=6;
00336 if (y==0 || y==6) inc=1;
00337 for (int x=0;x<7;x+=inc)
00338 {
00339 int Xstart=(x)*(swidth);
00340 int Ystart=(y)*(swidth);
00341 Mat square=grey(Rect(Xstart,Ystart,swidth,swidth));
00342 int nZ=countNonZero(square);
00343 if (nZ> (swidth*swidth) /2) {
00344
00345 return -1;
00346 }
00347 }
00348 }
00349
00350
00351 vector<int> markerInfo(5);
00352 Mat _bits=Mat::zeros(5,5,CV_8UC1);
00353
00354
00355 for (int y=0;y<5;y++)
00356 {
00357
00358 for (int x=0;x<5;x++)
00359 {
00360 int Xstart=(x+1)*(swidth);
00361 int Ystart=(y+1)*(swidth);
00362 Mat square=grey(Rect(Xstart,Ystart,swidth,swidth));
00363 int nZ=countNonZero(square);
00364 if (nZ> (swidth*swidth) /2) _bits.at<uchar>( y,x)=1;
00365 }
00366 }
00367
00368
00369
00370 Mat _bitsFlip;
00371 Mat Rotations[4];
00372 Rotations[0]=_bits;
00373 int dists[4];
00374 dists[0]=hammDistMarker( Rotations[0]) ;
00375 pair<int,int> minDist( dists[0],0);
00376 for (int i=1;i<4;i++)
00377 {
00378
00379 Rotations[i]=rotate(Rotations[i-1]);
00380
00381 dists[i]=hammDistMarker( Rotations[i]) ;
00382 if (dists[i]<minDist.first)
00383 {
00384 minDist.first= dists[i];
00385 minDist.second=i;
00386 }
00387 }
00388
00389
00390
00391 nRotations=minDist.second;
00392 if (minDist.first!=0)
00393 return -1;
00394 else {
00395 int MatID=0;
00396 cv::Mat bits=Rotations [ minDist.second];
00397 for (int y=0;y<5;y++)
00398 {
00399 MatID<<=1;
00400 if ( bits.at<uchar>(y,1)) MatID|=1;
00401 MatID<<=1;
00402 if ( bits.at<uchar>(y,3)) MatID|=1;
00403 }
00404 return MatID;
00405 }
00406 }
00407
00408
00409
00410
00411
00412
00413
00414
00415 bool FiducidalMarkers::correctHammMarker(Mat &bits)
00416 {
00417
00418 bool errors[4];
00419 int ids[4][5]=
00420 {
00421 {
00422 0,0,0,0,0
00423 }
00424 ,
00425 {
00426 0,0,1,1,1
00427 }
00428 ,
00429 {
00430 1,1,0,0,1
00431 }
00432 ,
00433 {
00434 1, 1, 1, 1, 0
00435 }
00436 };
00437
00438 for (int y=0;y<5;y++)
00439 {
00440 int minSum=1e5;
00441
00442 for (int p=0;p<4;p++)
00443 {
00444 int sum=0;
00445
00446 for (int x=0;x<5;x++)
00447 sum+= bits.at<uchar>(y,x) == ids[p][x]?0:1;
00448 if (minSum>sum) minSum=sum;
00449 }
00450 if (minSum!=0) errors[y]=true;
00451 else errors[y]=false;
00452 }
00453
00454 return true;
00455 }
00456
00457
00458
00459
00460
00461
00462
00463 int FiducidalMarkers::detect(const Mat &in,int &nRotations)
00464 {
00465 assert(in.rows==in.cols);
00466 Mat grey;
00467 if ( in.type()==CV_8UC1) grey=in;
00468 else cv::cvtColor(in,grey,CV_BGR2GRAY);
00469
00470 threshold(grey, grey,125, 255, THRESH_BINARY|THRESH_OTSU);
00471
00472
00473
00474
00475 return analyzeMarkerImage(grey,nRotations);;
00476
00477
00478
00479
00480
00481
00482 }
00483
00484 vector<int> FiducidalMarkers::getListOfValidMarkersIds_random(int nMarkers,vector<int> *excluded) throw (cv::Exception)
00485 {
00486
00487 if (excluded!=NULL)
00488 if (nMarkers+excluded->size()>1024) throw cv::Exception(8888,"FiducidalMarkers::getListOfValidMarkersIds_random","Number of possible markers is exceeded",__FILE__,__LINE__);
00489
00490 vector<int> listOfMarkers(1024);
00491
00492 for (int i=0;i<1024;i++) listOfMarkers[i]=i;
00493
00494 if (excluded!=NULL)
00495 for (size_t i=0;i<excluded->size();i++)
00496 listOfMarkers[excluded->at(i)]=-1;
00497
00498 random_shuffle(listOfMarkers.begin(),listOfMarkers.end());
00499
00500 int i=0;
00501 vector<int> retList;
00502 while (retList.size()<nMarkers) {
00503 if (listOfMarkers[i]!=-1)
00504 retList.push_back(listOfMarkers[i]);
00505 i++;
00506 }
00507 return retList;
00508 }
00509
00510 }
00511