Skip to content
Snippets Groups Projects
bstracker.cpp 9.76 KiB
Newer Older
even's avatar
even committed
#include "bstracker.h"
#include "blurredsegmentproto.h"



even's avatar
even committed
const int BSTracker::DEFAULT_FAST_TRACK_MAX_WIDTH = 5;
const int BSTracker::DEFAULT_FINE_TRACK_MAX_WIDTH = 8;
even's avatar
even committed
const int BSTracker::DEFAULT_ACCEPTED_LACKS = 5;
even's avatar
even committed
const int BSTracker::NO_VICINITY = 10000;
const int BSTracker::DEFAULT_VICINITY_THRESHOLD = 10;
even's avatar
even committed
const int BSTracker::MIN_SCAN = 8;
const int BSTracker::DEFAULT_MAX_SCAN = 32;
const int BSTracker::DEFAULT_FITTING_DELAY = 20;

const int BSTracker::DEFAULT_THINNING_DELAY = 20;
const int BSTracker::DEFAULT_THINNING_SPEED = 2;
const int BSTracker::DEFAULT_THINNING_REACH = 50;
const int BSTracker::DEFAULT_THINNING_RESOLUTION = 1000;

const int BSTracker::FAILURE_NO_START = 1;
const int BSTracker::FAILURE_IMAGE_BOUND_ON_RIGHT = 2;
const int BSTracker::FAILURE_IMAGE_BOUND_ON_LEFT = 4;
const int BSTracker::FAILURE_LOST_ORIENTATION = 32;



BSTracker::BSTracker ()
{
even's avatar
even committed
  vicinityThreshold = NO_VICINITY + DEFAULT_VICINITY_THRESHOLD;
even's avatar
even committed
  imaxWidth = DEFAULT_FAST_TRACK_MAX_WIDTH;
  fmaxWidth = DEFAULT_FINE_TRACK_MAX_WIDTH;
even's avatar
even committed
  acceptedLacks = DEFAULT_ACCEPTED_LACKS;
  minRestart = acceptedLacks;

  dynamicScans = true;
  fittingDelay = DEFAULT_FITTING_DELAY;
  recordScans = false;
  orthoScan = false;

  thinningOn = true;
  thinningDelay = DEFAULT_THINNING_DELAY;
  thinningSpeed.set (DEFAULT_THINNING_SPEED, 100);
  thinningReach.set (100 + DEFAULT_THINNING_REACH, 100);
  gMap = NULL;

  maxScan = DEFAULT_MAX_SCAN;

  fail = 0;

  cand = new int[1]; // to avoid systematic tests
  lscan = 0;
  rscan = 0;
}


BSTracker::~BSTracker ()
{
  delete cand;
}


void BSTracker::clear ()
{
  scanBound1.clear ();
  scanBound2.clear ();
  scanLine.clear ();
}


void BSTracker::setGradientMap (VMap *data)
{
  gMap = data;
even's avatar
even committed
  scanp.setSize (gMap->getWidth (), gMap->getHeight ());
even's avatar
even committed
  delete cand;
  cand = new int[data->getHeightWidthMax ()];
}


