void GoalDetection::performSanityChecks(const Fovea &fovea,
                                        const VisionFrame &frame,
                                        std::vector<BBox> &regions) {
   std::vector<BBox> candidates (regions);
   regions.clear();
   std::vector<BBox>::iterator it;
   for (it = candidates.begin(); it != candidates.end(); ++it) {

      // Check that the middle and the bottom 75% for edges
      // Only need 1 strong edge in each to be good

      int middle = (it->a.y() + it->b.y()) / 2;
      int bottom = it->a.y() + (it->b.y() - it->a.y()) * 0.75;
      bool keepM = false;
      bool keepB = false;
      for (int col = it->a.x(); col <= it->b.x(); ++col) {
         
         // Check middle
         Point edge = fovea.edge(col, middle);
         float magnitude = (edge.x() * edge.x()) + (edge.y() * edge.y());
         if (magnitude > MIN_EDGE_THRESHOLD) keepM = true;

         // Check bottom
         edge = fovea.edge(col, bottom);
         magnitude = (edge.x() * edge.x()) + (edge.y() * edge.y());
         if (magnitude > MIN_EDGE_THRESHOLD) keepB = true;
      }
      if (!keepM) {
         //std::cout << "throwing away since no keepM" << std::endl;
         continue              ;
      }
      if (!keepB) {
         //std::cout << "throwing away since no keepB" << std::endl;
         continue;
      }

      // Check % of colour in goal post - ie compare length and colour
      float length = it->b.y() - it->a.y();
      int centre = (it->a.x() + it->b.x()) / 2;
      float numColourPixels = 0;
      for (int row = it->a.y(); row < it->b.y(); ++row) {
         if (fovea.colour(centre, row) == cGOAL_YELLOW) {
            ++numColourPixels;
         }
      }

      if ((numColourPixels / length) < COLOUR_RATIO_THRESHOLD) {
         //std::cout << "throwing away since not enough colour" << std::endl;
         continue;
      }

      // Check bottom of goal post is below field edge
      Point fieldEdge = fovea.mapFoveaToImage(Point(centre, 0));
      int fieldEdgeY = 0;
      if (fovea.top) {
         fieldEdgeY = frame.topStartScanCoords[fieldEdge.x()];
      } else {
         fieldEdgeY = frame.botStartScanCoords[fieldEdge.x()];
      }
      fieldEdge.y() = std::max(fieldEdge.y(), fieldEdgeY);
      fieldEdge = fovea.mapImageToFovea(fieldEdge);

      if (fieldEdge.y() > it->b.y()) {
         //std::cout << "throwing away since above field edge" << std::endl;
         continue;
      }

      regions.push_back(*it);
   }

   // If more than 2 goals, things have gone wrong, so panic
   if (regions.size() > 2) regions.clear();
}
void GoalDetection::findGoals(
         VisionFrame    &frame,
         const Fovea    &fovea, 
         unsigned int   *seed)
{

   goalFoveas.clear();
   std::vector<PostInfo> posts;

   // Find candidate regions to look for goals
   std::vector<BBox> regions;
   findCandidateRegions(fovea, regions);

   // Sanity check goals to eliminate crappy ones
   performSanityChecks(fovea, frame, regions);

   // Convert into goal posts
   std::vector<BBox>::const_iterator it;
   for (it = regions.begin(); it != regions.end(); ++it) {

      PostInfo p;
      BBox bb (fovea.mapFoveaToImage(it->a), fovea.mapFoveaToImage(it->b));

      // If we are in the top camera, ensure we don't round the BBox into the bottom camera
      if (fovea.top && bb.b.y() == TOP_IMAGE_ROWS) {
         bb.b.y() = (TOP_IMAGE_ROWS-1);
      }

      // Calculate best distance to goal - also fine tunes the BBox
      // Fine tunes and sets the BBox
      // Sets rr, kDistance, wDistance, trustDistance on variable p
      RRCoord rr = findDistanceToPost(frame, fovea, bb, regions.size(), p);
      //if (rr.distance() > 2000) p.trustDistance = false;

      // Work out left and right goals
      // Sets type and dir
      determineLeftRightPost(fovea, regions, *it, p);
      
      // Save goal post
      posts.push_back(p);
   }

   // Sanity check the LHS / RHS of goals
   if (posts.size() == 2) {
      PostInfo pLeft, pRight;
      if (posts[0].type == PostInfo::pLeft) {
         pLeft  = posts[0];
         pRight = posts[1];
      } else {
         pLeft  = posts[1];
         pRight = posts[0];
      }

      if (pLeft.dir == PostInfo::pToLeftOf &&
          pLeft.rr.distance() >= pRight.rr.distance()) {
         posts[0].dir = PostInfo::pUnknown;
         posts[1].dir = PostInfo::pUnknown;
      } else if (pLeft.dir == PostInfo::pToRightOf &&
          pRight.rr.distance() >= pLeft.rr.distance()) {
         posts[0].dir = PostInfo::pUnknown;
         posts[1].dir = PostInfo::pUnknown;
      }
   }

   // Merge top and bottom cameras
   const float headingThreshold = DEG2RAD(7);
   if (frame.posts.empty()) {
      frame.posts = posts;
   } else if (fovea.top && posts.size() > 0 && frame.posts.size() == 1) { 
      for (unsigned int i = 0; i < posts.size(); i++) {
         if (abs(posts[i].rr.heading() - frame.posts[0].rr.heading())
               > headingThreshold) {
            frame.posts.push_back(posts[i]);
         }
      }

      // Ensure if we have 2 posts, they are left and right
      if (frame.posts.size() == 2) {
         if (frame.posts[0].rr.heading() < frame.posts[1].rr.heading()) {
            frame.posts[0].type = PostInfo::pRight;
            frame.posts[1].type = PostInfo::pLeft;
         } else {
            frame.posts[0].type = PostInfo::pLeft;
            frame.posts[1].type = PostInfo::pRight;
         }
      }
   }
}