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

Modules/ImageProcessor/GT2004ImageProcessor/GT2004BeaconDetector.cpp

Go to the documentation of this file.
00001 /**
00002 * @file GT2004BeaconDetector.cpp
00003 * 
00004 * Implementation of class GT2004BeaconDetector.
00005 *
00006 * @author <a href="mailto:timlaue@tzi.de">Tim Laue</a>
00007 * @author <a href="mailto:walter.nistico@uni-dortmund.de">Walter Nistico</a>
00008 */ 
00009 
00010 
00011 #include "Representations/Perception/Image.h"
00012 #ifdef CT32K_LAYOUT
00013 #include "Representations/Perception/ColorTable32K.h"
00014 #else
00015 #include "Representations/Perception/ColorTable64.h"
00016 #endif
00017 #include "Representations/Perception/CameraMatrix.h"
00018 #include "Representations/Perception/LandmarksPercept.h"
00019 #include "Tools/FieldDimensions.h"
00020 #include "Tools/RingBuffer.h"
00021 #include "Tools/Math/Vector2.h"
00022 #include "Modules/ImageProcessor/ImageProcessorTools/ColorCorrector.h"
00023 #include "GT2004ImageProcessorTools.h"
00024 #include "GT2004BeaconDetector.h"
00025 
00026 
00027 const int Y(0),
00028           U(cameraResolutionWidth_ERS7),     /* Relative offset of U component. */
00029           V(2 * cameraResolutionWidth_ERS7); /* Relative offset of V component. */
00030 
00031 
00032 GT2004BeaconDetector::GT2004BeaconDetector(const Image& image, 
00033                        const CameraMatrix& cameraMatrix,
00034                        const CameraMatrix& prevCameraMatrix,
00035                        const ImageInfo& imageInf,
00036                        const ColorTable& colorTable, 
00037                        const ColorCorrector& colorCorrector,
00038                        LandmarksPercept& landmarksPercept):
00039                        image(image), cameraMatrix(cameraMatrix), 
00040                        prevCameraMatrix(prevCameraMatrix),
00041                        imageInf(imageInf), colorTable(colorTable), 
00042                        colorCorrector(colorCorrector),
00043                        landmarksPercept(landmarksPercept),
00044                        flagSpecialist(colorCorrector),
00045                        horizontalBaseOffset(1.6), // was 1.5
00046                        numOfHorizontalScanLineAbove(10), 
00047                        numOfHorizontalScanLineBelow(3),
00048                        horizontalOffsetModifier(0.9),  // was 0.7
00049                        clusteringDistanceTolerance(6), 
00050                        minPinkRunLength(2), 
00051                        clusteringAspectRatio(1.5),
00052                        projectionAspectRatio(0.4),
00053                        minFlagConfidence(0.5),
00054                        edgeScanDepth(6),
00055                        edgeDetectionU(edgeThresholdU),
00056                        edgeDetectionV(edgeThresholdV)
00057 {}
00058 
00059 
00060 void GT2004BeaconDetector::execute()
00061 {
00062   flagSpecialist.init(image);
00063   numOfBeaconCandidates = 0;
00064   double dist(horizontalBaseOffset);
00065   Geometry::Line scanLine;
00066   scanLine.direction = imageInf.horizon.direction;
00067   scanLine.base = imageInf.horizon.base;
00068   int i;
00069   for(i=0; i < numOfHorizontalScanLineBelow; i++)
00070   {
00071     scanLine.base += imageInf.vertLine.direction*dist;
00072     dist += horizontalOffsetModifier;
00073     Vector2<int> startPoint, endPoint;
00074     if(Geometry::getIntersectionPointsOfLineAndRectangle(
00075        Vector2<int>(0,0), imageInf.maxImageCoordinates, scanLine, startPoint, endPoint))
00076     {
00077       LINE(imageProcessor_general, startPoint.x, startPoint.y, endPoint.x, endPoint.y, 1, Drawings::ps_dash, Drawings::gray);
00078       scanForPink(startPoint, endPoint);
00079     }
00080     else
00081     {
00082       break;
00083     }
00084   }
00085   dist  = horizontalBaseOffset;
00086   scanLine.base = imageInf.horizon.base;
00087   for(i=0; i < numOfHorizontalScanLineAbove; i++)
00088   {
00089     scanLine.base -= imageInf.vertLine.direction*dist;
00090     dist += horizontalOffsetModifier;
00091     Vector2<int> startPoint, endPoint;
00092     if(Geometry::getIntersectionPointsOfLineAndRectangle(
00093        Vector2<int>(0,0), imageInf.maxImageCoordinates, scanLine, startPoint, endPoint))
00094     {
00095       LINE(imageProcessor_general, startPoint.x, startPoint.y, endPoint.x, endPoint.y, 1, Drawings::ps_dash, Drawings::yellow);
00096       scanForPink(startPoint, endPoint);
00097     }
00098     else
00099     {
00100       break;
00101     }
00102   }
00103   if(numOfBeaconCandidates)
00104   {
00105     clusterPinkBeaconParts();
00106   }
00107   flagSpecialist.getFlagPercept(cameraMatrix, prevCameraMatrix, image.cameraInfo, imageInf.horizon, landmarksPercept);
00108 }
00109 
00110 
00111 void GT2004BeaconDetector::scanForPink(const Vector2<int>& start, 
00112                                        const Vector2<int>& end)
00113 {
00114   BresenhamLineScan bresenham(start, end);
00115   bresenham.init();
00116   Vector2<int> pixel(start), lastPixel(start);
00117   int Y(0),U(0),V(0),lastY(0),lastU(0),lastV(0);
00118   Run currentRun;
00119   bool isPinkRun(false);
00120   bool currentPixelPink(false);
00121   for(int i=0; i<bresenham.numberOfPixels; i++)
00122   {
00123     U = image.image[pixel.y][1][pixel.x];
00124     U = ColorCorrector::correct(pixel.x, pixel.y, 1, U);
00125     if(U > minPinkUValue)
00126     {
00127       Y = image.image[pixel.y][0][pixel.x];
00128       Y = ColorCorrector::correct(pixel.x, pixel.y, 0, Y);
00129       V = image.image[pixel.y][2][pixel.x];
00130       V = ColorCorrector::correct(pixel.x, pixel.y, 2, V);
00131       currentPixelPink = (COLOR_CLASS(Y,U,V) == pink);
00132     }
00133     else
00134     {
00135       currentPixelPink = false;
00136     }
00137     if(!isPinkRun)
00138     {
00139       if(currentPixelPink)
00140       {
00141         currentRun.scanLineStart = start;
00142         currentRun.start = pixel;
00143         currentRun.length = 1;
00144         isPinkRun = true;
00145       }
00146     }
00147     else //if(isPinkRun)
00148     {
00149       if(currentPixelPink)// || (COLOR_COMPONENT_DIST(U,lastU) < params.maxPinkUContrast))
00150       {
00151         currentRun.length++;
00152         lastY = Y;
00153         lastU = U;
00154         lastV = V;
00155       }
00156       else
00157       {
00158         isPinkRun = false;
00159         if(currentRun.length >= minPinkRunLength)
00160         {
00161           currentRun.end = lastPixel;
00162           if(!addCandidate(currentRun)) break; //List is full, stop searching
00163           LINE(imageProcessor_general, currentRun.start.x, currentRun.start.y, 
00164                currentRun.end.x, currentRun.end.y, 1, Drawings::ps_solid, Drawings::pink);
00165         }
00166       }
00167     }    
00168     //Compute next position according to Bresenham algorithm
00169     lastPixel = pixel;
00170     bresenham.getNext(pixel);
00171   }
00172   //Finish the last Run
00173   if(isPinkRun)
00174   {
00175     currentRun.end = pixel;
00176     addCandidate(currentRun);
00177     LINE(imageProcessor_general, currentRun.start.x, currentRun.start.y, 
00178                currentRun.end.x, currentRun.end.y, 1, Drawings::ps_solid, Drawings::pink);
00179   }
00180 }
00181 
00182 
00183 bool GT2004BeaconDetector::addCandidate(const Run& pinkRun)
00184 {
00185   bool returnValue(numOfBeaconCandidates<MAX_NUMBER_OF_PINK_RUNS);
00186   if(returnValue)
00187   {
00188     if((numOfBeaconCandidates>0) &&
00189        (pinkRun.scanLineStart == beaconCandidates[numOfBeaconCandidates-1].scanLineStart) &&
00190        ((beaconCandidates[numOfBeaconCandidates-1].end - pinkRun.start).abs()<4))
00191     {
00192       beaconCandidates[numOfBeaconCandidates-1].end = pinkRun.end;
00193     }
00194     else
00195     {
00196       beaconCandidates[numOfBeaconCandidates++] = pinkRun;
00197     }
00198   }
00199   return returnValue;
00200 }
00201 
00202 
00203 void GT2004BeaconDetector::clusterPinkBeaconParts()
00204 {
00205   //Transform pink runs
00206   double rotation(imageInf.horizon.direction.angle());
00207   double ca(cos(-rotation));
00208   double sa(sin(-rotation));
00209   Matrix2x2<double> rotMat(Vector2<double>(ca,sa),Vector2<double>(-sa,ca));
00210   Matrix2x2<double> invRotMat = rotMat.transpose(); //=rotMat.invert(), orthonormal matrix
00211   for(int i=0; i<numOfBeaconCandidates; i++)
00212   {
00213     transformedCandidates[i].transform(beaconCandidates[i], rotMat, i);
00214   }
00215   //Sort by starting position from left to right
00216   for(int j=0; j<numOfBeaconCandidates; j++)
00217   {
00218     int leftest(j);
00219     for(int k=j; k<numOfBeaconCandidates; k++)
00220     {
00221       if(transformedCandidates[k].start.x<transformedCandidates[leftest].start.x)
00222       {
00223         leftest = k;
00224       }
00225     }
00226     if(leftest != j)
00227     {
00228       TransformedRun help(transformedCandidates[j]);
00229       transformedCandidates[j] = transformedCandidates[leftest];
00230       transformedCandidates[leftest] = help;
00231     }
00232   }
00233   //Find overlapping runs
00234   int beginOfBeacon(0), endOfBeacon(0);
00235   double xRight(transformedCandidates[0].end.x);
00236   double xLeft(transformedCandidates[0].start.x);
00237   double yRight(transformedCandidates[0].end.y);
00238   double yLeft(transformedCandidates[0].start.y);
00239   double runLength(xRight-xLeft);
00240   double yMax, yMin;
00241   double originMinY, originMaxY;
00242   if (yRight > yLeft)
00243   {
00244     yMax = yRight;
00245     yMin = yLeft;
00246   }
00247   else 
00248   {
00249     yMax = yLeft;
00250     yMin = yRight;
00251   }
00252   originMinY = beaconCandidates[0].scanLineStart.y;
00253   originMaxY = beaconCandidates[0].scanLineStart.y;
00254   for(int l=1; l<numOfBeaconCandidates; l++)
00255   {
00256     if(transformedCandidates[l].start.x < xRight  + (double)clusteringDistanceTolerance)
00257     {// within merging distance
00258       if(transformedCandidates[l].end.x > xRight)
00259       { // new piece could extend the run
00260         double candidateLength = transformedCandidates[l].end.x-xLeft;
00261         bool newRunHigher = (transformedCandidates[l].end.y-yLeft > 0.0);
00262         double candidateHeight = newRunHigher ? (transformedCandidates[l].end.y-yLeft) : (yLeft-transformedCandidates[l].end.y);
00263         if (candidateHeight < candidateLength*clusteringAspectRatio) // the proportions of the landmark are consistent (1:1 ideally)
00264         {
00265           xRight = transformedCandidates[l].end.x;
00266           yRight = transformedCandidates[l].end.y;
00267           endOfBeacon = l;
00268           if (transformedCandidates[l].end.y-yMax > 0.0)
00269           {
00270             yMax = transformedCandidates[l].end.y;
00271             originMaxY = beaconCandidates[l].scanLineStart.y;
00272           }
00273           else if (yMin - transformedCandidates[l].end.y > 0.0)
00274           {
00275             yMin = transformedCandidates[l].end.y;
00276             originMinY = beaconCandidates[l].scanLineStart.y;
00277           }
00278         }
00279         else 
00280         { // we have to choose one run, the other is perhaps a false positive 
00281           double newLength = transformedCandidates[l].end.x - transformedCandidates[l].start.x;
00282           if (newLength > runLength) // the new run wins if longest
00283           {
00284             xRight = transformedCandidates[l].end.x;
00285             xLeft = transformedCandidates[l].start.x;
00286             yRight = transformedCandidates[l].end.y;
00287             yLeft = transformedCandidates[l].start.y;
00288             endOfBeacon = l;
00289             beginOfBeacon = l;
00290             runLength = newLength;
00291             if (yRight > yLeft)
00292             {
00293               yMax = yRight;
00294               yMin = yLeft;
00295             }
00296             else 
00297             {
00298               yMax = yLeft;
00299               yMin = yRight;
00300             }
00301             originMinY = beaconCandidates[l].scanLineStart.y;
00302             originMaxY = beaconCandidates[l].scanLineStart.y;
00303           }
00304         }
00305       }
00306       else
00307       { // the new run is horizontally contained 
00308         double newRunHeight = (transformedCandidates[l].end.y + transformedCandidates[l].start.y)/2.0;
00309         bool candidateMax = (newRunHeight-yMax > 0.0);
00310         bool candidateMin = (yMin - newRunHeight > 0.0);
00311         if (candidateMax)
00312         {
00313           double candidateHeight = newRunHeight - yMin;
00314           double candidateLength = xRight-xLeft;
00315           if (candidateHeight < candidateLength*clusteringAspectRatio) // the proportions of the landmark are consistent (1:1 ideally)
00316           {
00317             yMax = newRunHeight;
00318             originMaxY = beaconCandidates[l].scanLineStart.y;
00319           }        
00320         }
00321         else if (candidateMin)
00322         {
00323           double candidateHeight = yMax - newRunHeight;
00324           double candidateLength = xRight-xLeft;
00325           if (candidateHeight < candidateLength*clusteringAspectRatio) // the proportions of the landmark are consistent (1:1 ideally)
00326           {
00327             yMin = newRunHeight;
00328             originMinY = beaconCandidates[l].scanLineStart.y;
00329           }        
00330         }
00331       }
00332     }
00333     else
00334     {
00335       double originX = (beaconCandidates[beginOfBeacon].scanLineStart.x + beaconCandidates[endOfBeacon].scanLineStart.x)/2.0;
00336       //~ double originY = (beaconCandidates[beginOfBeacon].scanLineStart.y + beaconCandidates[endOfBeacon].scanLineStart.y)/2.0;
00337       double originY = (originMinY + originMaxY)/2;
00338       double mergedScanLineLength = xRight-xLeft;
00339       Vector2<double> leftOfBeacon, rightOfBeacon;
00340       leftOfBeacon.x = xLeft - originX;
00341       rightOfBeacon.x = xRight - originX;
00342       leftOfBeacon.y = (yMax+yMin)/2.0 - originY;  
00343       rightOfBeacon.y = leftOfBeacon.y;
00344       leftOfBeacon = (invRotMat*leftOfBeacon);
00345       rightOfBeacon = (invRotMat*rightOfBeacon);
00346       leftOfBeacon.x += originX;
00347       leftOfBeacon.y += originY;
00348       rightOfBeacon.x += originX;
00349       rightOfBeacon.y += originY;
00350       LINE(imageProcessor_general, int(leftOfBeacon.x+0.5), int(leftOfBeacon.y+0.5), 
00351             int(rightOfBeacon.x+0.5), int(rightOfBeacon.y+0.5), 1, Drawings::ps_dash, Drawings::red);
00352       analyzeBeacon(leftOfBeacon, mergedScanLineLength);
00353       beginOfBeacon = l;
00354       endOfBeacon = l;
00355       xRight = transformedCandidates[l].end.x;
00356       yRight = transformedCandidates[l].end.y;
00357       xLeft = transformedCandidates[l].start.x;
00358       yLeft = transformedCandidates[l].start.y;
00359       if (yRight > yLeft)
00360       {
00361         yMax = yRight;
00362         yMin = yLeft;
00363       }
00364       else 
00365       {
00366         yMax = yLeft;
00367         yMin = yRight;
00368       }
00369       originMinY = beaconCandidates[l].scanLineStart.y;
00370       originMaxY = beaconCandidates[l].scanLineStart.y;
00371     }
00372   }
00373   double originX = (beaconCandidates[beginOfBeacon].scanLineStart.x + beaconCandidates[endOfBeacon].scanLineStart.x)/2.0;
00374   //~ double originY = (beaconCandidates[beginOfBeacon].scanLineStart.y + beaconCandidates[endOfBeacon].scanLineStart.y)/2.0;
00375   double originY = (originMinY + originMaxY)/2;
00376   double mergedScanLineLength = xRight-xLeft;
00377   Vector2<double> leftOfBeacon, rightOfBeacon;
00378   leftOfBeacon.x = xLeft - originX;
00379   rightOfBeacon.x = xRight - originX;
00380   leftOfBeacon.y = (yMax+yMin)/2.0 - originY;  
00381   rightOfBeacon.y = leftOfBeacon.y;
00382   leftOfBeacon = (invRotMat*leftOfBeacon);
00383   rightOfBeacon = (invRotMat*rightOfBeacon);
00384   leftOfBeacon.x += originX;
00385   leftOfBeacon.y += originY;
00386   rightOfBeacon.x += originX;
00387   rightOfBeacon.y += originY;
00388   LINE(imageProcessor_general, int(leftOfBeacon.x+0.5), int(leftOfBeacon.y+0.5), 
00389         int(rightOfBeacon.x+0.5), int(rightOfBeacon.y+0.5), 1, Drawings::ps_dash, Drawings::red);
00390   analyzeBeacon(leftOfBeacon, mergedScanLineLength);
00391 }
00392 
00393 void GT2004BeaconDetector::analyzeBeacon(const Vector2<double>& left, const double pinkRunWidth)
00394 {
00395   if (pinkRunWidth >= 3.0)
00396   {
00397     double rotation(imageInf.horizon.direction.angle());
00398     double ca(cos(rotation));
00399     double sa(sin(rotation));
00400     Matrix2x2<double> rotMat(Vector2<double>(ca,sa),Vector2<double>(-sa,ca));
00401     int flagReliability[4] = {0, 0, 0, 0}; 
00402     Flag::FlagType flagFound;
00403     Vector2<int> topEdge[4];
00404     Vector2<int> bottomEdge[4];
00405     int i;
00406     int totalWeight = 0;
00407     int highestReliability = 0;
00408     int mostReliableFlag = -1;
00409     for (i=0; i<4; i++)
00410     {
00411       topEdge[i].x = 0;
00412       topEdge[i].y = 0;
00413       bottomEdge[i].x = 0;
00414       bottomEdge[i].y = 0;
00415     }
00416     Vector2<double> currentPos;
00417     Vector2<double> step;
00418     int numberOfScanCol;
00419     if (pinkRunWidth <= 8.0)
00420     { // only 3 scan lines
00421       Vector2<double> displacement((pinkRunWidth-3.0)/2.0, 0); //1 pixel inner than theoretical border
00422       Vector2<double> init(1.0, 0.0);
00423       displacement = rotMat*displacement;
00424       init = rotMat*init;
00425       step = displacement;
00426       currentPos = left+init;
00427       numberOfScanCol = 3;
00428     }
00429     else
00430     {
00431       Vector2<double> displacement((pinkRunWidth-5.0)/3.0, 0); //2 pixels inner than theoretical border
00432       Vector2<double> init(2.0, 0.0);
00433       displacement = rotMat*displacement;
00434       init = rotMat*init;
00435       step = displacement;
00436       currentPos = left+init;
00437       numberOfScanCol = 4;
00438     }
00439     for (i=0; i<numberOfScanCol; i++)
00440     {
00441       Vector2<int> top, bottom;
00442       int reliability = scanForBeaconEdges(Vector2<int>(int(currentPos.x+0.5), int(currentPos.y+0.5)), 
00443             pinkRunWidth, flagFound, top, bottom);
00444       if (reliability != 0)
00445       {
00446         flagReliability[(int)flagFound] += reliability;
00447         topEdge[(int)flagFound] += top;
00448         bottomEdge[(int)flagFound] += bottom;
00449       }
00450       currentPos += step;
00451     }
00452     for (i=0; i<4; i++)
00453     {
00454       totalWeight += flagReliability[i];
00455       if (flagReliability[i] > highestReliability)
00456       {
00457         highestReliability = flagReliability[i];
00458         mostReliableFlag = i;
00459       }
00460     }
00461     if (mostReliableFlag != -1)
00462     {
00463       double confidence = highestReliability/totalWeight;
00464       if (confidence >= minFlagConfidence)
00465       {
00466         int posX = (int)((topEdge[mostReliableFlag].x + bottomEdge[mostReliableFlag].x)/(2*flagReliability[mostReliableFlag]) + 0.5);
00467         int posY = (int)((topEdge[mostReliableFlag].y + bottomEdge[mostReliableFlag].y)/(2*flagReliability[mostReliableFlag]) + 0.5);
00468         Vector2<int> center(posX, posY);
00469         const Vector2<int> border(0,0);
00470         Geometry::clipPointInsideRectange(border, imageInf.maxImageCoordinates-border, center);
00471         DOT(imageProcessor_ball4, center.x, center.y, Drawings::black, Drawings::blue);
00472         switch ((Flag::FlagType)mostReliableFlag)
00473         {
00474           case Flag::pinkAboveYellow:
00475               flagSpecialist.searchFlags(image, colorTable, cameraMatrix, yellow, true, imageInf.horizon, center.x, center.y);
00476               break;
00477           case Flag::pinkAboveSkyblue:
00478               flagSpecialist.searchFlags(image, colorTable, cameraMatrix, skyblue, true, imageInf.horizon, center.x, center.y);
00479               break;
00480           case Flag::yellowAbovePink:
00481               flagSpecialist.searchFlags(image, colorTable, cameraMatrix, yellow, false, imageInf.horizon, center.x, center.y);
00482               break;
00483           case Flag::skyblueAbovePink:
00484               flagSpecialist.searchFlags(image, colorTable, cameraMatrix, skyblue, false, imageInf.horizon, center.x, center.y);
00485               break;
00486         }
00487       }
00488     }
00489   }
00490 }
00491 
00492 int GT2004BeaconDetector::scanForBeaconEdges(const Vector2<int>& position, const double pinkRunWidth, 
00493         Flag::FlagType& flagType, Vector2<int>& topEdge, Vector2<int>& bottomEdge)
00494 {
00495   Vector2<int> beaconCenter(position);
00496   Vector2<int> start, end;
00497   Geometry::Line vertLine;
00498   vertLine.direction = imageInf.vertLine.direction;
00499   vertLine.base.x = (double)beaconCenter.x;
00500   vertLine.base.y = (double)beaconCenter.y;
00501   Geometry::getIntersectionPointsOfLineAndRectangle(Vector2<int>(1,1), // 1 pixel wide border has to be discarded
00502               imageInf.maxImageCoordinates-Vector2<int>(1,1), vertLine, start, end); // due to the edge detection convolution mask
00503   Vector2<int> pos, pos2, pinkBottomEdge, pinkTopEdge, colorBottomEdge, colorTopEdge;
00504   colorClass bottomColor(noColor),topColor(noColor), baseColor(noColor), dontCare(noColor);
00505   bool topColorEdgeFound(false), pinkTopFound(false);
00506   bool unreliable(false); // this flag signals that an unsafe beacon hypothesis has been accepted
00507   Flag::FlagType beaconType(Flag::pinkAboveYellow);
00508   int reliability;
00509   // Scan to bottom of pink beacon part
00510   if(scanForBeaconPart(beaconCenter, end, pos, pinkBottomEdge, bottomColor))
00511   {
00512     if(bottomColor == white || bottomColor == gray)
00513     {
00514       // Scan to top of pink beacon part
00515       if(scanForBeaconPart(beaconCenter, start, pos, pinkTopEdge, topColor))
00516       {
00517         if(topColor == skyblue)
00518         {
00519           beaconType = Flag::skyblueAbovePink;
00520           topColorEdgeFound = scanForBeaconPart(pos, start, pos2, colorTopEdge, dontCare);
00521         }
00522         else if(topColor == yellow)
00523         {
00524           beaconType = Flag::yellowAbovePink;
00525           topColorEdgeFound = scanForBeaconPart(pos, start, pos2, colorTopEdge, dontCare);
00526         }
00527         else
00528         {
00529           return 0; // unknown color above pink
00530         }
00531       }
00532       else
00533       {
00534         return 0; // pink part is at the upper border of the image
00535       }
00536     }
00537     else if((bottomColor == skyblue) || (bottomColor == yellow))
00538     {// Scan to bottom of color part
00539       if(scanForBeaconPart(pos, end, pos2, colorBottomEdge, baseColor))
00540       {
00541         if(baseColor == white || baseColor == gray)
00542         {
00543           if(bottomColor == skyblue)
00544           {
00545             beaconType = Flag::pinkAboveSkyblue;
00546             pinkTopFound = scanForBeaconPart(beaconCenter, start, pos, pinkTopEdge, topColor);
00547           }
00548           else
00549           {
00550             beaconType = Flag::pinkAboveYellow;
00551             pinkTopFound = scanForBeaconPart(beaconCenter, start, pos, pinkTopEdge, topColor);
00552           }
00553         }
00554         else
00555         { // perhaps we missed the real end of the colored part
00556           if (scanForBeaconPart(beaconCenter, start, pos, pinkTopEdge, topColor))
00557           { // estimate colorBottomEdge from pinkTopEdge
00558             Vector2<int> toPink(pinkTopEdge - pinkBottomEdge);
00559             double distanceToPink = toPink.abs();
00560             double similarityPink;
00561             if (distanceToPink > pinkRunWidth)
00562               similarityPink = (pinkRunWidth/distanceToPink);
00563             else 
00564               similarityPink = (distanceToPink/pinkRunWidth);
00565             if (similarityPink > projectionAspectRatio) // make sure that this proposed edge is compatible with the aspect ratio
00566             {
00567               colorBottomEdge = pinkBottomEdge + (pinkBottomEdge - pinkTopEdge);
00568               pinkTopFound = true;
00569               if(bottomColor == skyblue)
00570                 beaconType = Flag::pinkAboveSkyblue;
00571               else
00572                 beaconType = Flag::pinkAboveYellow;
00573             }
00574             else
00575               return 0;
00576           }
00577           else
00578             return 0; // unable to determine top edge of pink part
00579         }
00580       }
00581       else
00582       { // we were unable to find the end of the colored part (no contrast?), and reached the border of the image
00583         // before giving up, let's see if we can at least find the top edge of the pink part
00584         if (scanForBeaconPart(beaconCenter, start, pos, pinkTopEdge, topColor))
00585         { // estimate colorBottomEdge from pinkTopEdge
00586           Vector2<int> toPink(pinkTopEdge - pinkBottomEdge);
00587           double distanceToPink = toPink.abs();
00588           double similarityPink;
00589           if (distanceToPink > pinkRunWidth)
00590             similarityPink = (pinkRunWidth/distanceToPink);
00591           else 
00592             similarityPink = (distanceToPink/pinkRunWidth);
00593           if (similarityPink > projectionAspectRatio) // make sure that this proposed edge is compatible with the aspect ratio
00594           {
00595             colorBottomEdge = pinkBottomEdge + (pinkBottomEdge - pinkTopEdge);
00596             pinkTopFound = true;
00597             if(bottomColor == skyblue)
00598               beaconType = Flag::pinkAboveSkyblue;
00599             else
00600               beaconType = Flag::pinkAboveYellow;
00601           }
00602           else
00603             return 0;
00604         }
00605         else
00606           return 0; // unable to determine top edge of pink part
00607       }
00608     }
00609     else
00610     {
00611       return 0; // unknown color below pink
00612     }
00613   }
00614   else // unable to determine bottom edge (no gradient, or border of the image touched)
00615   { // so. try to scan to top
00616     if(scanForBeaconPart(beaconCenter, start, pos, pinkTopEdge, topColor))
00617     {
00618       DOT(imageProcessor_ball4, pinkTopEdge.x, pinkTopEdge.y, Drawings::black, Drawings::pink);
00619       if(topColor == skyblue)
00620       {
00621         beaconType = Flag::skyblueAbovePink;
00622         topColorEdgeFound = scanForBeaconPart(pos, start, pos2, colorTopEdge, dontCare);
00623         if (!topColorEdgeFound)
00624           return 0; // unable to calculate the vertical edges of the landmark
00625         DOT(imageProcessor_ball4, colorTopEdge.x, colorTopEdge.y, Drawings::black, Drawings::red);
00626       }
00627       else if(topColor == yellow)
00628       {
00629         beaconType = Flag::yellowAbovePink;
00630         topColorEdgeFound = scanForBeaconPart(pos, start, pos2, colorTopEdge, dontCare);
00631         if (!topColorEdgeFound)
00632           return 0; // unable to calculate the vertical edges of the landmark
00633         DOT(imageProcessor_ball4, colorTopEdge.x, colorTopEdge.y, Drawings::black, Drawings::red);
00634       }
00635       else // last attempt, try to use scan width information and top edge to make an hypothesis on bottom edge,
00636       { // check if this holds by looking at the color underlying the projected edge (should be yellow or skyblue)
00637         Vector2<double> projectedLenght(0, pinkRunWidth+3.0); //3 pixel more to make sure we jump over the pink/color edge
00638         double rotation(imageInf.horizon.direction.angle());
00639         double ca(cos(rotation));
00640         double sa(sin(rotation));
00641         Matrix2x2<double> rotMat(Vector2<double>(ca,sa),Vector2<double>(-sa,ca));
00642         Vector2<double> topToBottomDisplacement = rotMat*projectedLenght;
00643         Vector2<int> displacement((int)(topToBottomDisplacement.x+0.5), (int)(topToBottomDisplacement.y+0.5)); 
00644         pinkBottomEdge = pinkTopEdge + displacement;
00645         const Vector2<int> border(0,0);
00646         Geometry::clipPointInsideRectange(border, imageInf.maxImageCoordinates-border, pinkBottomEdge);
00647         unsigned char colorY(ColorCorrector::correct(pinkBottomEdge.x,pinkBottomEdge.y,0,image.image[pinkBottomEdge.y][0][pinkBottomEdge.x]));
00648         unsigned char colorU(ColorCorrector::correct(pinkBottomEdge.x,pinkBottomEdge.y,1,image.image[pinkBottomEdge.y][1][pinkBottomEdge.x]));
00649         unsigned char colorV(ColorCorrector::correct(pinkBottomEdge.x,pinkBottomEdge.y,2,image.image[pinkBottomEdge.y][2][pinkBottomEdge.x]));
00650         bottomColor = COLOR_CLASS(colorY,colorU,colorV);
00651         DOT(imageProcessor_ball4, pinkBottomEdge.x, pinkBottomEdge.y, Drawings::gray, ColorClasses::colorClassToDrawingsColor(bottomColor));
00652         if((bottomColor == skyblue) || (bottomColor == yellow))        
00653         {
00654           colorBottomEdge = pinkBottomEdge + displacement;
00655           Geometry::clipPointInsideRectange(border, imageInf.maxImageCoordinates-border, colorBottomEdge);
00656           colorY = ColorCorrector::correct(colorBottomEdge.x,colorBottomEdge.y,0,image.image[colorBottomEdge.y][0][colorBottomEdge.x]);
00657           colorU = ColorCorrector::correct(colorBottomEdge.x,colorBottomEdge.y,1,image.image[colorBottomEdge.y][1][colorBottomEdge.x]);
00658           colorV = ColorCorrector::correct(colorBottomEdge.x,colorBottomEdge.y,2,image.image[colorBottomEdge.y][2][colorBottomEdge.x]);
00659           baseColor = COLOR_CLASS(colorY,colorU,colorV);
00660           DOT(imageProcessor_ball4, colorBottomEdge.x, colorBottomEdge.y, Drawings::white, ColorClasses::colorClassToDrawingsColor(baseColor));
00661           if (baseColor == white || baseColor == gray)
00662           {
00663             if (bottomColor == skyblue)
00664             {
00665               beaconType = Flag::pinkAboveSkyblue;
00666               pinkTopFound = true;
00667               unreliable = true;
00668             }
00669             else if (bottomColor == yellow)
00670             {
00671               beaconType = Flag::pinkAboveYellow;
00672               pinkTopFound = true;
00673               unreliable = true;
00674             }
00675             else
00676               return 0;
00677           }
00678           else if (baseColor == pink) // we could have missed the middle edge of a landmark whose lower part is pink
00679           {
00680             if (bottomColor == skyblue) 
00681             {
00682               beaconType = Flag::skyblueAbovePink;
00683               pinkTopEdge = pinkBottomEdge; // we have to swap up and down, because it seems 
00684               pinkBottomEdge = colorBottomEdge; //that the assumption pinkAboveSkyblue was wrong
00685               topColor = skyblue;
00686               bottomColor = pink;
00687               unreliable = true;
00688             }
00689             else if (bottomColor == yellow)
00690             {
00691               beaconType = Flag::yellowAbovePink;
00692               pinkTopEdge = pinkBottomEdge; // we have to swap up and down, because it seems 
00693               pinkBottomEdge = colorBottomEdge; //that the assumption pinkAboveSkyblue was wrong
00694               topColor = yellow;
00695               bottomColor = pink;
00696               unreliable = true;
00697             }
00698             else
00699               return 0;
00700           }
00701           else
00702             return 0;
00703         }
00704         else
00705         {
00706           return 0; // unknown color above pink
00707         }
00708       }
00709     }
00710     else
00711     {
00712       return 0;
00713     }
00714   }
00715 
00716   //~ DOT(imageProcessor_ball4, pos.x, pos.y, Drawings::white, Drawings::black);
00717   //~ DOT(imageProcessor_ball4, pos2.x, pos2.y, Drawings::black, Drawings::gray);
00718   
00719   if(beaconType == Flag::pinkAboveYellow || beaconType == Flag::pinkAboveSkyblue)
00720   {
00721     if(pinkTopFound)
00722     {
00723       //~ DOT(imageProcessor_ball4, pinkBottomEdge.x, pinkBottomEdge.y, Drawings::black, Drawings::green);
00724       //~ DOT(imageProcessor_ball4, pinkTopEdge.x, pinkTopEdge.y, Drawings::white, Drawings::black);
00725       //~ DOT(imageProcessor_ball4, colorBottomEdge.x, colorBottomEdge.y, Drawings::black, Drawings::white);  
00726       Vector2<int> topPosition(pinkBottomEdge);
00727       Vector2<int> toColor(pinkBottomEdge - colorBottomEdge);
00728       Vector2<int> toPink(pinkTopEdge - pinkBottomEdge);
00729       double distanceToColor = toColor.abs();
00730       double distanceToPink = toPink.abs();
00731       double similarityColor, similarityPink;
00732       if (distanceToColor > pinkRunWidth)
00733         similarityColor = (pinkRunWidth/distanceToColor);
00734       else
00735         similarityColor = (distanceToColor/pinkRunWidth);
00736       if (distanceToPink > pinkRunWidth)
00737         similarityPink = (pinkRunWidth/distanceToPink);
00738       else
00739         similarityPink = (distanceToPink/pinkRunWidth);
00740       if (similarityColor > similarityPink)
00741         topPosition += toColor;
00742       else
00743         topPosition += toPink;
00744       //~ DOT(imageProcessor_ball4, topPosition.x, topPosition.y, Drawings::black, Drawings::gray);
00745       //~ DOT(imageProcessor_ball4, pinkBottomEdge.x, topPosition.y+(pinkBottomEdge.y-topPosition.y)/2, Drawings::black, Drawings::blue);
00746       if (unreliable)
00747         reliability = lowReliability;
00748       else 
00749         reliability = highReliability;
00750       flagType = beaconType;
00751       topEdge = topPosition*reliability;
00752       bottomEdge = pinkBottomEdge*reliability;
00753       return reliability;
00754     }
00755     else
00756     {
00757       // compute a theoretical position of the pink top edge:
00758       Vector2<int> topPosition(pinkBottomEdge);
00759       Vector2<int> relVec(pinkBottomEdge - colorBottomEdge);
00760       topPosition += relVec;
00761       //~ DOT(imageProcessor_ball4, pinkBottomEdge.x, topPosition.y+(pinkBottomEdge.y-topPosition.y)/2, Drawings::black, Drawings::blue);
00762       //~ DOT(imageProcessor_ball4, pinkBottomEdge.x, pinkBottomEdge.y, Drawings::black, Drawings::red);
00763       //~ DOT(imageProcessor_ball4, topPosition.x, topPosition.y, Drawings::white, Drawings::black);
00764       reliability = mediumReliability;
00765       flagType = beaconType;
00766       topEdge = topPosition*reliability;
00767       bottomEdge = pinkBottomEdge*reliability;
00768       return reliability;
00769     }
00770   }
00771   else
00772   { // pink is bottom
00773     if (bottomColor == noColor && topColorEdgeFound) // no pink bottom edge found
00774     { //theoretical position will be calculated from color top edge
00775       Vector2<int> bottomPosition(pinkTopEdge);
00776       Vector2<int> relVec(pinkTopEdge - colorTopEdge);
00777       bottomPosition += relVec;
00778       flagSpecialist.searchFlags(image, colorTable, cameraMatrix, topColor, false, 
00779           imageInf.horizon, (int)((pinkTopEdge.x+bottomPosition.x+1.0)/2), (int)((pinkTopEdge.y+bottomPosition.y+1.0)/2));
00780       DOT(imageProcessor_ball4, pinkTopEdge.x, pinkTopEdge.y, Drawings::white, Drawings::black);
00781       DOT(imageProcessor_ball4, bottomPosition.x, bottomPosition.y, Drawings::black, Drawings::yellow);
00782       DOT(imageProcessor_ball4, (int)((pinkTopEdge.x+bottomPosition.x+1.0)/2), (int)((pinkTopEdge.y+bottomPosition.y+1.0)/2), Drawings::black, Drawings::blue);
00783       reliability = mediumReliability;
00784       flagType = beaconType;
00785       topEdge = pinkTopEdge*reliability;
00786       bottomEdge = bottomPosition*reliability;
00787       return reliability;
00788     }
00789     else{
00790       Vector2<int> bottomPosition;
00791       if (topColorEdgeFound) //color edge information used just for redundancy check
00792       {
00793         bottomPosition = pinkTopEdge;
00794         Vector2<int> toColor(pinkTopEdge - colorTopEdge);
00795         Vector2<int> toPink(pinkBottomEdge - pinkTopEdge);
00796         double distanceToColor = toColor.abs();
00797         double distanceToPink = toPink.abs();
00798         double similarityColor, similarityPink;
00799         if (distanceToColor > pinkRunWidth)
00800           similarityColor = (pinkRunWidth/distanceToColor);
00801         else
00802           similarityColor = (distanceToColor/pinkRunWidth);
00803         if (distanceToPink > pinkRunWidth)
00804           similarityPink = (pinkRunWidth/distanceToPink);
00805         else
00806           similarityPink = (distanceToPink/pinkRunWidth);
00807         if (similarityColor > similarityPink)
00808           bottomPosition += toColor;
00809         else
00810           bottomPosition += toPink;
00811         if (unreliable)
00812           reliability = lowReliability;
00813         else
00814           reliability = highReliability;
00815         flagType = beaconType;
00816         topEdge = pinkTopEdge*reliability;
00817         bottomEdge = bottomPosition*reliability;
00818         return reliability;
00819       }
00820       else
00821       {
00822         if (unreliable)
00823           reliability = lowReliability;
00824         else
00825           reliability = mediumReliability;
00826         flagType = beaconType;
00827         topEdge = pinkTopEdge*reliability;
00828         bottomEdge = pinkBottomEdge*reliability;
00829         return reliability;
00830       }
00831     }
00832   }
00833   return true;
00834 }
00835 
00836 
00837 bool GT2004BeaconDetector::scanForBeaconPart(const Vector2<int>& start, const Vector2<int>& end,
00838                                              Vector2<int>& position, Vector2<int>& edge, colorClass& color)
00839 {
00840   // DOT(imageProcessor_ball4, start.x, start.y, Drawings::blue, Drawings::orange);
00841   // DOT(imageProcessor_ball4, end.x, end.y, Drawings::yellow, Drawings::white);
00842   BresenhamLineScan bresenham(start, end);
00843   bresenham.init();
00844   Vector2<int> pixel(start);
00845   for(int i=0; i<bresenham.numberOfPixels; i++)
00846   {
00847     // bool edgeY = edgeDetectionY.isEdgePoint(image, pixel.x, pixel.y, SUSANEdgeDetectionLite::componentA);
00848     bool edgeU = edgeDetectionU.isEdgePoint(image, pixel.x, pixel.y, SUSANEdgeDetectionLite::componentB);
00849     bool edgeV = edgeDetectionV.isEdgePoint(image, pixel.x, pixel.y, SUSANEdgeDetectionLite::componentC);
00850     DOT(imageProcessor_ball4, pixel.x, pixel.y, Drawings::black, Drawings::orange);
00851   
00852     if (edgeU || edgeV)
00853     {
00854       position = pixel;
00855       edge = pixel;
00856       color = noColor;
00857       unsigned char colorY(ColorCorrector::correct(edge.x,edge.y,0,image.image[edge.y][0][edge.x]));
00858       unsigned char colorU(ColorCorrector::correct(edge.x,edge.y,1,image.image[edge.y][1][edge.x]));
00859       unsigned char colorV(ColorCorrector::correct(edge.x,edge.y,2,image.image[edge.y][2][edge.x]));
00860       colorClass edgeColor = COLOR_CLASS(colorY,colorU,colorV);
00861       color = edgeColor;
00862       int counter;
00863       for (counter = 0; counter < edgeScanDepth && 
00864             ((color == noColor)||(color==edgeColor)||(color==orange)||edgeU||edgeV); counter++)
00865       {
00866         bresenham.getNext(position);
00867         unsigned char colorY(ColorCorrector::correct(position.x,position.y,0,image.image[position.y][0][position.x]));
00868         unsigned char colorU(ColorCorrector::correct(position.x,position.y,1,image.image[position.y][1][position.x]));
00869         unsigned char colorV(ColorCorrector::correct(position.x,position.y,2,image.image[position.y][2][position.x]));
00870         color = COLOR_CLASS(colorY,colorU,colorV);
00871         edgeU = edgeDetectionU.isEdgePoint(image, position.x, position.y, SUSANEdgeDetectionLite::componentB);
00872         edgeV = edgeDetectionV.isEdgePoint(image, position.x, position.y, SUSANEdgeDetectionLite::componentC);
00873       }
00874       if (counter != edgeScanDepth) 
00875         return true;
00876     }
00877     //Compute next position according to Bresenham algorithm
00878     bresenham.getNext(pixel);
00879   }
00880   return 0;
00881 }
00882 
00883 void GT2004BeaconDetector::analyzeColorTable()
00884 {
00885   Vector3<int> nearPoint,farPoint;
00886 #ifdef CT32K_LAYOUT
00887   ((ColorTable32K&) colorTable).getBoxAroundColorClass(pink,nearPoint,farPoint);
00888 #else
00889   ((ColorTable64&) colorTable).getBoxAroundColorClass(pink,nearPoint,farPoint);
00890 #endif
00891   minPinkUValue = nearPoint.y;
00892 }
00893 
00894 
00895 /*
00896  * $Log: GT2004BeaconDetector.cpp,v $
00897  * Revision 1.20  2004/09/08 14:39:02  wachter
00898  * - Fixed some doxygen-errors
00899  *
00900  * Revision 1.19  2004/07/09 14:12:55  nistico
00901  * Some more safety checks
00902  *
00903  * Revision 1.18  2004/06/14 22:11:52  nistico
00904  * Minor improvement in clustering
00905  *
00906  * Revision 1.17  2004/06/13 21:22:14  nistico
00907  * Minor bug fixes
00908  *
00909  * Revision 1.16  2004/06/12 17:37:00  nistico
00910  * Eventually, would be nice to have only one Bresenham on the whole
00911  * GT2004ImageProcessor
00912  *
00913  * Revision 1.15  2004/06/09 15:04:14  nistico
00914  * Changed distribution of scanlines
00915  * Some cleanup
00916  * Test result positive
00917  *
00918  * Revision 1.14  2004/06/08 16:00:33  nistico
00919  * Final(?) improvement to the beaconSpecialist: 3 or 4 columns (depending on size)
00920  * are always scanned, and the results are merged based on validity, which is
00921  * calculated from the number of edges found and consistency checks
00922  *
00923  * Revision 1.13  2004/06/05 19:48:45  nistico
00924  * Added one more special situation to BeaconDetector
00925  * imageProcessorGradients now visualized edges used by
00926  * BeaconDetector, for debugging
00927  *
00928  * Revision 1.12  2004/06/05 07:58:21  roefer
00929  * Compensation for motion distortions of images
00930  *
00931  * Revision 1.11  2004/06/04 16:34:22  juengel
00932  * fixed gcc compile error
00933  *
00934  * Revision 1.10  2004/06/04 16:07:32  nistico
00935  * AnalyzeBeacon: more special cases added,
00936  * should be (almost) final
00937  *
00938  * Revision 1.9  2004/06/04 14:16:34  nistico
00939  * AnalyzeBeacon: additional recovery situation from missing edge (gradient too smooth) debugged
00940  *
00941  * Revision 1.8  2004/06/03 16:28:49  nistico
00942  * AnalyzeBeacon: additional recovery situation from missing edge (gradient too smooth)
00943  * doesn't work properly yet
00944  *
00945  * Revision 1.5  2004/06/02 14:35:59  nistico
00946  * - Pink scanline clustering further improved
00947  *
00948  * Revision 1.4  2004/06/01 16:07:28  nistico
00949  * Clustering of beacon pink scanlines improved
00950  *
00951  * Revision 1.1.1.1  2004/05/22 17:19:41  cvsadm
00952  * created new repository GT2004_WM
00953  *
00954  * Revision 1.5  2004/05/18 18:39:25  nistico
00955  * BeaconDetector improved, the landmarks are recognized much more often,
00956  * and the width is estimated correctly most of the time.
00957  * Some things are still less then ideal, though (i'll post something on the forum),
00958  *
00959  * Revision 1.4  2004/05/15 15:00:22  nistico
00960  * Beacon scanlines begin slightly below the horizon.
00961  * Scanline spacing changed.
00962  *
00963  * Revision 1.3  2004/05/14 12:19:24  tim
00964  * fixed bug
00965  *
00966  * Revision 1.2  2004/05/06 16:03:56  nistico
00967  * Supports ColorTable32K through CT32K_LAYOUT switch located into
00968  * GT2004ImageProcessorTools.h
00969  *
00970  * Revision 1.1  2004/05/04 13:40:19  tim
00971  * added GT2004ImageProcessor
00972  *
00973  */

Generated on Thu Sep 23 19:57:28 2004 for GT2004 by doxygen 1.3.6