00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include <aruco/highlyreliablemarkers.h>
00030
00031 namespace aruco {
00032
00033
00034 Dictionary HighlyReliableMarkers::_D;
00035 HighlyReliableMarkers::BalancedBinaryTree HighlyReliableMarkers::_binaryTree;
00036 unsigned int HighlyReliableMarkers::_n, HighlyReliableMarkers::_ncellsBorder, HighlyReliableMarkers::_correctionDistance;
00037 int HighlyReliableMarkers::_swidth;
00038
00039
00042 MarkerCode::MarkerCode(unsigned int n) {
00043
00044 for(unsigned int i=0; i<4; i++) {
00045 _bits[i].resize(n*n);
00046 for(unsigned int j=0; j<_bits[i].size(); j++) _bits[i][j]=0;
00047 _ids[i] = 0;
00048 }
00049 _n = n;
00050 };
00051
00052
00055 MarkerCode::MarkerCode(const MarkerCode& MC)
00056 {
00057 for(unsigned int i=0; i<4; i++) {
00058 _bits[i] = MC._bits[i];
00059 _ids[i] = MC._ids[i];
00060 }
00061 _n = MC._n;
00062 }
00063
00064
00065
00068 void MarkerCode::set(unsigned int pos, bool val) {
00069
00070 if( get(pos) != val ) {
00071 for(unsigned int i=0; i<4; i++) {
00072 unsigned int y=pos/n(), x=pos%n();
00073
00074 if(i==1) { unsigned int aux=y; y=x; x=n()-aux-1; }
00075 else if(i==2) { y=n()-y-1; x=n()-x-1; }
00076 else if(i==3) { unsigned int aux=y; y=n()-x-1; x=aux; }
00077 unsigned int rotPos = y*n()+x;
00078 _bits[i][rotPos] = val;
00079
00080 if(val==true) _ids[i] += (unsigned int)pow(float(2),float(rotPos));
00081 else _ids[i] -= (unsigned int)pow(float(2),float(rotPos));
00082 }
00083 }
00084 }
00085
00086
00089 unsigned int MarkerCode::selfDistance(unsigned int &minRot) {
00090 unsigned int res = _bits[0].size();
00091 for(unsigned int i=1; i<4; i++) {
00092 unsigned int hammdist = hammingDistance(_bits[0], _bits[i]);
00093 if(hammdist<res) {
00094 minRot = i;
00095 res = hammdist;
00096 }
00097 }
00098 return res;
00099 }
00100
00101
00104 unsigned int MarkerCode::distance(MarkerCode m, unsigned int &minRot) {
00105 unsigned int res = _bits[0].size();
00106 for(unsigned int i=0; i<4; i++) {
00107 unsigned int hammdist = hammingDistance(_bits[0], m.getRotation(i));
00108 if(hammdist<res) {
00109 minRot = i;
00110 res = hammdist;
00111 }
00112 }
00113 return res;
00114 };
00115
00116
00119 void MarkerCode::fromString(std::string s)
00120 {
00121 for(unsigned int i=0; i<s.length(); i++) {
00122 if(s[i]=='0') set(i, false);
00123 else set(i, true);
00124 }
00125 }
00126
00129 std::string MarkerCode::toString()
00130 {
00131 std::string s;
00132 s.resize(size());
00133 for(unsigned int i=0; i<size(); i++) {
00134 if(get(i)) s[i]='1';
00135 else s[i]='0';
00136 }
00137 return s;
00138 }
00139
00140
00143 cv::Mat MarkerCode::getImg(unsigned int pixSize) {
00144 const unsigned int borderSize=1;
00145 unsigned int nrows = n()+2*borderSize;
00146 if(pixSize%nrows != 0) pixSize = pixSize + nrows - pixSize%nrows;
00147 unsigned int cellSize = pixSize / nrows;
00148 cv::Mat img(pixSize, pixSize, CV_8U, cv::Scalar::all(0));
00149
00150 for(unsigned int i=0; i<n(); i++) {
00151 for(unsigned int j=0; j<n(); j++) {
00152 if(_bits[0][i*n()+j]!=0) {
00153
00154 for(unsigned int k=0; k<cellSize; k++) {
00155 for(unsigned int l=0; l<cellSize; l++) {
00156 img.at<uchar>((i+borderSize)*cellSize+k, (j+borderSize)*cellSize+l) = 255;
00157 }
00158 }
00159 }
00160 }
00161 }
00162 return img;
00163 }
00164
00165
00168 unsigned int MarkerCode::hammingDistance(std::vector<bool> m1, std::vector<bool> m2) {
00169 unsigned int res=0;
00170 for(unsigned int i=0; i<m1.size(); i++)
00171 if(m1[i]!=m2[i]) res++;
00172 return res;
00173 };
00174
00175
00176
00177
00178
00179
00182 bool Dictionary::fromFile(std::string filename) {
00183 cv::FileStorage fs(filename, cv::FileStorage::READ);
00184 int nmarkers, markersize;
00185
00186
00187 fs["nmarkers"] >> nmarkers;
00188 fs["markersize"] >> markersize;
00189
00190
00191 for (int i=0; i<nmarkers; i++) {
00192 std::string s;
00193 fs["marker_" + toStr(i)] >> s;
00194 MarkerCode m(markersize);
00195 m.fromString(s);
00196 push_back(m);
00197 }
00198 fs.release();
00199 return true;
00200 };
00201
00204 bool Dictionary::toFile(std::string filename) {
00205 cv::FileStorage fs(filename, cv::FileStorage::WRITE);
00206
00207 fs << "nmarkers" << (int)size();
00208 fs << "markersize" << (int)( (*this)[0].n() );
00209
00210 for (unsigned int i=0; i<size(); i++) {
00211 std::string s = ((*this)[i]).toString();
00212 fs << "marker_" + toStr(i) << s;
00213 }
00214 fs.release();
00215 return true;
00216 };
00217
00220 unsigned int Dictionary::distance(MarkerCode m, unsigned int &minMarker, unsigned int &minRot) {
00221 unsigned int res = m.size();
00222 for(unsigned int i=0; i<size(); i++) {
00223 unsigned int minRotAux;
00224 unsigned int distance = (*this)[i].distance(m, minRotAux);
00225 if(distance<res) {
00226 minMarker = i;
00227 minRot = minRotAux;
00228 res=distance;
00229 }
00230 }
00231 return res;
00232 }
00233
00234
00237 unsigned int Dictionary::minimunDistance()
00238 {
00239 if(size()==0) return 0;
00240 unsigned int minDist = (*this)[0].size();
00241
00242 for(unsigned int i=0; i<size(); i++) {
00243
00244 minDist = std::min( minDist, (*this)[i].selfDistance() );
00245
00246
00247 for(unsigned int j=i+1; j<size(); j++) {
00248 minDist = std::min( minDist, (*this)[i].distance((*this)[j]) );
00249 }
00250 }
00251 return minDist;
00252 }
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00265 bool HighlyReliableMarkers::loadDictionary(Dictionary D){
00266 if(D.size()==0) return false;
00267 _D = D;
00268 _n = _D[0].n();
00269 _ncellsBorder = (_D[0].n()+2);
00270 _correctionDistance = (unsigned int)floor( (_D.minimunDistance()-1)/2. );
00271
00272 _binaryTree.loadDictionary(&D);
00273
00274 return true;
00275
00276 };
00277
00278 bool HighlyReliableMarkers::loadDictionary(std::string filename){
00279 Dictionary D;
00280 D.fromFile(filename);
00281 return loadDictionary(D);
00282 };
00283
00284
00287 int HighlyReliableMarkers::detect(const cv::Mat& in, int& nRotations)
00288 {
00289
00290 assert(in.rows==in.cols);
00291 cv::Mat grey;
00292 if ( in.type()==CV_8UC1) grey=in;
00293 else cv::cvtColor(in,grey,CV_BGR2GRAY);
00294
00295 cv::threshold(grey, grey,125, 255, cv::THRESH_BINARY|cv::THRESH_OTSU);
00296 _swidth=grey.rows/_ncellsBorder;
00297
00298
00299
00300
00301
00302 MarkerCode candidate = getMarkerCode(grey);
00303
00304
00305 unsigned int orgPos;
00306 for(unsigned int i=0; i<4; i++) {
00307 if(_binaryTree.findId( candidate.getId(i), orgPos )) {
00308 nRotations = i;
00309 return candidate.getId(i);
00310
00311 }
00312 }
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325 unsigned int minMarker, minRot;
00326 if(_D.distance(candidate, minMarker, minRot) <= _correctionDistance) {
00327 nRotations = minRot;
00328
00329 return _D[minMarker].getId();
00330 }
00331
00332 return -1;
00333
00334 }
00335
00336
00339 bool HighlyReliableMarkers::checkBorders(cv::Mat grey) {
00340 for (int y=0;y<_ncellsBorder;y++)
00341 {
00342 int inc=_ncellsBorder-1;
00343 if (y==0 || y==_ncellsBorder-1) inc=1;
00344 for (int x=0;x<_ncellsBorder;x+=inc)
00345 {
00346 int Xstart=(x)*(_swidth);
00347 int Ystart=(y)*(_swidth);
00348 cv::Mat square=grey(cv::Rect(Xstart,Ystart,_swidth,_swidth));
00349 int nZ=cv::countNonZero(square);
00350 if (nZ> (_swidth*_swidth) /2) {
00351 return false;
00352 }
00353 }
00354 }
00355 return true;
00356 }
00357
00360 MarkerCode HighlyReliableMarkers::getMarkerCode(cv::Mat grey) {
00361 MarkerCode candidate( _n );
00362 for (int y=0;y<_n;y++)
00363 {
00364 for (int x=0;x<_n;x++)
00365 {
00366 int Xstart=(x+1)*(_swidth);
00367 int Ystart=(y+1)*(_swidth);
00368 cv::Mat square=grey(cv::Rect(Xstart,Ystart,_swidth,_swidth));
00369 int nZ=countNonZero(square);
00370 if (nZ> (_swidth*_swidth) /2) candidate.set(y*_n+x, 1);
00371 }
00372 }
00373 return candidate;
00374 };
00375
00376
00377
00380 void HighlyReliableMarkers::BalancedBinaryTree::loadDictionary(Dictionary *D) {
00381
00382 _orderD.clear();
00383 for(unsigned int i=0; i<D->size(); i++) {
00384 _orderD.push_back( std::pair<unsigned int,unsigned int>( (*D)[i].getId() ,i) );
00385 }
00386 std::sort(_orderD.begin(), _orderD.end());
00387
00388
00389 unsigned int levels=0;
00390 while( pow(float(2),float(levels)) <= _orderD.size() ) levels++;
00391
00392
00393
00394 std::vector<bool> visited;
00395 visited.resize(_orderD.size());
00396 for(unsigned int i=0; i<_orderD.size(); i++) visited[i]=false;
00397
00398
00399 unsigned int rootIdx = _orderD.size()/2;
00400 visited[rootIdx] = true;
00401
00402
00403 std::vector< std::pair<unsigned int, unsigned int> > intervals;
00404
00405 intervals.push_back( std::pair<unsigned int, unsigned int>(0,rootIdx) );
00406 intervals.push_back( std::pair<unsigned int, unsigned int>(rootIdx,_orderD.size()) );
00407
00408
00409 _binaryTree.clear();
00410 _binaryTree.resize(_orderD.size());
00411
00412
00413 if(!visited[(0+rootIdx)/2]) _binaryTree[rootIdx].first = (0+rootIdx)/2;
00414 else _binaryTree[rootIdx].first = -1;
00415 if(!visited[(rootIdx+_orderD.size())/2]) _binaryTree[rootIdx].second = (rootIdx+_orderD.size())/2;
00416 else _binaryTree[rootIdx].second = -1;
00417
00418
00419 for(unsigned int i=1; i<levels; i++) {
00420 unsigned int nintervals = intervals.size();
00421 for(unsigned int j=0; j<nintervals; j++) {
00422
00423 unsigned int lowerBound, higherBound;
00424 lowerBound = intervals.back().first;
00425 higherBound = intervals.back().second;
00426 intervals.pop_back();
00427
00428
00429 unsigned int center = (higherBound + lowerBound)/2;
00430
00431
00432 if(!visited[center]) visited[center]=true;
00433 else continue;
00434
00435
00436 unsigned int lowerChild = (lowerBound + center)/2;
00437 unsigned int higherChild = (center + higherBound)/2;
00438
00439
00440 if(!visited[lowerChild]) {
00441 intervals.insert( intervals.begin(), std::pair<unsigned int, unsigned int>(lowerBound, center) );
00442 _binaryTree[center].first = lowerChild;
00443 }
00444 else _binaryTree[center].first = -1;
00445
00446
00447 if(!visited[higherChild]) {
00448 intervals.insert( intervals.begin(), std::pair<unsigned int, unsigned int>(center, higherBound) );
00449 _binaryTree[center].second = higherChild;
00450 }
00451 else _binaryTree[center].second = -1;
00452
00453 }
00454 }
00455
00456
00457
00458
00459
00460 };
00461
00462
00465 bool HighlyReliableMarkers::BalancedBinaryTree::findId(unsigned int id, unsigned int &orgPos) {
00466 unsigned int pos = _root;
00467 while(pos!=-1) {
00468 unsigned int posId = _orderD[pos].first;
00469 if(posId == id ) {
00470 orgPos = _orderD[pos].second;
00471 return true;
00472 }
00473 else if(posId < id) pos = _binaryTree[pos].second;
00474 else pos = _binaryTree[pos].first;
00475 }
00476 return false;
00477 }
00478
00479
00480 };