BlurredSegment *BSTracker::fastTrack (const Pt2i &p1, const Pt2i &p2,
                                      Pt2i *p0)
{
  // Creates the scanner
  DirectionalScanner *ds = NULL;
  if (p0 != NULL)
even's avatar
even committed
    ds = scanp.getScanner (*p0, p1.vectorTo (p2), 4 * imaxWidth, false);
  else ds = scanp.getScanner (p1, p2);
even's avatar
even committed
  if (ds == NULL) return NULL;

  // Builds a BS builder around a central point
  vector<Pt2i> pix;
  if (ds->first (pix) < MIN_SCAN)
  {
    delete ds;
    return NULL;
  }

  if (recordScans)
  {
    scanBound1.push_back (pix.front ());
    scanBound2.push_back (pix.back ());
    scanLine.push_back (pix);
  }
  int candide;
  Pt2i pfirst;
  if (p0 != NULL) pfirst.set (p0->x (), p0->y ());
  else
  {
    candide = gMap->largestIn (pix);
    if (candide == -1)
    {
      delete ds;
      return NULL;
    }
    pfirst.set (pix.at (candide));
  }

even's avatar
even committed
  BlurredSegmentProto bs (imaxWidth, pfirst);
even's avatar
even committed
  Pt2i lastLeft (pfirst);
  Pt2i lastRight (pfirst);
  
  // Extends the segment
  int lstop = 0;
  int rstop = 0;
  int lstart = 0;
  int rstart = 0;
  bool added = false;
  bool scanningRight = true;
  bool scanningLeft = true;
  int fsCount = maxScan;

  while ((scanningRight || scanningLeft) && (fsCount--))
  {
    // Extends on right
    if (scanningRight)
    {
      if (ds->nextOnRight (pix) < MIN_SCAN) scanningRight = false;
      else
      {
        if (recordScans)
        {
          scanBound1.push_back (pix.front ());
          scanBound2.push_back (pix.back ());
          scanLine.push_back (pix);
        }
        added = false;
        candide = gMap->largestIn (pix);
        if (candide != -1)
        {
even's avatar
even committed
          if (lastRight.manhattan (pix.at (candide)) <= vicinityThreshold)
even's avatar
even committed
            added = bs.addRight (pix.at (candide));
        }
        if (added)
        {
          lastRight = pix.at (candide);
          if (rstop != 0) rstop = 0;
        }
        else if (++rstop > acceptedLacks)
          scanningRight = false;
      }
    }
    // Extends on left
    if (scanningLeft)
    {
      if (ds->nextOnLeft (pix) < MIN_SCAN) scanningLeft = false;
      else
      {
        if (recordScans)
        {
          scanBound1.push_back (pix.front ());
          scanBound2.push_back (pix.back ());
          scanLine.push_back (pix);
        }
        added = false;
        candide = gMap->largestIn (pix);
        if (candide != -1)
        {
even's avatar
even committed
          if (lastLeft.manhattan (pix.at (candide)) < vicinityThreshold)
even's avatar
even committed
            added = bs.addLeft (pix.at (candide));
        }
        if (added)
        {
          lastLeft = pix.at (candide);
          if (lstop != 0) lstop = 0;
        }
        else if (++lstop > acceptedLacks)
          scanningLeft = false;
      }
    }
  }
  if (rstart) bs.removeRight (rstart);
  if (lstart) bs.removeLeft (lstart);
  delete ds;
  return (bs.endOfBirth ());
}



