Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

# Modules/ImageProcessor/GT2005ImageProcessor/GT2005GoalRecognizer.h

Go to the documentation of this file.

    00001 /**
    00002 * @file GT2005GoalRecognizer.h
    00003 *
    00004 * @author <a href="mailto:oberlies@sim.tu-darmstadt.de">Tobias Oberlies</a>
    00005 */
    00006
    00007 #ifndef GT2005GoalRecognizer_h_
    00008 #define GT2005GoalRecognizer_h_
    00009
    00010 #include "Modules/ImageProcessor/ImageProcessor.h"
    00011 #include "Modules/ImageProcessor/ImageProcessorTools/ImageProcessorUtilityClasses.h"
    00012 #include "Modules/ImageProcessor/ImageProcessorTools/BresenhamLineScan.h"
    00013 #include "Modules/ImageProcessor/ImageProcessorTools/ColorCorrector.h"
    00014 #include "Tools/Range.h"
    00015
    00016
    00017 /**
    00018  * Macro to handle the stupid problem, that the DEBUG_IMAGE_SET_PIXEL_xxx macros need the
    00019  * debug image identifier at compile time. There are two instances of this class, and one of
    00020  * them uses imageProcessorGoal1 and the other imageProcessorGoal2.
    00021  * @param targetcolor A variable that contains the goal colour at runtime.
    00022  * @param x The x-value of the pixel to be set (at runtime).
    00023  * @param y The y-value of the pixel to be set (at runtime).
    00024  * @param drawcolor Text suitable to complete DEBUG_IMAGE_SET_PIXEL_ at compile time.
    00025  */
    00026 #ifndef NDEBUG
    00027 #define STUPID_DEBUG_IMAGE_SET_PIXEL(targetcolor, x, y, drawcolor) \
    00028   if (targetcolor == yellow) \
    00029   { \
    00030     DEBUG_IMAGE_SET_PIXEL_##drawcolor(imageProcessorGoal1, (x), (y)); \
    00031   } \
    00032   else \
    00033   { \
    00034     DEBUG_IMAGE_SET_PIXEL_##drawcolor(imageProcessorGoal2, (x), (y)); \
    00035   }
    00036
    00037 #define N_STUPID_DEBUG_IMAGE_SET_PIXEL(targetcolor, x, y, drawcolor) \
    00038   if (targetcolor == yellow) \
    00039   { \
    00040     N_SET_COLORED_PIXEL_IN_GRAY_SCALE_IMAGE(goalRecognizerYellow, (x), (y), (drawcolor)); \
    00041   } \
    00042   else \
    00043   { \
    00044     N_SET_COLORED_PIXEL_IN_GRAY_SCALE_IMAGE(goalRecognizerBlue, (x), (y), (drawcolor)); \
    00045   }
    00046
    00047 #else
    00048
    00049 #define STUPID_DEBUG_IMAGE_SET_PIXEL(targetcolor, x, y, drawcolor) ;
    00050 #define N_STUPID_DEBUG_IMAGE_SET_PIXEL(targetcolor, x, y, drawcolor) ;
    00051
    00052 #endif
    00053
    00054 /**
    00055  * @class GT2005GoalRecognizer
    00056  *
    00057  * TODO: Write Summary
    00058  *
    00059  * @author <a href="mailto:oberlies@sim.tu-darmstadt.de"Tobias Oberlies</a>
    00060  */
    00061 class GT2005GoalRecognizer
    00062 {
    00063 private: // nested classes and enums
    00064
    00065   /**
    00066    * TODO: Write Summary
    00067    */
    00068   enum EdgeType
    00069   {
    00070     none = 0,
    00071     edge,
    00072     deviationWithoutEdge,
    00073     imageBorder       // Must be the last one
    00074   };
    00075
    00076   /**
    00077    * TODO: Write Summary
    00078    */
    00079   class EdgeDetector
    00080   {
    00081   public:
    00082     /** Constructor. */
    00083     EdgeDetector(colorClass target);
    00084     EdgeDetector(colorClass target, Vector3<int> referenceColor, int referenceColorBase);
    00085
    00086     /**
    00087      * Investigates the next pixel and reports, wether an edge, or repeated deviation of
    00088      * the pixel colours (without the indications of an edge) have been detected. It is
    00089      * assumed that edges are quite sharp and that the color beyond the edge is
    00090      * significantly different to the goal colour. In case of a blurred edge or a steady
    00091      * color gradient the method will (eventually) return deviationWithoutEdge.
    00092      */
    00093     inline EdgeType inspectPixel(const Vector2<int>& point, int index, const colorClass& colorCls, const Vector3<int>& color)
    00094     {
    00095       // Check, if pixel is in target colour class
    00096       if (colorCls == targetColorClass)
    00097       {
    00098         // Store as possible edge point
    00099         lastPointInClass = point;
    00100         lastPointInClassIndex = index;
    00101         connectedToClass = true;
    00102
    00103         // Update the reference colour
    00104         // The reference colour is an average of the colour of the last pixels. If
    00105         // this method has been called on less than eight pixels, the average is
    00106         // equally weighted. Otherwise recent pixels have a higher weight (1/8 for
    00107         // the last one etc.)
    00108         referenceColor = (referenceColor * referenceColorBase + color) / (referenceColorBase+1);
    00109         if (referenceColorBase < 7)
    00110           ++referenceColorBase;
    00111
    00112         // Decrease deviation counter (since this pixel has no deviation)
    00113         deviationCounter = deviationCounter<=4 ? 0 : deviationCounter-4;
    00114
    00115         // Reset edge counter (assuming no pixels in the goal color class beyond the edge)
    00116         edgeCounter = 0;
    00117       }
    00118       else if (colorCls != noColor && colorCls != ignoredClassification && colorCls != gray)
    00119       {
    00120         // Pixel is classified as a different colour. Take this as an indication for
    00121         // an edge.
    00122         ++edgeCounter;
    00123         deviationCounter = deviationCounter + 2;
    00124
    00125         // From now on don't use pixels with tolerable (but non-zero) pixels as
    00126         // lastPointInClass, because they're likely to be noise
    00127         connectedToClass = false;
    00128
    00129         // Check whether an edge or excessive deviation have been detected
    00130         if (edgeCounter >= 3)
    00131           return edge;
    00132         else if (deviationCounter >= 10)
    00133           return deviationWithoutEdge;
    00134
    00135       }
    00136       else
    00137       {
    00138         // Pixel is not classified in any color class, so calculate the distance to
    00139         // the reference color for further investigations
    00140         int dist = distance(color);
    00141
    00142         // Update deviation and edge counters
    00143         if (dist < classThreshold)
    00144         {
    00145           // This pixel very likely to be part of the goal.
    00146           if (edgeCounter > 0)
    00147             --edgeCounter;
    00148           ++deviationCounter;
    00149
    00150           // Store as possible edge point if there haven't been any (strong)
    00151           // indications for an edge since the last pixel in the target class
    00152           if (connectedToClass)
    00153           {
    00154             lastPointInClass = point;
    00155             lastPointInClassIndex = index;
    00156           }
    00157         }
    00158         else if(dist < edgeThreshold)
    00159         {
    00160           // This pixel is neither likely to be part of the goal, nor to be bejond
    00161           // the edge. Since we're looking for a reasonably sharp edge, only few
    00162           // pixels in this distance range are expected to occur.
    00163           deviationCounter = deviationCounter + 2;
    00164           connectedToClass = false;
    00165         }
    00166         else
    00167         {
    00168           // This pixel is likely to be beyond the edge of the goal. In this case
    00169           // the edge counter is increased more than the deviation counter (with
    00170           // respect to their trigger thresholds 3 and 10). Hence in case of a
    00171           // clear edge, the edge will trigger before the deviation.
    00172           ++edgeCounter;
    00173           deviationCounter = deviationCounter + 2;
    00174           connectedToClass = false;
    00175         }
    00176
    00177         // Check whether an edge or excessive deviation have been detected
    00178         if (edgeCounter >= 3)
    00179           return edge;
    00180         else if (deviationCounter >= 10)
    00181           return deviationWithoutEdge;
    00182       }
    00183
    00184       return none;
    00185     }
    00186
    00187     /**
    00188      * Returns the last point in or near the target colour class. If inspectPixel
    00189      * returned the value edge the returned point is located just before the edge.
    00190      */
    00191     inline Vector2<int> getLastPointInClass()
    00192     {
    00193       return lastPointInClass;
    00194     };
    00195
    00196     /**
    00197      * Similar to getLastPointInClass().
    00198      */
    00199     inline void getLastPointInClass(Vector2<int>& point)
    00200     {
    00201       point = lastPointInClass;
    00202     }
    00203
    00204     /**
    00205      * Similar to getLastPointInClass(), but also returns the index of the
    00206      * lastPointInClass.
    00207      */
    00208     inline void getLastPointInClass(Vector2<int>& point, int& index)
    00209     {
    00210       point = lastPointInClass;
    00211       index = lastPointInClassIndex;
    00212     }
    00213
    00214     /**
    00215      * Similar to getLastPointInClass(), but returns the index of the lastPointInClass
    00216      * instead of the poinst itself.
    00217      */
    00218     inline int getLastIndexInClass()
    00219     {
    00220       return lastPointInClassIndex;
    00221     }
    00222
    00223     /**
    00224      * Resets the edge detection state while keeping the reference colour.
    00225      * @param defaultLast The value returned by getLastPointInClass if none of the
    00226      * inspected pixels is in or near the target colour class.
    00227      * @param connectedToClass Assume that the first pixel that is inspected is
    00228      * connected to the target colour class.
    00229      */
    00230     void clear(const Vector2<int>& defaultLast, bool connectedToClass)
    00231     {
    00232       // Reset the counters
    00233       deviationCounter = 0;
    00234       edgeCounter = 0;
    00235
    00236       // Set provided default values
    00237       this->lastPointInClass = defaultLast;
    00238       this->lastPointInClassIndex = -1;
    00239       this->connectedToClass = connectedToClass;
    00240     }
    00241
    00242     /**
    00243      * Reset the edge detection state while keeping the reference colour.
    00244      */
    00245     void clear()
    00246     {
    00247       // Reset the counters
    00248       deviationCounter = 0;
    00249       edgeCounter = 0;
    00250
    00251       // Keep the lastPointInClass value assuming that the scan begins there (hence
    00252       // first pixel inspected is connected to the class)
    00253       lastPointInClassIndex = -1;
    00254       connectedToClass = true;
    00255     }
    00256
    00257     /**
    00258      * Reset the reference colour and the edge detector state.
    00259      */
    00260     void reset()
    00261     {
    00262       // Reset the reference colour
    00263       referenceColorBase = 0;
    00264
    00265       // Reset the counters
    00266       deviationCounter = 0;
    00267       edgeCounter = 0;
    00268
    00269       // Keep the lastPointInClass value assuming that the scan begins there (hence
    00270       // first pixel inspected is connected to the class)
    00271       lastPointInClassIndex = -1;
    00272       connectedToClass = false;
    00273     }
    00274
    00275     /**
    00276      * Sets the reference colour.
    00277      */
    00278     void setReferenceColor(const Vector3<int>& color, int referenceColorBase)
    00279     {
    00280       this->referenceColor = color;
    00281       this->referenceColorBase = referenceColorBase;
    00282     };
    00283
    00284     /**
    00285      * Returns the reference colour.
    00286      */
    00287     void getReferenceColor(Vector3<int>& result)
    00288     {
    00289       result = this->referenceColor;
    00290     };
    00291
    00292   private: // methods
    00293
    00294     /**
    00295      * The inverse covariance matrix of the goal color variation (needed for the
    00296      * Mahalalanobis distance).
    00297      * @param goalColour the goal colour class
    00298      * TODO: load these values from a file
    00299      **/
    00300     static Matrix3x3<int> inverseCovarianceMatrix(colorClass goalColor);
    00301
    00302     /**
    00303      * Calculates the Mahalanobis distance of the given colour to the reference colour.
    00304      */
    00305     inline int distance(const Vector3<int>& color)
    00306     {
    00307       // Calculate difference in each channel
    00308       Vector3<int> difference = color - referenceColor;
    00309
    00310       // Weigh with respect to usual variation
    00311       return (invCovar * difference) * difference;  // in MATLAB syntax: difference'*invCovar*difference
    00312     }
    00313
    00314   private: // class members
    00315
    00316     /** The maximum distance from the reference colour that is tolerated for a pixel to
    00317         be considered in the target colour class. */
    00318     static const float classThreshold;
    00319
    00320     /** The minimum distance from the reference colour that pixels on the other side of
    00321         an edge need to have. This value is greater than classThreshold. */
    00322     static const float edgeThreshold;
    00323
    00324
    00325   private: // members
    00326
    00327     /** The colour class of the goal. */
    00328     const colorClass targetColorClass;
    00329
    00330     /** Colour class that is not considered as an indication for an edge. */
    00331     const colorClass ignoredClassification;
    00332
    00333     /** The inverse covariance matrix of the goal colour. It is used to calculate the
    00334         Mahalanobis distance between the reference colour and the current colour for
    00335         pixels in no colour class (or an ignored one). */
    00336     const Matrix3x3<int> invCovar;
    00337
    00338     /** The reference colour for distance calculations. It is an average of recent
    00339         pixels that were classified in the target colour class. */
    00340     Vector3<int> referenceColor;
    00341     int referenceColorBase;
    00342
    00343     /** Counter to detect the end of an area in target colour without an edge. */
    00344     int deviationCounter;
    00345
    00346     /** Counter to detect an edge. */
    00347     int edgeCounter;
    00348
    00349
    00350     /** Last pixel in the target colour class (or within tolerable distance). */
    00351     Vector2<int> lastPointInClass;
    00352
    00353     /** The index of the last point in/near the target colour class. By default, this
    00354         value is -1, so it can be used to check whether any pixel was considered to be
    00355         in/near the target colour class. */
    00356     int lastPointInClassIndex;
    00357
    00358     /** Current pixel is connected via to a pixel in the target colour class only via
    00359         pixels within tolerable distance, i.e. there hasn't been a pixel with a distance
    00360         greater than the classDistance threshold since the last pixel classified in the
    00361         target class (without distance). As long as this variable is true, points with
    00362         tolerable distance will be stored as lastPointInClass. */
    00363     bool connectedToClass;
    00364
    00365   }; // class GT2005GoalRecognizer::EdgeDetector
    00366
    00367
    00368   /**
    00369    * TODO: Write Summary
    00370    */
    00371   struct EdgePointList
    00372   {
    00373   private: // members
    00374
    00375     // Arrays to store the edge points and their type
    00376     enum {maxPoints = 20};
    00377     Vector2<int> edgePoint[maxPoints];
    00378     GT2005GoalRecognizer::EdgeType edgeType[maxPoints];
    00379
    00380     // Current number of edge points
    00381     int numberOfPoints;
    00382
    00383     // Number of edge points of a certain type
    00384     int numberOfType[imageBorder+1];
    00385
    00386
    00387   public: // methods
    00388
    00389     /** Constructs an empty list. */
    00390     EdgePointList()
    00391       : numberOfPoints(0)
    00392     {
    00393       for (int i=0; i<=imageBorder; ++i)
    00394         numberOfType[i] = 0;
    00395     }
    00396
    00397     /** Add an edge point to the list. */
    00398     inline void add(Vector2<int>& point, GT2005GoalRecognizer::EdgeType type)
    00399     {
    00400       // Ignore if too many points
    00401       if (numberOfPoints>=maxPoints)
    00402         return;
    00403
    00404       // Store point and increase counters
    00405       edgePoint[numberOfPoints] = point;
    00406       edgeType[numberOfPoints] = type;
    00407       ++numberOfPoints;
    00408       ++numberOfType[type];
    00409     }
    00410
    00411     /** Returns the current size of the list. */
    00412     inline int size()
    00413     {
    00414       return numberOfPoints;
    00415     }
    00416
    00417     /** Returns the number of edge points of a certain type. */
    00418     inline int getCount(GT2005GoalRecognizer::EdgeType type)
    00419     {
    00420       return numberOfType[type];
    00421     }
    00422
    00423     /** Returns an element of the list. */
    00424     inline Vector2<int>& operator[](int index)
    00425     {
    00426       return edgePoint[index];
    00427     }
    00428
    00429     /** Returns the type of an edge points. */
    00430     inline GT2005GoalRecognizer::EdgeType getType(int index)
    00431     {
    00432       return edgeType[index];
    00433     }
    00434   }; // struct EdgePointList
    00435
    00436
    00437   /**
    00438    *
    00439    */
    00440   struct Goalpost
    00441   {
    00442     /** Constructs an empty goalpost hypothesis. */
    00443     Goalpost() {};
    00444
    00445     /** Top endpoints of the goalposts. Might not be aligned with the edge
    00446         horizontally. */
    00447     Vector2<int> topPoint;
    00448     Vector2<double> topPointH;
    00449     bool topPointInImage; // FILL
    00450
    00451     /** Bottom endpoints of the goalpost. Might not be aligned with the edge
    00452         horizontally. */
    00453     Vector2<int> bottomPoint;
    00454     Vector2<double> bottomPointH;
    00455
    00456     /** Goalpost is standing on the green field. */
    00457     bool onGreen;
    00458
    00459     /** Height and height not conting the image border. */
    00460     int height;
    00461     int visibleHeight; // FILL
    00462
    00463     /** A point aligned with the edge of the goalpost. */
    00464     Vector2<int> edgePoint;
    00465     Vector2<double> edgePointH;
    00466
    00467     /** Number of strong and weak edge points detected. */
    00468     int strongEdgeCount;
    00469     int weakEdgeCount;
    00470
    00471     /** Straightness */
    00472     /** Slope */
    00473
    00474     /** Scans towards the edge detected unbound goal-coloured areas. This often happens
    00475         when the goal is detected again after the scan along the cross bar failed. If
    00476         this value is set to true, all other values are invalid.*/
    00477     bool nonExistant;
    00478
    00479     /** Average colour of some pixels near the goalpost. This value is used to
    00480         detect the free part of goal (is done in the interpretation after the entire
    00481         image was scanned) or straightnes checks. */
    00482     Vector3<int> color;
    00483
    00484   }; // struct Goalpost
    00485
    00486   /**
    00487    * TODO: Write Summary
    00488    */
    00489   struct GoalHypothesis : public Streamable
    00490   {
    00491     /** Constructs an empty GoalHypothesis with default values. */
    00492     GoalHypothesis(){};
    00493
    00494     /** The left and right goalpost. */
    00495     Goalpost goalpost[2];
    00496
    00497     /** Right end of cross bar. */
    00498     Vector2<int> crossBarEndpoint;
    00499     Vector2<double> crossBarEndpointH;
    00500
    00501     /** Goal width. */
    00502     int width;
    00503
    00504     /** Goal height (the maximum of the goalpost heights). */
    00505     int height;
    00506
    00507     /** Cross bar is in image -> if true, height cannot be determined. */
    00508     bool crossBarInImage;
    00509     // TODO: set top left point to begin of visible crossbar (if higher)
    00510
    00511     /** There is green below the goal hypthesis. */
    00512     bool onGreen;
    00513
    00514
    00515     /** Streaming operators */
    00516     virtual void serialize(In* in, Out* out)
    00517     {
    00518       STREAM_REGISTER_BEGIN();
    00519       STREAM(height);
    00520       STREAM(width);
    00521       STREAM(crossBarInImage);
    00522       STREAM(goalpost[0].height);
    00523       STREAM(goalpost[0].onGreen);
    00524       //STREAM(goalpost[0].strongEdgeCount);
    00525       //STREAM(goalpost[0].weakEdgeCount);
    00526       STREAM(goalpost[0].visibleHeight);
    00527       STREAM(goalpost[0].nonExistant);
    00528       STREAM(goalpost[1].height);
    00529       STREAM(goalpost[1].onGreen);
    00530       //STREAM(goalpost[1].strongEdgeCount);
    00531       //STREAM(goalpost[1].weakEdgeCount);
    00532       STREAM(goalpost[1].visibleHeight);
    00533       STREAM(goalpost[1].nonExistant);
    00534       STREAM_REGISTER_FINISH();
    00535     }
    00536
    00537   }; // struct GoalHypothesis
    00538
    00539
    00540   /**
    00541    * TODO: Write Summary
    00542    */
    00543   enum ImageBorderSide
    00544   {
    00545     topBorder = 0,
    00546     leftBorder = 1,
    00547     bottomBorder = 2,
    00548     rightBorder = 3,
    00549     noBorder
    00550   };
    00551
    00552   enum FreeSide
    00553   {
    00554     noFreeSide,
    00555     leftSide,
    00556     rightSide,
    00557     bothSides
    00558   };
    00559
    00560
    00561 // class GT2005GoalRecognizer
    00562
    00563 public:
    00564
    00565   /**
    00566    * Initializes the GT2005GoadRecognizer.
    00567    */
    00568   GT2005GoalRecognizer(colorClass color, const ImageProcessorInterfaces& interfaces, const ColorCorrector& colorCorrector, const ImageInfo& horizonInfo);
    00569
    00570   /**
    00571    * Destructor
    00572    */
    00573   ~GT2005GoalRecognizer() {};
    00574
    00575   /**
    00576    * Call this method on every new image before the first call of inspect pixel.
    00577    */
    00578   void notifyAboutNewImage();
    00579
    00580   /**
    00581    * Call this method to use seen flags (from the LandmarkPercept object) as areas
    00582    * where not to look for goals.
    00583    */
    00584   void notifyAboutFlags();
    00585
    00586   /**
    00587    *
    00588    */
    00589   void notifyAboutFinish();
    00590
    00591
    00592   inline void notifyAboutNewScanline(const Vector2<int>& scanLineStart)
    00593   {
    00594     // Reset counter for detection of goal candidates
    00595     detectionCounter = 0;
    00596
    00597     // Calculate how many pixels on this scanline should be skipped in order to prevent
    00598     // previously recognized goals to be found again
    00599     calculateLockedPixels(scanLineStart);
    00600   };
    00601
    00602
    00603   inline void inspectPixel(const Vector2<int>& point, const colorClass& color)
    00604   {
    00605     // Check if this pixel is inside the area that has been analysed before
    00606     if (--lockedPixels < 0)
    00607     {
    00608
    00609       // Check if pixel has target colour
    00610       if (color == goalColor)
    00611       {
    00612         // Increase counter
    00613         ++detectionCounter;
    00614
    00615         // When counter reaches 3, start the actual goal detection routine
    00616         if (detectionCounter >= 4)
    00617         {
    00618           STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, point.x, point.y, BLUE);
    00619           N_STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, point.x, point.y, goalColor);
    00620
    00621           if (true)//inspectNeighbourhood(point))
    00622           {
    00623             // Analyze this blob
    00624             analyzeGoal(point);
    00625
    00626             // Recalculate the number of locked pixels on this scanline
    00627             recalculateLockedPixels(point);
    00628
    00629             //testDone = true;
    00630             //testNow = false;
    00631             detectionCounter = 0;
    00632           }
    00633           else
    00634           {
    00635             // Take into account that there were only few goal-coloured pixels in
    00636             // the neighbourhood of the current pixel
    00637             detectionCounter = 1;
    00638           }
    00639         }
    00640         else
    00641           STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, point.x, point.y, BLUE);
    00642           N_STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, point.x, point.y, goalColor);
    00643       }
    00644       else
    00645       {
    00646         // Decrease counter
    00647         --detectionCounter;
    00648         if (detectionCounter<0)
    00649           detectionCounter = 0;
    00650         STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, point.x, point.y, GRAY);
    00651         N_STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, point.x, point.y, gray);
    00652       }
    00653     }
    00654   };
    00655
    00656
    00657 private: // methods
    00658
    00659   /** */
    00660   bool inspectNeighbourhood(const Vector2<int>& point);
    00661
    00662   /**
    00663    * point copied on purpose
    00664    */
    00665   void analyzeGoal(Vector2<int> startPoint);
    00666
    00667
    00668   /**
    00669    * direction of edge normalized!!
    00670    * imageBorderScanned: to prevent that one image border is scanned again, never necessary
    00671    * in correct goal case
    00672    */
    00673   void analyzeGoalpost(GT2005GoalRecognizer::EdgeDetector* detector,
    00674                        const Vector2<double>& directionOfEdge,
    00675                        bool leftGoalpost,
    00676                        Vector2<int>& focus,
    00677                        GT2005GoalRecognizer::Goalpost& result,
    00678                        bool* imageBorderScanned);
    00679
    00680   /** */
    00681   void scanCrossBar(GT2005GoalRecognizer::EdgeDetector* detector,
    00682                     Vector2<int>& focus,
    00683                     GT2005GoalRecognizer::GoalHypothesis& result,
    00684                     bool* imageBorderScanned);
    00685
    00686   /**
    00687    * scans first from focus+scanLineOffset, using detector[0]!!
    00688    */
    00689   GT2005GoalRecognizer::EdgeType detectEdgeTwice(GT2005GoalRecognizer::EdgeDetector* detector,
    00690                                                  Vector2<int>& focus,
    00691                                                  BresenhamLineScan& scanLine,
    00692                                                  const Vector2<int>& scanLineOffset,
    00693                                                  int maxScanLength,
    00694                                                  EdgePointList& edgePoints);
    00695
    00696
    00697   /** */
    00698   GT2005GoalRecognizer::EdgeType detectEdge(GT2005GoalRecognizer::EdgeDetector& detector,
    00699                                             Vector2<int>& focus,
    00700                                             BresenhamLineScan& direction,
    00701                                             int maxScanLength,
    00702                                             int& pixelsUntilEdge);
    00703
    00704   /** */
    00705   GT2005GoalRecognizer::EdgeType scanAlongLine(GT2005GoalRecognizer::EdgeDetector* detector,
    00706                                                Vector2<int>& focus,
    00707                                                BresenhamLineScan& scanLine,
    00708                                                const Vector2<int>& scanLineOffset,
    00709                                                int maximumScan,
    00710                                                int& pixelsScanned);
    00711
    00712   /**
    00713    * case noBorder mustn't be ignored -> scanning hasn't been done before or doesn't make sense
    00714    */
    00715   GT2005GoalRecognizer::ImageBorderSide scanOnImageBorder(GT2005GoalRecognizer::EdgeDetector* detector,
    00716                                                           Vector2<int>& focus,
    00717                                                           const Vector2<double>& targetDirection,
    00718                                                           int maxBorderDistance,
    00719                                                           bool* scannedSides);
    00720
    00721   /**
    00722    * direction of edge normalized!!
    00723    * bottomPoint copied on purpose!!
    00724    */
    00725   bool detectGreenBelowGoalpost(Vector2<int> bottomPoint,
    00726                                 const Vector2<double>& directionOfEdge,
    00727                                 int goalpostHeight);
    00728
    00729   /**
    00730    * Returns true if the point is within one pixel distance to the image border.
    00731    */
    00732   inline bool nearImageBorder(const Vector2<int>& point, int maxDistance)
    00733   {
    00734     return point.x <= maxDistance ||
    00735            point.y <= maxDistance ||
    00736            point.x >= horizonInfo.maxImageCoordinates.x-maxDistance ||
    00737            point.y >= horizonInfo.maxImageCoordinates.y-maxDistance;
    00738   }
    00739
    00740   /** */
    00741   void mergeFragments(bool* deletedHypothesises);
    00742
    00743
    00744   /** */
    00745   void interpretResults(GT2005GoalRecognizer::EdgeDetector* detector, bool* deletedHypothesises);
    00746
    00747   /** */
    00748   void publishResults(const GT2005GoalRecognizer::GoalHypothesis& goal,
    00749                       const bool* visibleGoalpost,
    00750                       const GT2005GoalRecognizer::FreeSide freeSide,
    00751                       const Vector2<int>& endpointOfFreePart,
    00752                       const bool* freePartOnImageBorder);
    00753
    00754
    00755   /** */
    00756   GT2005GoalRecognizer::FreeSide detectFreePartOfGoal(GT2005GoalRecognizer::EdgeDetector* detector,
    00757                                                       const GT2005GoalRecognizer::GoalHypothesis& goal,
    00758                                                       int& freeWidth,
    00759                                                       Vector2<int>& otherSide,
    00760                                                       bool* onImageBorder);
    00761
    00762   /** */
    00763   void lockArea(double x, double y, bool onGreen);
    00764
    00765   /**
    00766    * pop lock, chekc flag intersection, calculate locked pixels
    00767    */
    00768   void calculateLockedPixels(const Vector2<int>& scanLineStart);
    00769
    00770   /**
    00771    * Recalculates the number of locked pixels after a goal has been detected. The scan
    00772    * will continue on the same scanline where it was before, so in this case it is not
    00773    * necessary to manage (i.e. remove) locked areas and to check if the scanline is
    00774    * near the flags.
    00775    */
    00776   void recalculateLockedPixels(const Vector2<int>& currentPoint);
    00777
    00778
    00779 private: // members
    00780
    00781   /** The colour class of the goal to be detected by this object. */
    00782   const colorClass goalColor;
    00783
    00784   /** Collection of references to objects that are needed by this object, e.g. the current
    00785       image, objects used to store the processing results, etc. */
    00786   const ImageProcessorInterfaces& interfaces;
    00787
    00788   /** Object containing useful information related to the current horizon. */
    00789   const ImageInfo& horizonInfo;
    00790
    00791   /** Reference to the color correction object. */
    00792   const ColorCorrector& colorCorrector;
    00793
    00794
    00795   /** Counter to detect accumulations of goal-coloured pixels. Used by the method
    00796       inspectPixel. */
    00797   int detectionCounter;
    00798
    00799   /** Number of pixels that will not be inspected, because they are inside previously
    00800       analysed areas. */
    00801   int lockedPixels;
    00802
    00803   /** Stack containing the locked area from previous goal analysises. This is to prevent
    00804       that goal-coloured blobs are analysed twice. With inspectPixel being called on pixel
    00805       from left to right and top to bottom, it is sufficient to store the bottom left point
    00806       of the locked areas. */
    00807   //TODO: There is a rare case when this doesn't work as intended (i.e. a goal is scannede 3 or more times) -> search the problem
    00808   enum {lockAreaStackSize = 2};
    00809   Vector2<double> lockAreaStack[lockAreaStackSize];
    00810   int lockAreaCount;
    00811
    00812   /** HY-Ranges where flags have been detected. Scanlines that are within that range are
    00813       not scanned at all to avoid that goals are detected within flags. */
    00814   enum {maxFlagLocks = 3};
    00815   Range<double> flagLock[maxFlagLocks];
    00816   int flagLockCount;
    00817   unsigned long frameNumberImage, frameNumberFlags;
    00818
    00819
    00820   /** Detected goal hypothesises. */
    00821   enum {maxHypothesises = 5};
    00822   GoalHypothesis hypothesis[maxHypothesises];
    00823   int hypothesisCount;
    00824
    00825   /** Declare raster image which will take debug information.
    00826       NB: due to the f**king macros, I have to declare both images, even though only need
    00827       one per instance. */
    00828   DECLARE_DEBUG_IMAGE(imageProcessorGoal1);
    00829   DECLARE_DEBUG_IMAGE(imageProcessorGoal2);
    00830   N_DECLARE_DEBUG_GRAY_SCALE_IMAGE(goalRecognizerYellow);
    00831   N_DECLARE_DEBUG_GRAY_SCALE_IMAGE(goalRecognizerBlue);
    00832
    00833 };
    00834 #endif // GT2005GoalRecognizer