Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

MapBuilder.cc

Go to the documentation of this file.
00001 //-*-c++-*-
00002 #include "Events/EventRouter.h"
00003 #include "Events/TextMsgEvent.h"
00004 #include "Events/LookoutEvents.h"
00005 #include "Motion/HeadPointerMC.h"
00006 #include "Motion/Kinematics.h"
00007 #include "Shared/mathutils.h"
00008 #include "Shared/Measures.h"
00009 #include "Shared/Config.h"
00010 #include "Shared/MarkScope.h"
00011 
00012 #include "DualCoding/ShapeRoot.h"
00013 #include "DualCoding/ShapeLine.h"
00014 #include "DualCoding/ShapeEllipse.h"
00015 #include "DualCoding/ShapeBlob.h"
00016 #include "DualCoding/ShapeCylinder.h"
00017 #include "DualCoding/ShapePolygon.h"
00018 #include "DualCoding/ShapeSphere.h"
00019 #include "DualCoding/ShapeTarget.h"
00020 #include "DualCoding/ShapeMarker.h"
00021 #include "DualCoding/ShapeSift.h"
00022 #include "DualCoding/ShapeAprilTag.h"
00023 #include "DualCoding/Sketch.h"    // for NEW_SKETCH
00024 #include "DualCoding/visops.h"
00025 #include "DualCoding/VRmixin.h"
00026 
00027 #include "Crew/LookoutRequests.h"
00028 #include "Crew/Lookout.h"
00029 #include "Crew/MapBuilder.h"
00030 
00031 #include "Vision/SIFT/API/SiftTekkotsu.h"
00032 #include "Vision/AprilTags/TagDetector.h"
00033 #include "Vision/AprilTags/TagDetection.h"
00034 
00035 using namespace std;
00036 
00037 namespace DualCoding {
00038 
00039 inline float distSq(const fmat::Column<4>& vec) {
00040   return vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2];
00041 }
00042 
00043 MapBuilder::MapBuilder() : 
00044   BehaviorBase("MapBuilder"),
00045   camSkS(VRmixin::getCamSkS()), camShS(camSkS.getDualSpace()),
00046   groundShS(VRmixin::getGroundShS()),
00047   localSkS(VRmixin::getLocalSkS()), localShS(localSkS.getDualSpace()),
00048   worldSkS(VRmixin::getWorldSkS()), worldShS(worldSkS.getDualSpace()),
00049   xres(camSkS.getWidth()), yres(camSkS.getHeight()), 
00050   ground_plane(),
00051   theAgent(VRmixin::theAgent),
00052   localToWorldMatrix(fmat::Transform::identity()),
00053   worldToLocalMatrix(fmat::Transform::identity()), 
00054   badGazePoints(), 
00055   requests(), curReq(NULL), idCounter(0), maxDistSq(0), siftMatchers(),
00056   pointAtID(Lookout::invalid_LO_ID), scanID(Lookout::invalid_LO_ID),
00057   nextGazePoint() {}
00058 
00059 void MapBuilder::preStart() {
00060   BehaviorBase::preStart();
00061   if ( verbosity & MBVstart )
00062     cout << "MapBuilder::start()\n";
00063 
00064   camSkS.clear(); camShS.clear();
00065   groundShS.clear();
00066   localSkS.clear(); localShS.clear();
00067   worldSkS.clear(); worldShS.clear();
00068   badGazePoints.clear();
00069 
00070   erouter->addListener(this,EventBase::textmsgEGID);  // listen for commands to move to next gaze point
00071   erouter->addListener(this,EventBase::lookoutEGID);  // listen for Lookout completion events
00072 }
00073 
00074 bool MapBuilder::retain = true;
00075 
00076 MapBuilder::MapBuilderVerbosity_t MapBuilder::verbosity = (-1U & ~(MBVevents | MBVcomplete | MBVgroundPlane | MBVskipShape));
00077 
00078 /*
00079   Since MapBuilder is constructed as static from VRmixin, the
00080   destructor doesn't get called until the robot shuts down.  We have
00081   to do everything assumed to be done in destructor in doStop, such as
00082   clearing the request queue and setting parameters to the initial
00083   values as set in the constructor.
00084 */
00085 void MapBuilder::stop() {
00086   cout << "MapBuilder::stop()\n";
00087   while(!requests.empty()) {
00088     delete requests.front();
00089     requests.pop();
00090   }
00091   curReq = NULL;
00092   BehaviorBase::stop();
00093 }
00094 
00095 void MapBuilder::executeRequest(BehaviorBase *requestingBehavior, const MapBuilderRequest &req) {
00096   MapBuilderRequest newreq(req);
00097   newreq.requestingBehavior = requestingBehavior;
00098   executeRequest(newreq);
00099 }
00100 
00101 unsigned int MapBuilder::executeRequest(const MapBuilderRequest& req, unsigned int *req_id) {
00102   MapBuilderRequest *newreq = new MapBuilderRequest(req);
00103   if ( !newreq->validateRequest() ) {
00104     cout << "*** Request rejected by MapBuilder" << endl;
00105     return 0;
00106   }
00107   const unsigned int this_request  = ++idCounter;
00108   newreq->requestID = this_request;
00109   if ( req_id != NULL ) *req_id = this_request;
00110   requests.push(newreq);
00111   executeRequest();
00112   return this_request;
00113 }
00114 
00115 void MapBuilder::executeRequest() {
00116   if ( curReq != NULL || requests.empty() ) return;
00117   curReq = requests.front();
00118   curReq->verbosity = (verbosity & ~curReq->clearVerbosity) | curReq->setVerbosity;
00119   if ( curReq->verbosity & MBVexecute )
00120     cout << "MapBuilder::executeRequest: execute " << curReq->requestID << endl;
00121   erouter->postEvent(EventBase::mapbuilderEGID, curReq->requestID, EventBase::activateETID,0);  
00122 
00123   calculateGroundPlane();
00124   maxDistSq = curReq->maxDist*curReq->maxDist;
00125  
00126  if ( curReq->clearCamera ) {
00127    camShS.clear();
00128    camSkS.clear();
00129    curReq->gazePts.clear();
00130    curReq->baseToCamMats.clear();
00131  }
00132  if ( curReq->clearLocal ) {
00133    localShS.clear();
00134    localSkS.clear();
00135  }
00136  if ( curReq->clearWorld ) {
00137    worldShS.clear();
00138    worldSkS.clear();
00139  }
00140 
00141  if ( curReq->immediateRequest )
00142    grabCameraImageAndGo();
00143  else if ( !curReq->searchArea.isValid() && curReq->worldTargets.size() == 0 )
00144    storeImage(false);
00145  else {
00146    defineGazePts();
00147    if ( curReq->doScan == true )
00148      return; // wait for Lookout to finish scanning
00149    else if ( curReq->worldTargets.size() > 0 )
00150      doNextSearch();
00151    else if ( determineNextGazePoint() )
00152      moveToNextGazePoint();
00153    else
00154      requestComplete();
00155  }
00156 }
00157   
00158 //================================================================
00159 
00160 /*
00161 There are three kinds of events for MapBuilder::doEvent to handle:
00162 
00163 1. TextMsg event saying to move to the next gaze point; only needed
00164 when we've asserted manual control of gaze point sequencing for
00165 debugging.
00166 
00167 2. Lookout event announcing that a "point at" request is complete and
00168 an image has been stored.  We can now go ahead and process that image.
00169 
00170 3. Lookout event announcing that a "scan" request is complete and gaze
00171 points have been stored.  We can now start examining those gaze points.
00172 */
00173 
00174 void MapBuilder::doEvent() {
00175   if ( curReq == NULL) return;
00176   if ( curReq->verbosity & MBVevents )
00177     cout << "MapBuilder got event " << event->getName() << endl;
00178 
00179   switch ( event->getGeneratorID() ) {
00180   case EventBase::textmsgEGID:
00181     if ( strcmp(dynamic_cast<const TextMsgEvent&>(*event).getText().c_str(),"MoveHead") == 0 )
00182       moveToNextGazePoint(true);
00183     break;
00184 
00185   case EventBase::lookoutEGID:
00186     if ( event->getSourceID() == pointAtID )
00187       processImage(dynamic_cast<const LookoutSketchEvent&>(*event));
00188     else if ( event->getSourceID() == scanID ) {
00189       const vector<Point>& pts = dynamic_cast<const LookoutScanEvent*>(event)->getTasks().front()->data;
00190       cout << " doScan found " << pts.size() << " interest points." << endl;
00191       curReq->gazePts.insert(curReq->gazePts.end(),pts.begin(), pts.end());
00192     }
00193     else {
00194       cout << "MapBuilder: unexpected Lookout event " << event->getDescription()
00195      << ",   current pointAtID=" << pointAtID << ", scanID=" << scanID << endl;
00196       return;
00197     }
00198     // we've dealt with the event (processed an image or did a scan); now see what we should do next
00199     if ( requestExitTest() )
00200       requestComplete();
00201     else if ( curReq->worldTargets.size() > 0 )
00202       doNextSearch();
00203     else if ( determineNextGazePoint() )
00204       moveToNextGazePoint();
00205     else
00206       requestComplete();
00207     break;
00208 
00209   default:
00210     cout << "MapBuilder received unexpected event: " << event->getDescription() << endl;
00211   }
00212 }
00213 
00214 void MapBuilder::processImage(const LookoutSketchEvent &e) {
00215   if ( curReq->clearCamera ) {
00216     camSkS.clear(false); // don't clear retained sketches
00217     camShS.clear();
00218   }
00219   if ( curReq->rawY ) {
00220     NEW_SKETCH(rawY, uchar, VRmixin::sketchFromRawY());
00221   }
00222   NEW_SKETCH(camFrame, uchar, e.getSketch());
00223   if (curReq->userImageProcessing != NULL) (*curReq->userImageProcessing)();
00224   const fmat::Transform& camToBase = curReq->baseTransform * e.toBaseMatrix;
00225   const fmat::Transform baseToCam = camToBase.rigidInverse();
00226   getCameraShapes(camFrame);
00227   if (curReq->userCamProcessing != NULL) (*curReq->userCamProcessing)();
00228   if (curReq->getRequestType() > MapBuilderRequest::cameraMap) {
00229     projectToGround(camToBase);
00230     if (curReq->userGroundProcessing != NULL) (*curReq->userGroundProcessing)();
00231     filterGroundShapes(baseToCam);
00232   }
00233   switch ( curReq->getRequestType() ) {
00234   case MapBuilderRequest::cameraMap:
00235   case MapBuilderRequest::groundMap:
00236     break;
00237   case MapBuilderRequest::localMap:
00238     extendLocal(baseToCam);
00239     if (curReq->userLocalProcessing != NULL) (*curReq->userLocalProcessing)();
00240     break;
00241   case MapBuilderRequest::worldMap:
00242     extendLocal(baseToCam);
00243     if (curReq->userLocalProcessing != NULL) (*curReq->userLocalProcessing)();
00244     extendWorld(baseToCam);
00245     if (curReq->userWorldProcessing != NULL) (*curReq->userWorldProcessing)();
00246   }
00247 }
00248 
00249 bool MapBuilder::determineNextGazePoint() {
00250   if (curReq->getRequestType() == MapBuilderRequest::worldMap) {
00251     worldShS.applyTransform(worldToLocalMatrix,egocentric);
00252     bool b = determineNextGazePoint(worldShS.allShapes()) || determineNextGazePoint(curReq->gazePts);
00253     worldShS.applyTransform(localToWorldMatrix,allocentric); // transform back
00254     return b;
00255   }
00256   else
00257     return determineNextGazePoint(localShS.allShapes()) || determineNextGazePoint(curReq->gazePts);
00258 }
00259 
00260   
00261 bool MapBuilder::determineNextGazePoint(const vector<ShapeRoot>& shapes) {
00262   if ( ! curReq->pursueShapes ) return false;
00263   HeadPointerMC headpointer_mc;
00264   for (vector<ShapeRoot>::const_iterator it = shapes.begin();
00265        it != shapes.end(); it++) {
00266     // look for invalid endpoints of lines / polygons
00267     if ((*it)->isType(lineDataType) || (*it)->isType(polygonDataType)) {
00268       const Shape<LineData>& ld = ShapeRootTypeConst(*it,LineData);
00269       const Shape<PolygonData>& pd = ShapeRootTypeConst(*it,PolygonData);
00270       bool isLine = (*it)->isType(lineDataType);
00271       EndPoint p[2] = { isLine ? ld->end1Pt(): pd->end1Pt(), isLine ? ld->end2Pt() : pd->end2Pt()};
00272       for (int i = 0; i < 2; i++) {
00273   if ( !p[i].isValid() && !isBadGazePoint(p[i] )
00274       && badGazePoints.end() == find(badGazePoints.begin(), badGazePoints.end(), (Point) p[i])) {
00275     cout << "Next gazepoint at endpoint" << (i+1) << " of shape id " 
00276          << (*it)->getId() << " at " << p[i] << endl;
00277     if ( !headpointer_mc.lookAtPoint(p[i].coordX(),p[i].coordY(),p[i].coordZ()) ) {
00278       if ( curReq->verbosity & MBVbadGazePoint )
00279         cout << "MapBuilder noting unreachable gaze point " << (Point)p[i] << endl;
00280       badGazePoints.push_back((Point)p[i]);
00281     }
00282     nextGazePoint = p[i];
00283     return true;
00284   }
00285       }
00286     }
00287     // look for blobs with incomplete bounding boxes
00288     if ( (*it)->isType(blobDataType) ) {
00289       const Shape<BlobData>& bd = ShapeRootTypeConst(*it,BlobData);
00290       if ( ! (bd->bottomValid && bd->topValid && bd->leftValid && bd->rightValid) ) {
00291   Point bcentroid = bd->getCentroid();
00292   if ( ! isBadGazePoint(bcentroid) ) {
00293     // blob may never be completely visible, so give it one try but don't keep trying
00294     badGazePoints.push_back(bcentroid);
00295     cout << "Next gazepoint for blob at " << bcentroid << endl;
00296     nextGazePoint = bcentroid;
00297     return true;
00298   }
00299       }
00300     }
00301     // look for shapes w/ <2 confidence
00302     if ((!(*it)->isType(agentDataType)) &&
00303   (*it)->getLastMatchId() != 0 &&
00304   (*it)->getConfidence() <= 1 &&
00305   ! isBadGazePoint((*it)->getCentroid()) &&
00306   badGazePoints.end() == find(badGazePoints.begin(), badGazePoints.end(), (*it)->getCentroid()))  {
00307       const Point pt = (*it)->getCentroid();
00308       cout << "Next gaze point is shape " << (*it)
00309      << " (confidence level: " << (*it)->getConfidence() << ")" << endl;      
00310       cout << " at " << pt << endl;  
00311       if (! headpointer_mc.lookAtPoint(pt.coordX(),pt.coordY(),pt.coordZ()))
00312   badGazePoints.push_back(pt);
00313       nextGazePoint = pt;
00314       return true;
00315     }
00316     
00317     if ( curReq->verbosity & MBVskipShape )
00318       cout << "Skipping shape " << (*it)
00319      << " (confidence level: " << (*it)->getConfidence() << ")" << endl;      
00320   }
00321   return false;
00322 }
00323 
00324 
00325 bool MapBuilder::determineNextGazePoint(vector<Point>& gazePts) {
00326   for ( vector<Point>::iterator it = gazePts.begin();
00327   it != gazePts.end();  it = gazePts.erase(it) )
00328     if ( ! isBadGazePoint(*it) ) {
00329       nextGazePoint = *it;
00330       return true;
00331     }
00332   return false;
00333 }
00334 
00335 void MapBuilder::moveToNextGazePoint(const bool manualOverride) {
00336   if ( curReq == NULL ) {
00337     cout << "curReq == NULL in moveToNextGazePoint!" << endl;
00338     return;
00339   }
00340   if ( (curReq->verbosity & MBVnextGazePoint) || (curReq->manualHeadMotion && manualOverride==false) )
00341     cout << "moveToNextGazePoint " << nextGazePoint << endl;
00342   if ( curReq->manualHeadMotion && manualOverride==false ) {
00343     cout << "To proceed to this gaze point:  !msg MoveHead" << endl;
00344     return;
00345   }
00346   else
00347     storeImage(true);
00348 }
00349 
00350 
00351 void MapBuilder::doNextSearch() {
00352   LookoutSearchRequest *curLSR = curReq->worldTargets.front();
00353   curReq->worldTargets.pop();
00354   pointAtID = VRmixin::lookout->executeRequest(*curLSR);
00355 }
00356 
00357 bool MapBuilder::isBadGazePoint(const Point& Pt) const {
00358   const coordinate_t x = Pt.coordX();
00359   const coordinate_t y = Pt.coordY();
00360   return ( x*x + y*y > maxDistSq || x < -30.0);
00361 }
00362 
00363 void MapBuilder::storeImage(bool useNextGazePoint) {
00364   LookoutPointRequest lreq;
00365   lreq.motionSettleTime = curReq->motionSettleTime;
00366   lreq.numSamples = curReq->numSamples;
00367   lreq.sampleInterval = curReq->sampleInterval;
00368   if ( useNextGazePoint )
00369     lreq.setTarget(nextGazePoint);
00370   else
00371     lreq.setHeadMotionType(LookoutRequestBase::noMotion);
00372   pointAtID = VRmixin::lookout->executeRequest(lreq);
00373 }
00374 
00375 void MapBuilder::grabCameraImageAndGo() {
00376   // This is a performance hack to avoid calling the Lookout or event
00377   // router, so the MapBuilder can produce results very quickly when
00378   // we need real-time performance, e.g., for particle filtering where
00379   // we take multiple snapshots.
00380   pointAtID = 0;
00381   Sketch<uchar> camFrame(VRmixin::sketchFromSeg());
00382 #ifdef TGT_HAS_CAMERA
00383   const fmat::Transform camToBase = kine->linkToBase(CameraFrameOffset);
00384 #else
00385   const fmat::Transform &camToBase = fmat::Transform::identity();
00386 #endif
00387   LookoutSketchEvent dummy(true, camFrame, camToBase,
00388          EventBase::lookoutEGID, pointAtID, EventBase::deactivateETID);
00389   processImage(dummy);
00390   requestComplete();
00391 }
00392 
00393 void MapBuilder::scanForGazePts() {
00394   LookoutScanRequest lreq;
00395   lreq.searchArea = curReq->searchArea;
00396   lreq.motionSettleTime = curReq->motionSettleTime;
00397   set<color_index> colors;  // colors of all the shape types we're looking for
00398   for (map<ShapeType_t,set<color_index> >::const_iterator it1 = curReq->objectColors.begin();
00399        it1 != curReq->objectColors.end(); it1++)
00400     for (set<color_index>::const_iterator it2 = it1->second.begin();
00401    it2 != it1->second.end(); it2++)
00402       colors.insert(*it2);
00403   lreq.addTask(LookoutScanRequest::VisionRegionTask(colors,curReq->dTheta));
00404   scanID = VRmixin::lookout->executeRequest(lreq);
00405 }
00406 
00407 void MapBuilder::extendLocal(const fmat::Transform& baseToCam) {
00408   vector<ShapeRoot> all = localShS.allShapes();
00409   removeNoise(localShS, baseToCam);
00410   matchSrcToDst(groundShS,localShS,curReq->objectColors[polygonDataType]);
00411   // Took out the call to removeGazePts because AprilTags can get cut off if their 
00412   // centroid is visible but near the edge of the camera image; need to foveate them.
00413   // 
00414   // removeGazePts(curReq->gazePts, baseToCam);
00415   if ( curReq->gazePts.size() > 0 )
00416     curReq->gazePts.erase(curReq->gazePts.begin());
00417   curReq->baseToCamMats.push_back(baseToCam);
00418 }
00419 
00420 void MapBuilder::extendWorld(const fmat::Transform& baseToCam) {
00421   worldShS.applyTransform(worldToLocalMatrix,egocentric);
00422   removeNoise(worldShS, baseToCam);
00423   matchSrcToDst(localShS,worldShS,curReq->objectColors[polygonDataType]);  // *** should this be localShS instead of groundShS? -- DST
00424   worldShS.applyTransform(localToWorldMatrix,allocentric);
00425   removeGazePts(curReq->gazePts,baseToCam);
00426   curReq->baseToCamMats.push_back(baseToCam);
00427 }
00428 
00429 bool MapBuilder::requestExitTest() {
00430   if ( curReq->exitTest == NULL )
00431     return false;
00432   else
00433     return (*curReq->exitTest)();
00434 }
00435 
00436 void MapBuilder::requestComplete() {
00437   const size_t reqID = curReq->requestID;
00438   if ( curReq->verbosity & MBVcomplete )
00439     cout << "MapBuilderRequest " << reqID << " complete\n";
00440   BehaviorBase* reqbeh = curReq->requestingBehavior;
00441   delete curReq;
00442   curReq = NULL;
00443   requests.pop();
00444   if ( reqbeh )
00445     erouter->postEvent(EventBase::mapbuilderEGID, (size_t)reqbeh, EventBase::statusETID,0);
00446  else {
00447    erouter->postEvent(EventBase::mapbuilderEGID, reqID, EventBase::statusETID,0);
00448    erouter->postEvent(EventBase::mapbuilderEGID, reqID, EventBase::deactivateETID,0);
00449  }
00450   if ( requests.empty() )
00451     VRmixin::lookout->relax();
00452   else
00453     executeRequest(); // execute next request AFTER deactivate event has finished processing
00454 }
00455 
00456 void MapBuilder::setAgent(const Point &worldLocation, const AngTwoPi worldHeading, bool quiet) {
00457   if ( !quiet && ((curReq==NULL ? verbosity : curReq->verbosity) & MBVsetAgent) )
00458     cout << "Agent now at " << worldLocation << " hdg " << worldHeading 
00459          << " (= " << float(worldHeading)*180/M_PI << " deg.)" << endl;
00460   theAgent->setCentroidPt(worldLocation);
00461   theAgent->setOrientation(worldHeading);
00462   const coordinate_t dx = worldLocation.coordX();
00463   const coordinate_t dy = worldLocation.coordY();
00464   const coordinate_t dz = worldLocation.coordZ();
00465   float const c = cos(worldHeading);
00466   float const s = sin(worldHeading);
00467   float localToWorld[] =
00468     {c, -s, 0, dx,
00469      s,  c, 0, dy, 
00470      0,  0, 1, dz};
00471   localToWorldMatrix = fmat::Matrix<4,3>(localToWorld).transpose();;
00472   worldToLocalMatrix = localToWorldMatrix.inverse();
00473 }
00474 
00475 void MapBuilder::moveAgent(coordinate_t const local_dx, coordinate_t const local_dy, coordinate_t const local_dz, AngSignPi dtheta) {
00476   Point const agentLoc = theAgent->getCentroid();
00477   AngTwoPi const heading = theAgent->getOrientation();
00478   float const c = cos(heading);
00479   float const s = sin(heading);
00480   float const dx = local_dx*c + local_dy*-s;
00481   float const dy = local_dx*s + local_dy*c;
00482   setAgent(agentLoc + Point(dx,dy,local_dz,allocentric), heading+dtheta);
00483 }
00484 
00485 void MapBuilder::importLocalToWorld() {
00486   worldShS.applyTransform(worldToLocalMatrix,egocentric);
00487   matchSrcToDst(localShS,worldShS);
00488   worldShS.applyTransform(localToWorldMatrix,allocentric);
00489 }
00490 
00491 ShapeRoot MapBuilder::importLocalShapeToWorld(const ShapeRoot &localShape) {
00492   ShapeRoot worldShape(worldShS.importShape(localShape));
00493   worldShape->applyTransform(localToWorldMatrix, allocentric);
00494   return worldShape;
00495 }
00496 
00497 ShapeRoot MapBuilder::importWorldToLocal(const ShapeRoot &worldShape) {
00498   ShapeRoot localShape(localShS.importShape(worldShape));
00499   localShape->applyTransform(worldToLocalMatrix,egocentric);
00500   return localShape;
00501 }
00502 
00503 bool MapBuilder::isPointVisible(const Point &pt, const fmat::Transform& baseToCam, float maxDistanceSq) {
00504   fmat::Column<3> camCoords = baseToCam*fmat::pack(pt.coordX(),pt.coordY(),pt.coordZ());
00505   //  if (camCoords(3) <=0 || distSq(camCoords) >= maxDistanceSq) return false;
00506   if ( fmat::SubVector<2>(camCoords).sumSq() >= maxDistanceSq )
00507     return false;
00508   float normX,normY; // normalized coordinates in cam frame
00509   config->vision.computePixel(camCoords[0],camCoords[1],camCoords[2],normX,normY);
00510   return (fabs(normX) < 0.9f && fabs(normY) < config->vision.aspectRatio*.9f); //normX and normY range from -1 to 1. Giving 10% offset here
00511 }
00512 
00513 bool MapBuilder::isLineVisible(const LineData& ln, const fmat::Transform& baseToCam) {
00514   float normX1,normX2,normY1,normY2;
00515   fmat::Column<3> camCoords;
00516   Point pt = ln.end1Pt();
00517   camCoords = baseToCam*fmat::pack(pt.coordX(),pt.coordY(),pt.coordZ());
00518   config->vision.computePixel(camCoords[0],camCoords[1],camCoords[2],normX1,normY1);
00519   pt = ln.end2Pt();
00520   camCoords = baseToCam*fmat::pack(pt.coordX(),pt.coordY(),pt.coordZ());
00521   config->vision.computePixel(camCoords[0],camCoords[1],camCoords[2],normX2,normY2);
00522   const float xRange = 0.9f;
00523   const float yRange = config->vision.aspectRatio*.9f; // y ranges ± aspect ratio, then 10% margin
00524   const bool end1Pt_visible = fabs(normX1) < xRange && fabs(normY1) < yRange;
00525   const bool end2Pt_visible = fabs(normX2) < xRange && fabs(normY2) < yRange;
00526   if (end1Pt_visible && end2Pt_visible)
00527     return true;
00528   LineData lnCam(VRmixin::groundShS, Point(normX1,normY1), Point(normX2,normY2));
00529   // define bounding box of camera frame in terms of normalized coordinates with 10% offset
00530   LineData camBounds[] = {LineData(VRmixin::groundShS, Point( xRange, yRange), Point( xRange,-yRange)),
00531         LineData(VRmixin::groundShS, Point( xRange,-yRange), Point(-xRange,-yRange)),
00532         LineData(VRmixin::groundShS, Point(-xRange,-yRange), Point(-xRange, yRange)),
00533         LineData(VRmixin::groundShS, Point(-xRange, yRange), Point( xRange, yRange))};
00534   unsigned int ptCount = 0;
00535   Point p[2];
00536   // find if a portion of the line shows up in cam
00537   if (end1Pt_visible) p[ptCount++].setCoords(normX1,normY1,0); // end1Pt in frame
00538   else if (end2Pt_visible) p[ptCount++].setCoords(normX2,normY2,0); // end2Pt in frame
00539   for (int i = 0; i < 4; i++)
00540     if (camBounds[i].intersectsLine(lnCam)) {
00541       p[ptCount++].setCoords(lnCam.intersectionWithLine(camBounds[i]));
00542       // Let's say portion of line seen in cam should be longer than .1 normalized
00543       if (ptCount > 1)
00544   return p[0].distanceFrom(p[1]) > 0.1; 
00545     }
00546   return false;
00547 }
00548 
00549 bool MapBuilder::isShapeVisible(const ShapeRoot &ground_shape, const fmat::Transform& baseToCam,
00550            float maxDistanceSq) {
00551   if (ground_shape->isType(lineDataType))
00552     return isLineVisible(ShapeRootTypeConst(ground_shape,LineData).getData(), baseToCam);
00553   else if (ground_shape->isType(polygonDataType)) {
00554     const Shape<PolygonData>& polygon = ShapeRootTypeConst(ground_shape,PolygonData);
00555     for (vector<LineData>::const_iterator edge_it = polygon->getEdges().begin();
00556    edge_it != polygon->getEdges().end(); edge_it++)
00557       if (isLineVisible(*edge_it,baseToCam))
00558   return true;
00559     return false;
00560   }
00561   else 
00562     return isPointVisible(ground_shape->getCentroid(), baseToCam, maxDistanceSq);
00563 }
00564 
00565 
00566 // filter "bad" ground shapes before importing to dst shape space.
00567 // 1. ignore shapes too far from dog or projected to the other side of cam plane
00568 // 2. chop off line at max distance if it is extending beyond the distance and leave the endpoint invalid
00569 void MapBuilder::filterGroundShapes(const fmat::Transform& baseToCam) {
00570   //  cout << "MapBuilder::filterGroundShapes()" << endl;
00571   vector<ShapeRoot> ground_shapes = groundShS.allShapes();
00572 
00573   for (vector<ShapeRoot>::iterator ground_it = ground_shapes.begin();
00574        ground_it != ground_shapes.end(); ground_it++ ) {
00575     const coordinate_t cenX = (*ground_it)->getCentroid().coordX();
00576     const coordinate_t cenY = (*ground_it)->getCentroid().coordY();
00577     if (cenX*cenX + cenY*cenY > maxDistSq) { // too far
00578       if ( curReq->verbosity & MBVnotAdmissible )
00579   cout << "ground shape " << (*ground_it)->getId() << " (lastMatch " 
00580        << (*ground_it)->getLastMatchId() << ") too far, delete\n";
00581       ground_it->deleteShape();
00582     }
00583     fmat::Column<3> coords = Kinematics::pack(cenX,cenY,(*ground_it)->getCentroid().coordZ());
00584     coords = baseToCam*coords;
00585     if (coords[2] < 0) { // negative z-coordinate in camera frame indicates projection failed
00586       if ( curReq->verbosity & MBVprojectionFailed )
00587   cout << "Projection failed for ground shape " << (*ground_it)->getId()
00588        << ": " << coords
00589        << " (lastMatch " << (*ground_it)->getLastMatchId() << "): deleting\n";
00590       ground_it->deleteShape();
00591     }
00592     // if a line is extending to maxDistance, chop it off at maxdistance and mark the endpoint invalid
00593     else if ((*ground_it)->isType(lineDataType)) {
00594       Shape<LineData>& line = ShapeRootType(*ground_it,LineData);
00595       const coordinate_t e1x = line->end1Pt().coordX();
00596       const coordinate_t e1y = line->end1Pt().coordY();
00597       const coordinate_t e2x = line->end2Pt().coordX();
00598       const coordinate_t e2y = line->end2Pt().coordY();
00599       if (e1x*e1x + e1y*e1y > maxDistSq && e2x*e2x + e2y*e2y > maxDistSq)
00600   ground_it->deleteShape();
00601       else if (e1x*e1x + e1y*e1y > maxDistSq || e2x*e2x + e2y*e2y > maxDistSq) {
00602   //  cout << (*ground_it)->getId() << "(lastMatch " << (*ground_it)->getLastMatchId() 
00603   //       << ")  extends beyond maximum distance we want. Chop off the line" << endl;
00604   vector<float> line_abc = line->lineEquation_abc();
00605   Point pt;
00606   const EndPoint* far_ept = (e1x*e1x + e1y*e1y > maxDistSq) ? &line->end1Pt() : &line->end2Pt(); 
00607         if (line_abc[1] == 0.0) {
00608     const coordinate_t y_abs = sqrt(maxDistSq - line_abc[2]*line_abc[2]);
00609     if (fabs(far_ept->coordY()-y_abs) < fabs(far_ept->coordY()+y_abs))
00610       pt.setCoords(e1x, y_abs, far_ept->coordZ());
00611     else
00612       pt.setCoords(e1x, -y_abs, far_ept->coordZ());
00613   }
00614   else {
00615     const float a = - line_abc[0]/line_abc[1];
00616     const float b = line_abc[2]/line_abc[1];
00617     const coordinate_t x1 = (std::sqrt((a*a+1)*maxDistSq-b*b)-a*b)/(a*a+1);
00618     const coordinate_t x2 = (-std::sqrt((a*a+1)*maxDistSq-b*b)-a*b)/(a*a+1);
00619     if (std::abs(far_ept->coordX()-x1) < std::abs(far_ept->coordX()-x2))
00620       pt.setCoords(x1, a*x1+b, far_ept->coordZ());
00621     else
00622       pt.setCoords(x2, a*x2+b, far_ept->coordZ());
00623   }
00624   EndPoint temp_endPt(pt);
00625   temp_endPt.setValid(false);
00626   //  cout << " (" << far_ept->coordX() << "," << far_ept->coordY() << ") => ("
00627   //       << pt.coordX() << "," << pt.coordY() << ")" << endl;
00628   if (e1x*e1x + e1y*e1y > maxDistSq)
00629     line->setEndPts(temp_endPt, line->end2Pt());
00630   else
00631     line->setEndPts(line->end1Pt(), temp_endPt);
00632   badGazePoints.push_back(pt);
00633       }
00634     }
00635   }
00636 }
00637 
00638 void MapBuilder::calculateGroundPlane() {
00639   switch(curReq->groundPlaneAssumption) {
00640   case MapBuilderRequest::onLegs:
00641     ground_plane = kine->calculateGroundPlane(); 
00642     if ( curReq->verbosity & MBVgroundPlane ) 
00643       cout << "Calculated ground plane: " << ground_plane << endl;
00644     break;
00645   case MapBuilderRequest::onStand:
00646 #if defined(TGT_ERS210) || defined(TGT_ERS220) || defined(TGT_ERS2xx)
00647     ground_plane = PlaneEquation(0,0,-1,200);
00648 #else
00649     ground_plane = PlaneEquation(0,0,-1,170);
00650 #endif
00651     // cout << "Hard-coded ground plane: " << NEWMAT::printmat(ground_plane) << endl;    
00652     break;
00653   case MapBuilderRequest::onWheel:
00654 #ifdef TGT_REGIS1
00655     std::cout << "Target Regis 1 Mapping";
00656       ground_plane = fmat::pack<float>(0,0,(-1/85.0),1);
00657 #endif
00658   break;    
00659   case MapBuilderRequest::custom:
00660     ground_plane = curReq->customGroundPlane;
00661   }
00662 }
00663 
00664 void MapBuilder::projectToGround(const fmat::Transform& camToBase) {
00665   VRmixin::projectToGround(camToBase, ground_plane);
00666 }
00667 
00668 ShapeRoot MapBuilder::projectToLocal(ShapeRoot &shape) {
00669 #ifdef TGT_HAS_CAMERA
00670   fmat::Transform camToBase = kine->linkToBase(CameraFrameOffset);
00671 #else
00672   fmat::Transform camToBase = fmat::Transform::identity();
00673 #endif
00674   ground_plane = kine->calculateGroundPlane();
00675   
00676   groundShS.importShape(shape);
00677   ShapeRoot &newShape = groundShS.allShapes().back();
00678   newShape->projectToGround(camToBase, ground_plane);
00679   localShS.importShape(newShape);
00680   return localShS.allShapes().back();
00681 }
00682   
00683 void MapBuilder::matchSrcToDst(ShapeSpace &srcShS, ShapeSpace &dstShS,
00684           set<color_index> polCols, bool mergeSrc, bool mergeDst) {
00685   vector<ShapeRoot> src_shapes = srcShS.allShapes();
00686   vector<ShapeRoot> dst_shapes = dstShS.allShapes();
00687   vector<LineData> polygon_edges;
00688   
00689   // clean up src_shapes before messing with dst space
00690   std::set<int> markedForDeletion;
00691   vector<ShapeRoot> cleaned_src;
00692   for (vector<ShapeRoot>::iterator src_it = src_shapes.begin();
00693        src_it != src_shapes.end(); src_it++ ) {
00694     if (!(*src_it)->isAdmissible()) {
00695       if (curReq && curReq->verbosity & MBVnotAdmissible )
00696   cout << "shape " << (*src_it)->getId() << " (lastMatch " 
00697        << (*src_it)->getLastMatchId() << ") is not admissible." << endl;
00698       // (*src_it)->printParams();
00699       markedForDeletion.insert((*src_it)->getId());
00700     }
00701     else if ((*src_it)->isType(polygonDataType)) { 
00702       const vector<LineData>& lines = ShapeRootTypeConst(*src_it, PolygonData)->getEdges();
00703       polygon_edges.insert(polygon_edges.end(), lines.begin(), lines.end());
00704       markedForDeletion.insert((*src_it)->getId());
00705     }
00706     else if ((*src_it)->isType(lineDataType)) {
00707       const color_index colorIndex = ProjectInterface::getColorIndex((*src_it)->getColor());
00708       if ( polCols.end() != find(polCols.begin(), polCols.end(), colorIndex)) {
00709   polygon_edges.push_back(ShapeRootTypeConst(*src_it, LineData).getData());
00710   markedForDeletion.insert((*src_it)->getId());
00711       }
00712     }
00713   }
00714   // now copy the shapes we didn't mark for deletion
00715   for ( vector<ShapeRoot>::iterator it = src_shapes.begin();
00716   it != src_shapes.end(); it++ )
00717     if ( markedForDeletion.find((*it)->getId()) == markedForDeletion.end() )
00718       cleaned_src.push_back(*it);
00719   src_shapes = cleaned_src;
00720 
00721   // merge shapes inside srcShS
00722   if (mergeSrc) {
00723     markedForDeletion.clear();
00724     cleaned_src.clear();
00725     for ( vector<ShapeRoot>::iterator it = src_shapes.begin();
00726     it != src_shapes.end(); it++ )
00727       for ( vector<ShapeRoot>::iterator it2 = it+1;
00728       it2 != src_shapes.end(); it2++)
00729   if ((*it2)->isMatchFor(*it) && (*it)->updateParams(*it2)) {
00730     if (curReq && curReq->verbosity & MBVshapesMerge )
00731       cout << "merging shape " << (*it)->getId() << " (from " << (*it)->getLastMatchId()
00732      << ") and shape " << (*it2)->getId() << " (from " << (*it2)->getLastMatchId() << ")" << endl;
00733     markedForDeletion.insert((*it2)->getId());
00734   }
00735     // now copy the shapes we didn't mark for deletion
00736     for ( vector<ShapeRoot>::iterator it = src_shapes.begin();
00737     it != src_shapes.end(); it++ )
00738       if ( markedForDeletion.find((*it)->getId()) == markedForDeletion.end() )
00739   cleaned_src.push_back(*it);
00740     src_shapes = cleaned_src;
00741   }
00742 
00743   // update dst shapes from src shapes
00744   markedForDeletion.clear();
00745   cleaned_src.clear();
00746   vector<Shape<PolygonData> > existingPolygons;
00747   for (vector<ShapeRoot>::iterator dst_it = dst_shapes.begin();
00748        dst_it != dst_shapes.end(); dst_it++ ) {
00749     if ((*dst_it)->isType(polygonDataType)) {
00750       existingPolygons.push_back(ShapeRootType(*dst_it,PolygonData));
00751       continue;
00752     }
00753     if ( (*dst_it)->isType(localizationParticleDataType) )
00754       continue;
00755     for (vector<ShapeRoot>::iterator src_it = src_shapes.begin();
00756    src_it != src_shapes.end(); src_it++)
00757       if ((*dst_it)->isMatchFor(*src_it) && (*dst_it)->updateParams((*src_it))) {
00758   (*dst_it)->increaseConfidence(2);  // add 2 because we'll subtract 1 at the end
00759   (*dst_it)->setLastMatchId((*src_it)->getId());
00760   if (curReq && curReq->verbosity & MBVshapeMatch )
00761     cout << "Matched src shape " << (*src_it)->getId() << " (lastMatch " << (*src_it)->getLastMatchId()
00762          << ") to dst shape " << (*dst_it)->getId() << endl;
00763   markedForDeletion.insert((*src_it)->getId());
00764       }
00765   }
00766 
00767   // now copy the shapes we didn't mark for deletion
00768   for ( vector<ShapeRoot>::iterator it = src_shapes.begin();
00769   it != src_shapes.end(); it++ )
00770     if ( markedForDeletion.find((*it)->getId()) == markedForDeletion.end() )
00771       cleaned_src.push_back(*it);
00772   src_shapes = cleaned_src;
00773   
00774   // form polygons from lines and import unmatched src shapes into dstShS
00775   vector<ShapeRoot> deletedPolygons;
00776   //  cout << existingPolygons.size() << " polygons\n";
00777   vector<ShapeRoot> newPolygons = PolygonData::formPolygons(polygon_edges,existingPolygons,deletedPolygons);
00778   //  cout << existingPolygons.size()+deletedPolygons.size() << " polygons\n";
00779   for (vector<ShapeRoot>::iterator delete_it = deletedPolygons.begin();
00780        delete_it != deletedPolygons.end(); delete_it++)
00781     delete_it->deleteShape();
00782   dstShS.importShapes(newPolygons);
00783   dstShS.importShapes(src_shapes);
00784   if (curReq && curReq->verbosity & MBVimportShapes )
00785     cout << "Imported " << (src_shapes.size()+newPolygons.size()) << " new shape(s) from "
00786    << srcShS.name << "ShS to " << dstShS.name << "ShS\n";
00787   
00788   // match shapes inside dstShS
00789   // ***NOTE: this code is wrong because deleting a shape should force a restart of the iteration
00790   if (mergeDst) {
00791     dst_shapes = dstShS.allShapes();
00792     for ( vector<ShapeRoot>::iterator it = dst_shapes.begin();
00793     it != dst_shapes.end(); it++ )
00794       if ( ! (*it)->isType(localizationParticleDataType) )
00795   for ( vector<ShapeRoot>::iterator it2 = it+1;
00796         it2 < dst_shapes.end(); it2++)
00797     if ((*it2)->isMatchFor(*it) && (*it)->updateParams(*it2,true)) {
00798       cout << "Matched src shape " << (*it)->getId() << " (lastMatch " 
00799      << (*it)->getLastMatchId()<< ") is a match for " 
00800      << (*it2)->getId() << " (lastMatch " 
00801      << (*it2)->getLastMatchId() << "), delete\n";
00802       it2->deleteShape();
00803     }
00804   }
00805 }
00806 
00807 // decrease confidence of those shapes that should have been seen in the last snap,
00808 // remove shapes from ShapeSpace if confidence becomes < 0
00809 void MapBuilder::removeNoise(ShapeSpace& ShS, const fmat::Transform& baseToCam) {
00810   //  cout << "MapBuilder::removeNoise()\n";
00811   vector<ShapeRoot> shapes = ShS.allShapes();
00812   for (vector<ShapeRoot>::iterator it = shapes.begin();
00813        it != shapes.end(); it++ ) {
00814     // was not looking for this object in the last snap, 
00815     // and it's not fair to decrease this guy's confidence
00816     if (curReq->objectColors[(*it)->getType()].find(ProjectInterface::getColorIndex((*it)->getColor())) ==
00817   curReq->objectColors[(*it)->getType()].end())
00818       continue; 
00819     if ((*it)->isType(polygonDataType)) {
00820       Shape<PolygonData>& polygon = ShapeRootType(*it,PolygonData);
00821       vector<LineData>& edges = polygon->getEdgesRW();
00822       unsigned int edge_index = 0;
00823       for (vector<LineData>::iterator edge_it = edges.begin();
00824      edge_it != edges.end(); edge_it++, edge_index++) {
00825   if (isLineVisible(*edge_it, baseToCam)) {
00826     if ( curReq->verbosity & MBVshouldSee )
00827       cout << "edge " << edge_index << " of polygon " << (*it)->getId() << "(confidence: " 
00828      << edge_it->getConfidence() << ") should be visible in this frame" << endl;
00829     edge_it->decreaseConfidence();
00830   }
00831       }
00832       vector<ShapeRoot> brokenPolygons = polygon->updateState();
00833       ShS.importShapes(brokenPolygons);
00834       if (!polygon->isAdmissible()) {
00835   if ( curReq->verbosity & MBVdeleteShape )
00836     cout << "delete polygon " << (*it)->getId() << " from map" << endl;
00837   it->deleteShape();
00838       }
00839     }
00840     else if ((!(*it)->isType(agentDataType)) && isShapeVisible(*it, baseToCam, maxDistSq)){
00841       (*it)->decreaseConfidence(); // decrease confidence by 1 for every visible shape
00842       if ((*it)->getConfidence() < 0 ) {
00843   if ( curReq->verbosity & MBVshouldSee )
00844     cout << "shape " << (*it)->getId() << "(confidence: " << (*it)->getConfidence() 
00845          << ") should be visible in this frame; deleted" << endl;
00846   it->deleteShape();
00847       }
00848     }
00849   }
00850 }
00851 
00852 //================ Gaze points ================
00853 
00854 void MapBuilder::defineGazePts() {
00855   const ShapeRoot &sArea = curReq->searchArea;
00856   if ( !sArea.isValid() )
00857     return;
00858   else if ( curReq->doScan == true )
00859     scanForGazePts();
00860   else
00861     switch ( sArea->getType() ) {
00862 
00863     case pointDataType:
00864       curReq->gazePts.push_back(sArea->getCentroid());
00865       break;
00866 
00867     case lineDataType: {
00868       static const float meshSize=50;
00869       const Shape<LineData>& line = ShapeRootTypeConst(sArea,LineData);
00870       if ( curReq->doScan == true )
00871   scanForGazePts();
00872       else {
00873   int numIntervals = (int) (line->getLength()/meshSize);
00874   const EndPoint& ep1 = line->end1Pt();
00875   const EndPoint& ep2 = line->end2Pt();
00876   curReq->gazePts.push_back(ep1);
00877   for (int i = 1; i < numIntervals; i++)
00878     curReq->gazePts.push_back((ep1*i + ep2*(numIntervals-i))/numIntervals);
00879   curReq->gazePts.push_back(ep2);
00880       }
00881     }
00882       break;
00883 
00884     case polygonDataType:
00885       if ( curReq->doScan == true )
00886   scanForGazePts();
00887       else {
00888   const Shape<PolygonData> &poly = ShapeRootTypeConst(sArea,PolygonData);
00889   const vector<Point> &verts = poly->getVertices();
00890   curReq->gazePts.insert(curReq->gazePts.end(),verts.begin(),verts.end());
00891       }
00892       break;
00893 
00894       default:
00895   cerr << "ERROR MapBuilder::defineGazePts: Supported searchArea shapes are Point, Line, and Polygon\n";
00896   requestComplete();
00897   break;
00898     }
00899 }
00900 
00901 void MapBuilder::removeGazePts(vector<Point> &gazePts, const fmat::Transform& baseToCam) {
00902   if (curReq->removePts) {
00903     int num_points_seen = 0;
00904     for ( vector<Point>::iterator it = gazePts.begin();
00905     it != gazePts.end(); it++ ) {
00906       if ( isPointVisible(*it,baseToCam,maxDistSq) ) {
00907   cout << "Removing already-visible gaze point " << *it << endl;
00908   num_points_seen++;
00909   gazePts.erase(it--);
00910       }
00911     }
00912     // cout << num_points_seen << " pre-defined gaze points seen in last image, "
00913     //      << gazePts.size() << " left\n";
00914   }
00915 }
00916 
00917 
00918 //================================================================
00919 
00920 //Prints shapespace in the format to be used for particle filter on simulator
00921 void MapBuilder::printShS(ShapeSpace &ShS) const {
00922   cout << "MapBuilder::printShS()" << endl;
00923   unsigned int line_count = 0;
00924   vector<ShapeRoot> shapes = ShS.allShapes();
00925   for (vector<ShapeRoot>::const_iterator it = shapes.begin();
00926        it != shapes.end(); it++) {
00927     if ((*it)->isType(lineDataType)) {
00928       const Shape<LineData>& ld = ShapeRootTypeConst(*it,LineData);
00929       cout << (*it)->getId() << " " << lineDataType << " " 
00930      << ProjectInterface::getColorIndex((*it)->getColor()) 
00931      << " " << ld->end1Pt().coordX()  << " " << ld->end1Pt().coordY()
00932      << " " << ++line_count << " " << ld->getLength() << " " << ld->end1Pt().isValid() << endl; 
00933       cout << (*it)->getId() << " " << lineDataType << " " 
00934      << ProjectInterface::getColorIndex((*it)->getColor()) 
00935      << " " << ld->end2Pt().coordX()  << " " << ld->end2Pt().coordY()
00936      << " " << line_count << " " << ld->getLength() << " " << ld->end2Pt().isValid() << endl;
00937     }
00938     else {
00939       cout << (*it)->getId() << " " << (*it)->getType() << " " 
00940      << ProjectInterface::getColorIndex((*it)->getColor()) 
00941      << " " << (*it)->getCentroid().coordX()  << " " << (*it)->getCentroid().coordY() << endl;
00942     }
00943   }
00944 }
00945 
00946 
00947 //================ Shape extraction ================
00948 
00949 void MapBuilder::getCameraShapes(const Sketch<uchar>& camFrame) { 
00950   getCamLines(camFrame, curReq->objectColors[lineDataType], curReq->occluderColors[lineDataType]);
00951   getCamEllipses(camFrame, curReq->objectColors[ellipseDataType], curReq->occluderColors[ellipseDataType]);
00952   getCamPolygons(camFrame, curReq->objectColors[polygonDataType], curReq->occluderColors[polygonDataType]);
00953   getCamSpheres(camFrame, curReq->objectColors[sphereDataType], curReq->occluderColors[sphereDataType]);
00954   getCamCylinders(camFrame, curReq->objectColors[cylinderDataType], curReq->assumedCylinderHeights, curReq->minBlobAreas);
00955   getCamWalls(camFrame, curReq->floorColor);
00956   if ( curReq->numSamples == 1 && !curReq->searchArea.isValid() && 
00957        !curReq->objectColors[blobDataType].empty() && curReq->userImageProcessing == NULL )  // use CMVision's blob extraction from current camera frame
00958     getCamBlobs(curReq->objectColors[blobDataType]);
00959   else
00960     getCamBlobs(camFrame, curReq->objectColors[blobDataType], curReq->minBlobAreas, curReq->blobOrientations, curReq->assumedBlobHeights);
00961   getCamTargets(camFrame, curReq->objectColors[targetDataType], curReq->occluderColors[targetDataType]);
00962   getCamMarkers(camFrame, curReq->objectColors[markerDataType], curReq->occluderColors[markerDataType],
00963     curReq->markerTypes);
00964   //***** HACK: SHOULD BE GETTING THIS FROM LOOKOUT *****
00965   NEW_SKETCH_N(rawY, uchar, VRmixin::sketchFromRawY());
00966   getCamSiftObjects(rawY, curReq->siftDatabasePath, curReq->siftObjectNames);
00967   getCamAprilTags(rawY);
00968 }
00969 
00970 vector<Shape<LineData> > MapBuilder::getCamLines(const Sketch<uchar> &camFrame, const set<color_index>& objectColors, 
00971              const set<color_index>& occluderColors) const {
00972   vector<Shape<LineData> > linesReturned;
00973   if ( objectColors.empty() ) 
00974     return linesReturned;
00975   if ( curReq->verbosity & MBVshapeSearch )
00976     cout << "*** Find the lines ***" << endl;
00977   NEW_SKETCH_N(occluders,bool,visops::zeros(camFrame));
00978   for ( set<color_index>::const_iterator it = occluderColors.begin();
00979   it != occluderColors.end(); it++ )
00980     occluders |= visops::minArea(visops::colormask(camFrame,*it));
00981 
00982   for (set<color_index>::const_iterator it = objectColors.begin();
00983        it != objectColors.end(); it++) {
00984     NEW_SKETCH_N(colormask,bool,visops::colormask(camFrame,*it));
00985     NEW_SKETCH_N(cleancolor,bool,visops::minArea(colormask));
00986     NEW_SKETCH_N(fatmask,bool,visops::fillin(cleancolor,1,2,8));
00987     NEW_SKETCH_N(skel,bool,visops::skel(fatmask));
00988     vector<Shape<LineData> > line_shapes(LineData::extractLines(skel,cleancolor|occluders));
00989     linesReturned.insert(linesReturned.end(), line_shapes.begin(), line_shapes.end());
00990     if ( curReq->verbosity & MBVshapesFound )
00991       cout << "Found " << line_shapes.size() << " " 
00992      << ProjectInterface::getColorName(*it) << " lines." << endl;
00993   }
00994   return linesReturned;
00995 }
00996 
00997 vector<Shape<EllipseData> > 
00998 MapBuilder::getCamEllipses(const Sketch<uchar> &camFrame,
00999          const set<color_index>& objectColors, const set<color_index>& ) const {
01000   vector<Shape<EllipseData> > ellipses;
01001   if (objectColors.empty())
01002     return ellipses;
01003   if ( curReq->verbosity & MBVshapeSearch )
01004     cout << "*** Find the ellipses ***" << endl;
01005   for (set<color_index>::const_iterator it = objectColors.begin();
01006        it !=objectColors.end(); it++) {
01007     NEW_SKETCH_N(colormask,bool,visops::colormask(camFrame,*it));
01008     vector<Shape<EllipseData> > ellipse_shapes = EllipseData::extractEllipses(colormask);
01009     ellipses.insert(ellipses.end(), ellipse_shapes.begin(),ellipse_shapes.end());
01010     if ( curReq->verbosity & MBVshapesFound )
01011       cout << "Found " << ellipse_shapes.size() << " "
01012      << ProjectInterface::getColorName(*it) << " ellipses." << endl;
01013   }
01014   return ellipses;
01015 }
01016 
01017 void MapBuilder::getCamPolygons(const Sketch<uchar> &camFrame,
01018             const set<color_index>& objectColors, const set<color_index>& occluderColors) const {
01019   vector<Shape<PolygonData> > polygons;
01020   if ( objectColors.empty() ) 
01021     return;
01022   if ( curReq->verbosity & MBVshapeSearch )
01023     cout << "*** Find the polygons ***" << endl;
01024   NEW_SKETCH_N(occluders,bool,visops::zeros(camFrame));
01025   for ( set<color_index>::const_iterator it = occluderColors.begin();
01026   it !=occluderColors.end(); it++ )
01027     occluders |= visops::colormask(camFrame,*it);
01028   
01029   for (set<color_index>::const_iterator it = objectColors.begin();
01030        it !=objectColors.end(); it++) {
01031     NEW_SKETCH_N(colormask,bool,visops::colormask(camFrame,*it));
01032     NEW_SKETCH_N(fatmask,bool,visops::fillin(colormask,1,2,8));
01033     NEW_SKETCH_N(skel,bool,visops::skel(fatmask));
01034     NEW_SKETCH_N(fatskel,bool,visops::fillin(skel,1,2,8));
01035     
01036     vector<Shape<LineData> > polygon_lines = PolygonData::extractPolygonEdges(fatskel,fatmask|occluders);
01037     polygons.insert(polygons.end(), polygon_lines.begin(), polygon_lines.end());
01038     if ( curReq->verbosity & MBVshapesFound )
01039       cout << "Found " << polygon_lines.size() << " lines." << endl;
01040   }
01041 }
01042 
01043 void MapBuilder::getCamSpheres(const Sketch<uchar> &camFrame,
01044              const set<color_index>& objectColors, const set<color_index>& ) const {
01045   vector<Shape<SphereData> > spheres;
01046   if ( objectColors.empty() )
01047     return;
01048   if ( curReq->verbosity & MBVshapeSearch )
01049     cout << "*** Find the spheres ***" << endl;
01050   for (set<color_index>::const_iterator it = objectColors.begin();
01051        it !=objectColors.end(); it++) {
01052     NEW_SKETCH_N(colormask,bool,visops::colormask(camFrame,*it));
01053     vector<Shape<SphereData> > sphere_shapes = SphereData::extractSpheres(colormask);
01054     spheres.insert(spheres.end(), spheres.begin(), spheres.end());
01055     if ( curReq->verbosity & MBVshapesFound )
01056       cout << "Found " << sphere_shapes.size() << " spheres." << endl;
01057   }
01058 }
01059 
01060 void MapBuilder::getCamCylinders(const Sketch<uchar>& camFrame,
01061          const set<color_index>& colors,
01062          const map<color_index,coordinate_t>& assumedHeights,
01063          const map<color_index,int>& minCylinderAreas) {
01064   if ( colors.empty() )
01065     return;
01066   if ( curReq->verbosity & MBVshapeSearch )
01067     cout << "*** Find the cylinders ***" << endl;
01068   int const maxcylinders = 50;
01069   vector<Shape<CylinderData> > result(CylinderData::extractCylinders(camFrame, colors, assumedHeights, minCylinderAreas, maxcylinders));
01070   if ( curReq->verbosity & MBVshapesFound )
01071     if ( !result.empty() )
01072       cout << "Found " << result.size() << " cylinders." << endl;
01073 }
01074   
01075 vector<Shape<LineData> > 
01076 MapBuilder::getCamWalls(const Sketch<uchar> &camFrame, unsigned int floorColor) const {
01077   if (floorColor == 0)
01078     return vector<Shape<LineData> >();
01079   if ( curReq->verbosity & MBVshapeSearch )
01080     cout << "*** Find the walls ***" << endl;
01081   const int camFrame_offset = 8;
01082   
01083   NEW_SKETCH_N(colormask,bool,visops::colormask(camFrame,floorColor));
01084   NEW_SKETCH_N(fillinmask ,bool,visops::fillin(colormask, 1, 6, 8)); //remove pixels w/ connectivity<6 (noise)
01085   NEW_SKETCH_N(fillinmask2 ,bool,visops::fillin(fillinmask, 2, 3, 8)); //remove pixels w/ connectivity<3 and fill in the others
01086   NEW_SKETCH_N(edgemask ,bool,visops::fillin(fillinmask2, 1, 5, 7)); //remove pixels w/ connectivity=8 (non-edge pixels)
01087   NEW_SKETCH_N(edgemask2 ,bool,visops::non_bounds(edgemask, camFrame_offset)); //remove pixels close to cam_bound
01088 
01089   NEW_SKETCH_N(occluders_floor, bool, (camFrame != uchar(0)) & (camFrame != uchar(floorColor)));
01090   NEW_SKETCH_N(occ_mask ,bool,visops::fillin(occluders_floor, 1, 8, 8)); //remove pixels w/ connectivity<7 (noises)
01091   usint const clear_dist = 15;
01092   Sketch<bool> not_too_close = (visops::mdist(occ_mask) >= clear_dist); 
01093   edgemask2 &= not_too_close; //remove pixels around occuluders
01094   
01095   NEW_SKETCH_N(fatmask ,bool,visops::fillin(edgemask2,2,2,8)); //make the remaining pixels fat
01096   NEW_SKETCH_N(skel,bool,visops::skel(fatmask));
01097   NEW_SKETCH_N(fatskel,bool,visops::fillin(skel,1,2,8));
01098   
01099   vector<Shape<LineData> > wall_bounds = PolygonData::extractPolygonEdges(fatskel,fatmask|occluders_floor);
01100 
01101   // larger offset from the cam frame should be applied to these lines
01102   // since all pixels near cam frame bounds are removed before extracting these lines.
01103   for (vector<Shape<LineData> >::iterator it = wall_bounds.begin();
01104        it != wall_bounds.end(); it++) {
01105     if (((*it)->end1Pt().coordX() < camFrame_offset*2.0 || (*it)->end1Pt().coordX() > xres - camFrame_offset*2.0
01106    || (*it)->end1Pt().coordY() < camFrame_offset*2.0 || (*it)->end1Pt().coordY() > yres - camFrame_offset*2.0)
01107   && (*it)->end1Pt().isValid())
01108       (*it)->end1Pt().setValid(false);
01109     if (((*it)->end2Pt().coordX() < camFrame_offset*2.0 || (*it)->end2Pt().coordX() > xres - camFrame_offset*2.0
01110    || (*it)->end2Pt().coordY() < camFrame_offset*2.0 || (*it)->end2Pt().coordY() > yres - camFrame_offset*2.0)
01111   && (*it)->end2Pt().isValid())
01112       (*it)->end2Pt().setValid(false);
01113   }
01114   
01115   if ( curReq->verbosity & MBVshapesFound )
01116     cout << "Found " << wall_bounds.size() << " wall boundary lines" << endl;
01117   return wall_bounds;
01118 }
01119 
01120 void MapBuilder::getCamBlobs(const Sketch<uchar>& camFrame,
01121            const set<color_index>& colors,
01122            const map<color_index,int>& minBlobAreas,
01123            const map<color_index, BlobData::BlobOrientation_t>& blobOrientations,
01124            const map<color_index,coordinate_t>& assumedBlobHeights) {
01125   if ( colors.empty() )
01126     return;
01127   int const maxblobs = 50;
01128   vector<Shape<BlobData> > result(BlobData::extractBlobs(camFrame, colors, minBlobAreas, blobOrientations, assumedBlobHeights, maxblobs));
01129   if ( curReq->verbosity & MBVshapesFound )
01130     if ( !result.empty() )
01131       cout << "Found " << result.size() << " blobs." << endl;
01132 }
01133 
01134 // The code below grabs blobs directly from the region generator stream, instead of
01135 // calling BlobgData::extractBlobs to do region extraction on camFrame 
01136 void MapBuilder::getCamBlobs(const set<color_index>& colors, int defMinBlobArea) {
01137   // don't check objectColors here because this may also be called by extractors for various marker types
01138   if ( curReq->verbosity & MBVshapeSearch )
01139     cout << "*** Find the blobs ***" << endl;
01140   for (set<color_index>::const_iterator it = colors.begin();
01141        it != colors.end(); it++) {
01142     int const minarea = (curReq->minBlobAreas.find(*it)==curReq->minBlobAreas.end()) ? 
01143       defMinBlobArea : curReq->minBlobAreas[*it];
01144     BlobData::BlobOrientation_t const orient = (curReq->blobOrientations.find(*it)==curReq->blobOrientations.end()) ? 
01145       BlobData::groundplane : curReq->blobOrientations[*it];
01146     coordinate_t const height = (curReq->assumedBlobHeights.find(*it)==curReq->assumedBlobHeights.end()) ? 
01147       0 : curReq->assumedBlobHeights[*it];
01148     vector<Shape<BlobData> > blob_shapes(VRmixin::getBlobsFromRegionGenerator(*it,minarea,orient,height));
01149     if ( curReq->verbosity & MBVshapesFound )
01150       if ( !blob_shapes.empty() )
01151   cout << "Found " << blob_shapes.size() << " "
01152        << ProjectInterface::getColorName(*it) << " region generator blobs." << endl;
01153   }
01154 }
01155 
01156 void MapBuilder::getCamTargets(const Sketch<uchar> &camFrame, const set<color_index>& objectColors, const set<color_index>& occluderColors) const {
01157   vector<Shape<TargetData> > targets;
01158   if (objectColors.empty())
01159     return;
01160   if ( curReq->verbosity & MBVshapeSearch )
01161     cout << "*** Find the targets ***" << endl;
01162   
01163   NEW_SKETCH_N(occluders,bool,visops::zeros(camFrame));
01164   for (set<color_index>::const_iterator it = occluderColors.begin();
01165        it != occluderColors.end(); it++)
01166     occluders |= visops::minArea(visops::colormask(camFrame,*it));
01167   
01168   // assumes multiples of 3 for objectColors (stays on the last color otherwise)
01169   for (set<color_index>::const_iterator it = objectColors.begin();
01170        it != objectColors.end(); it++) {
01171     NEW_SKETCH_N(front_colormask, bool, visops::colormask(camFrame,*it));
01172     it++;
01173     if (it == objectColors.end()) {
01174       it--;
01175     }
01176     NEW_SKETCH_N(back_colormask, bool, visops::colormask(camFrame,*it));
01177     it++;
01178     if (it == objectColors.end()) {
01179       it--;
01180     }
01181     NEW_SKETCH_N(right_colormask, bool, visops::colormask(camFrame,*it));
01182     Shape<TargetData> target = TargetData::extractLineTarget(front_colormask, back_colormask, right_colormask, occluders);
01183     if (target.isValid()) {
01184       targets.insert(targets.end(), target);
01185     }
01186     if ( curReq->verbosity & MBVshapesFound )
01187       cout << "Found " << (target.isValid() ? 1 : 0) << " targets." << endl;
01188   }
01189 }
01190 
01191 vector<Shape<MarkerData> > 
01192 MapBuilder::getCamMarkers(const Sketch<uchar> &camFrame, const set<color_index>& objectColors,
01193         const set<color_index>&, const set<MarkerType_t>& markerTypes) const
01194 {
01195   vector<Shape<MarkerData> > markers;
01196   if (objectColors.empty() || markerTypes.empty())
01197     return markers;
01198 
01199   if ( curReq->verbosity & MBVshapeSearch )
01200     cout << "*** Find the markers ***" << endl;
01201 
01202   /* Skip this and let the individual marker extraction algorithms do masking if they need it
01203   NEW_SKETCH_N(colormask,bool,visops::zeros(camFrame));
01204   for (set<color_index>::const_iterator it = objectColors.begin();
01205        it != objectColors.end(); it++) {
01206     colormask |= visops::colormask(camFrame,*it);
01207   }
01208   NEW_SKETCH_N(masked,uchar,visops::mask(camFrame,colormask));
01209   */
01210 
01211   for (set<MarkerType_t>::const_iterator it = markerTypes.begin();
01212        it != markerTypes.end(); it++) {
01213     vector<Shape<MarkerData> > single_type = MarkerData::extractMarkers(camFrame, *it, *curReq);
01214     markers.insert(markers.end(), single_type.begin(), single_type.end());
01215   }
01216 
01217   if ( curReq->verbosity & MBVshapesFound )
01218     cout << "Found " << markers.size() << " markers." << endl;
01219             
01220   return markers;
01221 }
01222 
01223 void MapBuilder::getCamSiftObjects(const Sketch<uchar> &rawY, const std::string &siftDatabasePath, 
01224            const std::set<std::string> &siftObjectNames) {
01225   if ( siftDatabasePath.empty() ) return;
01226   SiftTekkotsu *matcher = siftMatchers[siftDatabasePath];
01227   if ( matcher == NULL ) {
01228     matcher = new SiftTekkotsu;
01229     matcher->loadFile(siftDatabasePath);
01230     matcher->setParameter("probOfMatch", 0.8);
01231     siftMatchers[siftDatabasePath] = matcher;
01232   }
01233   ImageBuffer buff = matcher->sketchToBuffer(rawY);
01234   vector<SiftMatch*> results;
01235   if ( siftObjectNames.empty() )
01236     matcher->findAllObjectsInImage(buff, results);
01237   else
01238     for ( std::set<std::string>::const_iterator it = siftObjectNames.begin();
01239     it != siftObjectNames.end(); it++ ) {
01240       int id = matcher->getObjectID(*it);
01241       matcher->findObjectInImage(id, buff, results);
01242     };
01243   if ( curReq->verbosity & MBVshapesFound )
01244     cout << "Found " << results.size() << " sift objects." << endl;
01245   for ( vector<SiftMatch*>::const_iterator it = results.begin();
01246   it != results.end(); it++ ) {
01247     NEW_SHAPE(siftobj, SiftData, new SiftData(VRmixin::camShS, *it));
01248   }
01249 }
01250     
01251 void MapBuilder::newSiftMatcher(const std::string &siftDatabasePath) {
01252   if ( siftDatabasePath.empty() ) return;
01253   SiftTekkotsu *matcher = siftMatchers[siftDatabasePath];
01254   if ( matcher == NULL ) {
01255     matcher = new SiftTekkotsu;
01256     // need to check if file exists matcher->loadFile(siftDatabasePath);
01257     matcher->setParameter("probOfMatch", 0.8);
01258     siftMatchers[siftDatabasePath] = matcher;
01259   }
01260 }
01261 
01262 void MapBuilder::getCamAprilTags(const Sketch<uchar> &rawY) {
01263   std::map<std::pair<int,int>,AprilTags::TagFamily*>::const_iterator registryEntry =
01264     AprilTags::TagFamily::tagFamilyRegistry.find(curReq->aprilTagFamily);
01265   if ( registryEntry == AprilTags::TagFamily::tagFamilyRegistry.end() )
01266     return;
01267   AprilTags::TagFamily *tagFamily = registryEntry->second;
01268   std::vector<Shape<AprilTagData> > results = AprilTagData::extractAprilTags(rawY, *tagFamily);
01269   // calculate distance from the camera based on assumed marker height
01270   coordinate_t markerHeight = MapBuilderRequest::defaultMarkerHeight;
01271   for ( std::vector<Shape<AprilTagData> >::iterator it = results.begin();
01272   it != results.end(); it++ ) {
01273     Point c = (*it)->getCentroid();
01274     MarkerData::calculateCameraDistance(c, markerHeight);
01275     (*it)->setCentroid(c);
01276   }
01277 }
01278 
01279 void MapBuilder::saveSiftDatabase(const std::string &siftDatabasePath) {
01280   if ( siftDatabasePath.empty() ) return;
01281   SiftTekkotsu *matcher = siftMatchers[siftDatabasePath];
01282   if ( matcher == NULL )
01283     cout << "Error in saveSiftDatabase: no database named '" << siftDatabasePath << "' has been loaded." << endl;
01284   else {
01285     matcher->saveToFile(siftDatabasePath, false);
01286     cout << "Wrote SIFT database " << siftDatabasePath << endl;
01287   }
01288 }
01289 
01290 void MapBuilder::trainSiftObject(const std::string &siftDatabasePath,
01291          const std::string &objectName, const std::string &modelName) {
01292   NEW_SKETCH_N(rawY, uchar, VRmixin::sketchFromRawY());
01293   trainSiftObject(siftDatabasePath, rawY, objectName, modelName);
01294 }
01295 
01296 void MapBuilder::trainSiftObject(const std::string &siftDatabasePath, const Sketch<uchar> &sketch,
01297          const std::string &objectName, const std::string &modelName) {
01298   if ( siftDatabasePath.empty() ) return;
01299   SiftTekkotsu *matcher = siftMatchers[siftDatabasePath];
01300   if ( matcher == NULL ) {
01301     cout << "Error in trainSiftObject:: no database named '" << siftDatabasePath << "' has been loaded." << endl;
01302     return;
01303   }
01304   ImageBuffer buffer = SiftTekkotsu::sketchToBuffer(sketch);
01305   int id = matcher->getObjectID(objectName);
01306   if ( id == -1 ) {
01307     int new_id = matcher->train_addNewObject(buffer);
01308     matcher->setObjectName(new_id, objectName);
01309   } else {
01310     matcher->train_addToObject(id, buffer);
01311   }
01312 }
01313 
01314 } // namespace
01315 

Tekkotsu v5.1CVS
Generated Sat May 4 06:32:52 2013 by Doxygen 1.6.3