BlurredSegment *BSTracker::fineTrack (const Pt2i &center, const Vr2i &scandir,
even's avatar
even committed
                                      int scanwidth, int bswidth,
even's avatar
even committed
                                      const Vr2i &gref)
{
even's avatar
even committed
  // Checks scan width minimal size
  if (scanwidth < MIN_SCAN) scanwidth = MIN_SCAN;

even's avatar
even committed
  // Gets detected segment normal vector
  Vr2i normal = scandir.orthog ();
  if (! normal.orientedAs (gref)) normal.invert ();

  fail = 0;

  // Creation of the directional scanner and the array of candidates
even's avatar
even committed
  DirectionalScanner *ds = scanp.getScanner (center, normal,
                                             scanwidth, dynamicScans);
even's avatar
even committed
  if (ds == NULL)
  {
    fail = FAILURE_NO_START;
    return NULL;
  }

  // Looking for a central point
  vector<Pt2i> pix;
  if (ds->first (pix) < MIN_SCAN)
  {
    delete ds;
    fail = FAILURE_NO_START;
    return NULL;
  }
  if (recordScans)
  {
    scanBound1.push_back (pix.front ());
    scanBound2.push_back (pix.back ());
    scanLine.push_back (pix);
  }
  int nbc = gMap->localMax (cand, pix, normal);
  if (nbc == 0)
  {
    delete ds;
    fail = FAILURE_NO_START;
    return NULL;
  }

  BlurredSegmentProto bs (bswidth, pix[cand[0]]);

  // Handles thinning
  int count = 0;
  AbsRat maxw (bswidth * DEFAULT_THINNING_RESOLUTION,
               DEFAULT_THINNING_RESOLUTION);

  // Extends the segment
  lscan = 0;
  rscan = 0;
  int lstop = 0;
  int rstop = 0;
  int lstart = 0;
  int rstart = 0;
  bool added = false;
  bool scanningRight = true;
  bool scanningLeft = true;
  bool thon = thinningOn;

  while (scanningRight || scanningLeft)
  {
    count ++;

    // Handles thinning
    if (thon)
    {
      if (count > thinningDelay)
      {
        AbsRat sw = bs.segmentRationalWidth ();
        AbsRat oldmaxw (maxw);
        maxw.attractsTo (sw, thinningSpeed);
        sw.mult (thinningReach);
        if (maxw.lessThan (sw))
        {
          maxw.sticksTo (sw);
          if (oldmaxw.lessThan (maxw)) maxw.set (oldmaxw);
          thon = false;  // thinning deactivation
        }
        bs.setMaxWidth (maxw);
      }
    }

    // Resets the scan stripe
    if (dynamicScans && count > fittingDelay && bs.getLine () != NULL)
    {
      // Stops the detection if the segment gets crosswise
      if (count == fittingDelay + 1)
      {
        Vr2i dirn = bs.getSupportVector ();
        if (4 * dirn.squaredScalarProduct (scandir)
            < dirn.norm2 () * scandir.norm2 ())
        {
          scanningLeft = false;
          scanningRight = false;
          fail += FAILURE_LOST_ORIENTATION;
        }
      }
      int ppa, ppb, ppc;
      bs.getLine()->getMedialAxis (ppa, ppb, ppc);
      ds->bindTo (ppa, ppb, ppc);
    }

    // Extends on right
    if (scanningRight)
    {
      added = false;
      if (ds->nextOnRight (pix) < MIN_SCAN)
      {
        fail += FAILURE_IMAGE_BOUND_ON_RIGHT;
        scanningRight = false;
      }
      else
      {
        if (recordScans)
        {
          scanBound1.push_back (pix.front ());
          scanBound2.push_back (pix.back ());
          scanLine.push_back (pix);
        }
        added = false;
        nbc = gMap->localMax (cand, pix, normal);
        for (int i = 0; ! added && i < nbc; i++)
          added = bs.addRight (pix[cand[i]]);
        if (added)
        {
          rscan = count;
          if (rstop == 0) rstart = 0;
          else
          {
            rstart ++;
            //if (rstart >= minRestart || rstart >= rstop)
            if (rstart >= rstop)
            {
              rstop = 0;
              rstart = 0;
            }
          }
        }
        else
        {
          if (++rstop - rstart > acceptedLacks)
          {
            if (bs.size () <= 3) fail = FAILURE_NO_START;
            scanningRight = false;
          }
        }
      }
    }

    // Extends on left
    if (scanningLeft)
    {
      if (ds->nextOnLeft (pix) < MIN_SCAN)
      {
        fail += FAILURE_IMAGE_BOUND_ON_LEFT;
        scanningLeft = false;
      }
      else
      {
        if (recordScans)
        {
          scanBound1.push_back (pix.front ());
          scanBound2.push_back (pix.back ());
          scanLine.push_back (pix);
        }
        added = false;
        nbc = gMap->localMax (cand, pix, normal);
        for (int i = 0; ! added && i < nbc; i++)
          added = bs.addLeft (pix[cand[i]]);
        if (added)
        {
          lscan = count;
          if (lstop == 0) lstart = 0;
          else
          {
            lstart ++;
            //if (lstart >= minRestart || lstart >= lstop)
            if (lstart >= lstop)
            {
              lstop = 0;
              lstart = 0;
            }
          }
        }
        else
        {
          if (++lstop - lstart > acceptedLacks)
          {
            if (bs.size () <= 3) fail = FAILURE_NO_START;
            scanningLeft = false;
          }
        }
      }
    }
  }
  if (rstart) bs.removeRight (rstart);
  if (lstart) bs.removeLeft (lstart);
  delete ds;
  return (bs.endOfBirth ());
}


void BSTracker::switchOrthoScans ()
{
  orthoScan = ! orthoScan;
  scanp.setOrtho (orthoScan);
}


void BSTracker::switchScanExtent ()
{
  maxScan = (maxScan == gMap->getHeightWidthMax () ?
             DEFAULT_MAX_SCAN : gMap->getHeightWidthMax ());
}