pal-robotics / aruco_ros

Software package and ROS wrappers of the Aruco Augmented Reality marker detector library
MIT License
448 stars 306 forks source link

Detect markers with N bits resolution #56

Open GuillaumeHauss opened 6 years ago

GuillaumeHauss commented 6 years ago

Hi all,

As mentioned in my previous issue, I'm looking to adapt the Aruco Detector to make it able to detect and identify markers with N-bits resolution, N in [4; 7]. So far, the bit resolution is hardcoded in the function detect in the file arucofidmarkers.cpp, as shown in the following snippet of code

int FiducidalMarkers::analyzeMarkerImage(Mat &grey,int &nRotations)
  {

    //Markers  are divided in 7x7 regions, of which the inner 5x5 belongs to marker info
    //the external border shoould be entirely black

    int swidth=grey.rows/7;
    for (int y=0;y<7;y++)
    {
      int inc=6;
      if (y==0 || y==6) inc=1;//for first and last row, check the whole border
      for (int x=0;x<7;x+=inc)
      {
        int Xstart=(x)*(swidth);
        int Ystart=(y)*(swidth);
        Mat square=grey(Rect(Xstart,Ystart,swidth,swidth));
        int nZ=countNonZero(square);
        if (nZ> (swidth*swidth) /2) {
          //        cout<<"neb"<<endl;
          return -1;//can not be a marker because the border element is not black!
        }
      }
    }

    //now,
    vector<int> markerInfo(5);
    Mat _bits=Mat::zeros(5,5,CV_8UC1);
    //get information(for each inner square, determine if it is  black or white)

    for (int y=0;y<5;y++)
    {

      for (int x=0;x<5;x++)
      {
        int Xstart=(x+1)*(swidth);
        int Ystart=(y+1)*(swidth);
        Mat square=grey(Rect(Xstart,Ystart,swidth,swidth));
        int nZ=countNonZero(square);
        if (nZ> (swidth*swidth) /2)  _bits.at<uchar>( y,x)=1;
      }
    }
    //      printMat<uchar>( _bits,"or mat");

    //checkl all possible rotations
    Mat _bitsFlip;
    Mat Rotations[4];
    Rotations[0]=_bits;
    int dists[4];
    dists[0]=hammDistMarker( Rotations[0]) ;
    pair<int,int> minDist( dists[0],0);
    for (int i=1;i<4;i++)
    {
      //rotate
      Rotations[i]=rotate(Rotations[i-1]);
      //get the hamming distance to the nearest possible word
      dists[i]=hammDistMarker( Rotations[i]) ;
      if (dists[i]<minDist.first)
      {
        minDist.first=  dists[i];
        minDist.second=i;
      }
    }
    //              printMat<uchar>( Rotations [ minDist.second]);
    //          cout<<"MinDist="<<minDist.first<<" "<<minDist.second<<endl;

    nRotations=minDist.second;
    if (minDist.first!=0)    //FUTURE WORK: correct if any error
      return -1;
    else {//Get id of the marker
      int MatID=0;
      cv::Mat bits=Rotations [ minDist.second];
      for (int y=0;y<5;y++)
      {
        MatID<<=1;
        if ( bits.at<uchar>(y,1)) MatID|=1;
        MatID<<=1;
        if ( bits.at<uchar>(y,3)) MatID|=1;
      }
      return MatID;
    }
  }

My initial guess was to change every 7 in N, every 6 in N-1 and every 5 in N-2, after adding N as a parameter of the function. But this wasn't working. Does anyone has a better approach in mind?

I'll keep digging and keep you posted if I find a workaround :)

Thank you very much for your help

GuillaumeHauss commented 6 years ago

Well, it seems that this function is handling the major magic step to get the ID of the marker

int FiducidalMarkers::hammDistMarker(Mat  bits)

in the same arucodifmarker.cpp file. Now, there is some kind of mask set in the function, that has explicitly 5 columns, used in the computation of the Hammind distance. After some digging in the opencv library, I found this file which houses the function

int normHamming(const uchar* a, int n, int cellSize)

It also uses some predefined tables, but I cannot understand their origin. Do someone has a thought on that?