00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "Tools/ColorClasses.h"
00020 #include "Tools/Math/Geometry.h"
00021 #include "Tools/Debugging/DebugImages.h"
00022 #include "Tools/Debugging/DebugDrawings.h"
00023 #include "Platform/GTAssert.h"
00024 #include "Representations/Perception/LandmarksPercept.h"
00025 #include "GT2005ImageProcessorTools.h"
00026 #include "GT2005GoalRecognizer.h"
00027
00028
00029
00030
00031 enum parameters
00032 {
00033
00034
00035
00036 maxScanLengthToGoalpostEdge = 14,
00037
00038
00039 minGoalpostHeight = 7
00040 };
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052 #ifdef NDEBUG
00053 #define HORIZON_ALIGNED_RECTANGLE(horizoninfo, id, x1, y1, x2, y2, penwidth, penstyle, pencolor, cross) ;
00054
00055 #else
00056 #define HORIZON_ALIGNED_RECTANGLE(horizoninfo, id, x1, y1, x2, y2, penwidth, penstyle, pencolor, cross) \
00057 { \
00058 Vector2<int> _topleft = horizoninfo.toImageCoordinates(x1, y1); \
00059 Vector2<int> _bottomleft = horizonInfo.toImageCoordinates(x1, y2); \
00060 Vector2<int> _bottomright = horizoninfo.toImageCoordinates(x2, y2); \
00061 Vector2<int> _topright = horizoninfo.toImageCoordinates(x2, y1); \
00062 QUADRANGLE(imageProcessor_flagsAndGoals, _topleft.x, _topleft.y, _bottomleft.x, _bottomleft.y, _bottomright.x, _bottomright.y, _topright.x, _topright.y, penwidth, penstyle, pencolor); \
00063 NQUADRANGLE(id, _topleft.x, _topleft.y, _bottomleft.x, _bottomleft.y, _bottomright.x, _bottomright.y, _topright.x, _topright.y, penwidth, penstyle, pencolor); \
00064 if (cross) \
00065 { \
00066 NLINE(id, _topleft.x, _topleft.y, _bottomright.x, _bottomright.y, penwidth, penstyle, pencolor); \
00067 NLINE(id, _bottomleft.x, _bottomleft.y, _topright.x, _topright.y, penwidth, penstyle, pencolor); \
00068 } \
00069 }
00070
00071 #endif
00072
00073
00074 GT2005GoalRecognizer::GT2005GoalRecognizer(colorClass color, const ImageProcessorInterfaces& interfaces, const ColorCorrector& colorCorrector, const ImageInfo& horizonInfo)
00075 : goalColor(color),
00076 interfaces(interfaces),
00077 colorCorrector(colorCorrector),
00078 horizonInfo(horizonInfo)
00079 {
00080 }
00081
00082 void GT2005GoalRecognizer::notifyAboutNewImage()
00083 {
00084
00085 detectionCounter = 0;
00086
00087
00088 lockAreaCount = 0;
00089 flagLockCount = 0;
00090 hypothesisCount = 0;
00091
00092
00093 frameNumberImage = interfaces.image.frameNumber;
00094
00095
00096
00097 if (yellow == goalColor)
00098 {
00099 INIT_DEBUG_IMAGE(imageProcessorGoal1, interfaces.image);
00100 N_INIT_DEBUG_GRAY_SCALE_IMAGE(goalRecognizerYellow, interfaces.image);
00101 }
00102 else
00103 {
00104 INIT_DEBUG_IMAGE(imageProcessorGoal2, interfaces.image);
00105 N_INIT_DEBUG_GRAY_SCALE_IMAGE(goalRecognizerBlue, interfaces.image);
00106 }
00107
00108 NDECLARE_DEBUGDRAWING("GoalRecognizer:goal fragments:all",
00109 "drawingOnImage",
00110 "Draws the bounding box of all goal fragment candidates in the image. Crossed out fragments were discarded half right after the analysis.");
00111 NDECLARE_DEBUGDRAWING("GoalRecognizer:goal fragments:merged",
00112 "drawingOnImage",
00113 "Draws the bounding box of goal fragments that are merged.");
00114 NDECLARE_DEBUGDRAWING("GoalRecognizer:goal fragments:result",
00115 "drawingOnImage",
00116 "Draws the bounding box of the found goal.");
00117 }
00118
00119 void GT2005GoalRecognizer::notifyAboutFlags()
00120 {
00121
00122 flagLockCount = min(interfaces.landmarksPercept.numberOfFlags, static_cast<int>(maxFlagLocks));
00123 for (int i = 0; i<flagLockCount; i++)
00124 {
00125
00126 flagLock[i].min = horizonInfo.horizonAlignedXOf(interfaces.landmarksPercept.flags[i].bottomLeft);
00127 flagLock[i].max = horizonInfo.horizonAlignedXOf(interfaces.landmarksPercept.flags[i].bottomRight);
00128
00129
00130 double width = flagLock[i].max - flagLock[i].min;
00131 flagLock[i].min -= (width + 3.0);
00132 flagLock[i].max += (width + 3.0);
00133 }
00134
00135
00136 frameNumberFlags = interfaces.landmarksPercept.frameNumber;
00137 }
00138
00139
00140 void GT2005GoalRecognizer::notifyAboutFinish()
00141 {
00142
00143 GT2005GoalRecognizer::EdgeDetector detector[2] =
00144 {
00145 GT2005GoalRecognizer::EdgeDetector(goalColor),
00146 GT2005GoalRecognizer::EdgeDetector(goalColor)
00147 };
00148
00149
00150 bool deletedHypothesis[maxHypothesises];
00151 mergeFragments(deletedHypothesis);
00152
00153
00154 interpretResults(detector, deletedHypothesis);
00155
00156
00157 if (yellow == goalColor)
00158 {
00159
00160 SEND_DEBUG_IMAGE(imageProcessorGoal1);
00161 N_SEND_DEBUG_GRAY_SCALE_IMAGE(goalRecognizerYellow);
00162 }
00163 else
00164 {
00165
00166 DEBUG_DRAWING_FINISHED(imageProcessor_flagsAndGoals);
00167 SEND_DEBUG_IMAGE(imageProcessorGoal2);
00168 N_SEND_DEBUG_GRAY_SCALE_IMAGE(goalRecognizerBlue);
00169 }
00170 }
00171
00172 bool GT2005GoalRecognizer::inspectNeighbourhood(const Vector2<int>& point)
00173 {
00174
00175 if (nearImageBorder(point, 0))
00176 return false;
00177
00178
00179 int targetCount = 0;
00180 for (int i = -1; i<=1; ++i)
00181 for (int j = -1; j<=1; ++j)
00182 if (goalColor == CORRECTED_COLOR_CLASS(point.x+i, point.y+j, interfaces.image.image[point.y+j][0][point.x+i], interfaces.image.image[point.y+j][1][point.x+i], interfaces.image.image[point.y+j][2][point.x+i], interfaces.colorTable, colorCorrector))
00183 {
00184
00185 ++targetCount;
00186 }
00187
00188
00189 return targetCount>4;
00190 }
00191
00192 void GT2005GoalRecognizer::analyzeGoal(Vector2<int> focus)
00193 {
00194
00195 GT2005GoalRecognizer::EdgeDetector detector[2] =
00196 {
00197 GT2005GoalRecognizer::EdgeDetector(goalColor),
00198 GT2005GoalRecognizer::EdgeDetector(goalColor)
00199 };
00200
00201
00202
00203
00204 bool imageBorderScanned[4] = {false, false, false, false};
00205
00206
00207 GT2005GoalRecognizer::GoalHypothesis result;
00208
00209
00210
00211 analyzeGoalpost(detector, -horizonInfo.horizon.direction, true, focus, result.goalpost[0], imageBorderScanned);
00212
00213
00214
00215 scanCrossBar(detector, focus, result, imageBorderScanned);
00216
00217
00218 result.width = static_cast<int>(result.crossBarEndpointH.x-result.goalpost[0].edgePointH.x);
00219 if (result.goalpost[0].height+result.width < 15 || result.width <= 5)
00220 {
00221
00222 lockArea(result.crossBarEndpointH.x + 10, result.goalpost[0].bottomPointH.y + 10, result.goalpost[0].onGreen);
00223
00224 HORIZON_ALIGNED_RECTANGLE(horizonInfo, "GoalRecognizer:goal fragments:all",
00225 result.goalpost[0].edgePointH.x, min(result.goalpost[0].topPointH.y, result.crossBarEndpointH.y),
00226 result.crossBarEndpointH.x, result.goalpost[0].bottomPointH.y,
00227 1, Drawings::ps_solid, Drawings::red, true);
00228 return;
00229 }
00230
00231
00232
00233 analyzeGoalpost(detector, horizonInfo.horizon.direction, false, focus, result.goalpost[1], imageBorderScanned);
00234
00235
00236
00237 if (result.crossBarEndpointH.y < result.goalpost[1].topPointH.y)
00238 {
00239 result.goalpost[1].topPoint = result.crossBarEndpoint;
00240 result.goalpost[1].topPointH = result.crossBarEndpointH;
00241 result.goalpost[1].height = static_cast<int>(result.goalpost[1].bottomPointH.y - result.goalpost[1].topPointH.y);
00242 }
00243
00244
00245 result.width = static_cast<int>(result.goalpost[1].edgePointH.x - result.goalpost[0].edgePointH.x);
00246 result.height = max(result.goalpost[0].height, result.goalpost[1].height);
00247
00248
00249 result.onGreen = result.goalpost[0].onGreen || result.goalpost[1].onGreen;
00250
00251 HORIZON_ALIGNED_RECTANGLE(horizonInfo, "GoalRecognizer:goal fragments:all",
00252 result.goalpost[0].edgePointH.x, min(min(result.goalpost[0].topPointH.y, result.goalpost[1].topPointH.y), result.crossBarEndpointH.y),
00253 result.goalpost[1].edgePointH.x, max(result.goalpost[0].bottomPointH.y, result.goalpost[1].bottomPointH.y),
00254 1, Drawings::ps_solid, Drawings::red, false);
00255
00256
00257
00258 lockArea(result.goalpost[1].edgePoint.x + 5, max(result.goalpost[0].bottomPointH.y, result.goalpost[1].bottomPointH.y) + 5, result.goalpost[0].onGreen);
00259
00260
00261 if (hypothesisCount < maxHypothesises)
00262 hypothesis[hypothesisCount++] = result;
00263 }
00264
00265 void GT2005GoalRecognizer::analyzeGoalpost(GT2005GoalRecognizer::EdgeDetector* detector,
00266 const Vector2<double>& directionOfEdge,
00267 bool leftGoalpost,
00268 Vector2<int>& focus,
00269 GT2005GoalRecognizer::Goalpost& result,
00270 bool* imageBorderScanned)
00271 {
00272
00273 EdgePointList edgePoints;
00274
00275
00276 bool borderScan = false;
00277
00278
00279
00280
00281
00282
00283 bool detectionCanceled = false;
00284
00285
00286
00287
00288 const double verticalScanSlope = 0.2;
00289 BresenhamLineScan horizontalScan(directionOfEdge);
00290 BresenhamLineScan verticalScan(horizonInfo.vertLine.direction - directionOfEdge*verticalScanSlope);
00291
00292
00293
00294 Vector2<int> horizontalScanOffset(static_cast<int>(floor(-horizonInfo.vertLine.direction.x*2+0.5)),
00295 static_cast<int>(floor(-horizonInfo.vertLine.direction.y*2+0.5)));
00296 Vector2<int> verticalScanOffset(static_cast<int>(floor(-directionOfEdge.x+0.5)),
00297 static_cast<int>(floor(-directionOfEdge.y+0.5)));
00298
00299
00300 detector[0].clear(focus, false);
00301 detector[1].clear(focus, true);
00302 switch (detectEdgeTwice(detector, focus, horizontalScan, horizontalScanOffset, maxScanLengthToGoalpostEdge, edgePoints))
00303 {
00304 case imageBorder:
00305
00306
00307 borderScan = nearImageBorder(focus, 1);
00308
00309
00310
00311
00312
00313
00314
00315
00316 break;
00317
00318 case none:
00319
00320
00321
00322 detectionCanceled = true;
00323 }
00324
00325
00326 Vector2<int> firstEdgePoint = focus;
00327
00328
00329 GT2005GoalRecognizer::ImageBorderSide firstEdgeImageBorder = noBorder;
00330
00331
00332
00333 if (borderScan)
00334 {
00335
00336
00337
00338 firstEdgeImageBorder =
00339 scanOnImageBorder(detector, focus, horizonInfo.vertLine.direction, 3, imageBorderScanned);
00340 borderScan = false;
00341
00342 if (firstEdgeImageBorder != noBorder)
00343 {
00344
00345 edgePoints.add(focus, imageBorder);
00346
00347
00348 focus += verticalScanOffset*2;
00349 horizonInfo.clipToImageBoundary(focus);
00350 }
00351 }
00352
00353 for (int i = 0; !detectionCanceled && i<25; ++i)
00354 {
00355 DEBUG_RESPONSE ("gt05-ip:GoalRecognizer:assert loop termination",
00356 ASSERT(i<23);
00357 );
00358
00359 if (borderScan)
00360 {
00361
00362 if (noBorder == scanOnImageBorder(detector, focus, horizonInfo.vertLine.direction, 3, imageBorderScanned))
00363 {
00364
00365
00366
00367 break;
00368 }
00369 borderScan = false;
00370
00371
00372 edgePoints.add(focus, imageBorder);
00373
00374
00375 focus += verticalScanOffset*2;
00376 horizonInfo.clipToImageBoundary(focus);
00377 }
00378 else
00379 {
00380
00381 detector[0].clear(focus, true);
00382 detector[1].clear(focus, true);
00383 int scanned;
00384 if (scanAlongLine(detector, focus, verticalScan, verticalScanOffset, 20, scanned) == imageBorder)
00385 {
00386
00387
00388
00389
00390 borderScan = true;
00391
00392
00393 edgePoints.add(focus, imageBorder);
00394 continue;
00395 }
00396
00397
00398 if (scanned>4)
00399 {
00400
00401 detector[0].clear(focus, true);
00402 detector[1].clear(focus, true);
00403 switch(detectEdgeTwice(detector, focus, horizontalScan, horizontalScanOffset, maxScanLengthToGoalpostEdge, edgePoints))
00404 {
00405 case imageBorder:
00406
00407 borderScan = nearImageBorder(focus, 1);
00408 break;
00409
00410 case none:
00411
00412 detectionCanceled = true;
00413 }
00414 }
00415
00416
00417 if (scanned<8)
00418 break;
00419 }
00420 }
00421
00422
00423 result.bottomPoint = focus;
00424 result.bottomPointH = horizonInfo.toHorizonAligned(focus);
00425
00426
00427
00428
00429 detector[1].getReferenceColor(result.color);
00430
00431
00432
00433
00434 focus = firstEdgePoint;
00435
00436 if (leftGoalpost && !detectionCanceled)
00437 {
00438 verticalScan = BresenhamLineScan(-horizonInfo.vertLine.direction - directionOfEdge*verticalScanSlope);
00439 horizontalScanOffset = -horizontalScanOffset;
00440 borderScan = false;
00441
00442 if (firstEdgeImageBorder != noBorder)
00443 {
00444
00445
00446
00447
00448
00449 imageBorderScanned[firstEdgeImageBorder] = false;
00450
00451
00452 if (noBorder != scanOnImageBorder(detector, focus, -horizonInfo.vertLine.direction, 3, imageBorderScanned))
00453 {
00454
00455 edgePoints.add(focus, imageBorder);
00456
00457
00458 focus += verticalScanOffset*2;
00459 horizonInfo.clipToImageBoundary(focus);
00460 }
00461
00462
00463
00464
00465 imageBorderScanned[firstEdgeImageBorder] = true;
00466 }
00467
00468 for (int i = 0; !detectionCanceled && i<12; ++i)
00469 {
00470 DEBUG_RESPONSE ("gt05-ip:GoalRecognizer:assert loop termination",
00471 ASSERT(i<10);
00472 );
00473
00474 if (borderScan)
00475 {
00476
00477 if (noBorder == scanOnImageBorder(detector, focus, horizonInfo.vertLine.direction, 3, imageBorderScanned))
00478 {
00479
00480
00481
00482
00483 break;
00484 }
00485 borderScan = false;
00486
00487
00488 edgePoints.add(focus, imageBorder);
00489
00490
00491 focus += verticalScanOffset*2;
00492 horizonInfo.clipToImageBoundary(focus);
00493 }
00494 else
00495 {
00496
00497 detector[0].clear(focus, true);
00498 detector[1].clear(focus, true);
00499 int scanned;
00500 if (scanAlongLine(detector, focus, verticalScan, verticalScanOffset, 20, scanned) == imageBorder)
00501 {
00502
00503 edgePoints.add(focus, imageBorder);
00504
00505
00506 borderScan = true;
00507 continue;
00508 }
00509
00510
00511 if (scanned>8)
00512 {
00513
00514 detector[0].clear(focus, true);
00515 detector[1].clear(focus, true);
00516 switch(detectEdgeTwice(detector, focus, horizontalScan, horizontalScanOffset, maxScanLengthToGoalpostEdge, edgePoints))
00517 {
00518 case imageBorder:
00519
00520 borderScan = true;
00521 break;
00522
00523 case none:
00524
00525 detectionCanceled = true;
00526 }
00527 }
00528 else
00529 break;
00530 }
00531
00532 }
00533 }
00534
00535
00536
00537 if (detectionCanceled && leftGoalpost)
00538 {
00539 verticalScan = BresenhamLineScan(-horizonInfo.vertLine.direction);
00540
00541
00542
00543 detector[0].clear(focus, true);
00544 detector[1].clear(focus, true);
00545
00546 int dummy;
00547 if (scanAlongLine(detector, focus, verticalScan, verticalScanOffset, 1024, dummy) == imageBorder)
00548 {
00549
00550 detector[0].clear(focus, false);
00551 detector[1].clear(focus, false);
00552 scanOnImageBorder(detector, focus, horizonInfo.vertLine.direction, 3, imageBorderScanned);
00553 }
00554 }
00555
00556
00557
00558 result.topPoint = focus;
00559 result.topPointH = horizonInfo.toHorizonAligned(focus);
00560
00561
00562 result.nonExistant = detectionCanceled;
00563
00564 if (detectionCanceled)
00565 {
00566
00567 result.height = 0;
00568 result.visibleHeight = 0;
00569 result.edgePoint = result.topPoint;
00570 result.edgePointH = result.topPointH;
00571 result.onGreen = false;
00572 }
00573 else
00574 {
00575
00576 result.height = static_cast<int>(result.bottomPointH.y - result.topPointH.y);
00577
00578
00579
00580 if (result.height > minGoalpostHeight)
00581 result.onGreen = detectGreenBelowGoalpost(result.bottomPoint, directionOfEdge, result.height);
00582 else
00583 result.onGreen = false;
00584
00585
00586
00587 result.visibleHeight = edgePoints.getCount(edge)+edgePoints.getCount(deviationWithoutEdge)>0 ? 1 : 0;
00588
00589 if (result.visibleHeight > 0)
00590 {
00591
00592
00593
00594
00595
00596 for (int i = 0; i<edgePoints.size(); ++i)
00597 {
00598 switch (edgePoints.getType(i))
00599 {
00600 case edge:
00601 case deviationWithoutEdge:
00602 result.edgePoint += edgePoints[i];
00603 }
00604 }
00605 result.edgePoint /= edgePoints.getCount(edge)+edgePoints.getCount(deviationWithoutEdge);
00606 result.edgePointH = horizonInfo.invRotation * Vector2<double>(static_cast<double>(result.edgePoint.x),
00607 static_cast<double>(result.edgePoint.y));
00608
00609
00610 result.strongEdgeCount = edgePoints.getCount(edge);
00611 result.weakEdgeCount = edgePoints.getCount(deviationWithoutEdge);
00612 }
00613 else
00614 {
00615 ASSERT(edgePoints.getCount(imageBorder)>0);
00616
00617
00618 double extremeHX = leftGoalpost ? 1024.0f : -1024.0f;
00619
00620 for (int i = 0; i<edgePoints.size(); ++i)
00621 {
00622 if (edgePoints.getType(i) == imageBorder)
00623 {
00624
00625 double HX = horizonInfo.invRotation.c[0].x * edgePoints[i].x
00626 + horizonInfo.invRotation.c[1].x * edgePoints[i].y;
00627 if ((HX < extremeHX) == leftGoalpost)
00628 {
00629
00630
00631
00632 result.edgePoint = edgePoints[i];
00633 result.edgePointH.x = HX;
00634 extremeHX = HX;
00635 }
00636 }
00637 }
00638
00639
00640 result.edgePointH.y = horizonInfo.invRotation.c[0].y * result.edgePoint.x
00641 + horizonInfo.invRotation.c[1].y * result.edgePoint.y;
00642 }
00643 }
00644
00645
00646
00647
00648
00649
00650 }
00651
00652 GT2005GoalRecognizer::EdgeType GT2005GoalRecognizer::detectEdgeTwice(GT2005GoalRecognizer::EdgeDetector* detector,
00653 Vector2<int>& focus,
00654 BresenhamLineScan& scanLine,
00655 const Vector2<int>& scanLineOffset,
00656 int maxScanLength,
00657 EdgePointList& edgePoints)
00658 {
00659
00660 Vector2<int> focus0 = focus + scanLineOffset;
00661
00662 int distance[2];
00663 bool onImageBorder[2];
00664
00665
00666 GT2005GoalRecognizer::EdgeType type =
00667 detectEdge(detector[0], focus0, scanLine, maxScanLength, distance[0]);
00668 if (type == none)
00669 {
00670
00671 return none;
00672 }
00673 else
00674 {
00675
00676 edgePoints.add(focus0, type);
00677 onImageBorder[0] = type == imageBorder;
00678 }
00679
00680
00681 scanLine.init();
00682 Vector2<int> focus1 = focus;
00683 type = detectEdge(detector[1], focus1, scanLine, maxScanLength, distance[1]);
00684 if (type == none)
00685 {
00686
00687 return none;
00688 }
00689 else
00690 {
00691
00692 edgePoints.add(focus1, type);
00693 onImageBorder[1] = type == imageBorder;
00694 }
00695
00696
00697 if (onImageBorder[0])
00698 {
00699 if (onImageBorder[1])
00700 {
00701
00702 if (distance[0]<distance[1])
00703 focus = focus0;
00704 else
00705 focus = focus1;
00706
00707
00708 return imageBorder;
00709 }
00710 else
00711 {
00712
00713 focus = focus1;
00714 return edge;
00715 }
00716 }
00717 else
00718 {
00719 if (onImageBorder[1])
00720 {
00721
00722 focus = focus0;
00723 return edge;
00724 }
00725 else
00726 {
00727
00728 if (distance[0]<distance[1])
00729 focus = focus0;
00730 else
00731 focus = focus1;
00732
00733
00734 return edge;
00735 }
00736 }
00737 }
00738
00739 GT2005GoalRecognizer::ImageBorderSide GT2005GoalRecognizer::scanOnImageBorder(
00740 GT2005GoalRecognizer::EdgeDetector* detector,
00741 Vector2<int>& focus,
00742 const Vector2<double>& targetDirection,
00743 int maxBorderDistance,
00744 bool* scannedSides)
00745 {
00746
00747
00748 ASSERT(maxBorderDistance < horizonInfo.maxImageCoordinates.x/2 &&
00749 maxBorderDistance < horizonInfo.maxImageCoordinates.y/2);
00750
00751
00752
00753
00754
00755
00756 bool nearBorder[4] = {false, false, false, false};
00757 int nearCount = 0;
00758 bool scannedBefore[2];
00759 Vector2<double> scanDirection[2];
00760 double awayFromEdgeFactor = 1.0;
00761
00762 if (focus.y <= maxBorderDistance)
00763 {
00764
00765 nearBorder[topBorder] = true;
00766 scannedBefore[nearCount] = scannedSides[topBorder];
00767 scanDirection[nearCount++].x = awayFromEdgeFactor;
00768 awayFromEdgeFactor *= -1.0;
00769
00770 }
00771 if (focus.x <= maxBorderDistance)
00772 {
00773
00774 nearBorder[leftBorder] = true;
00775 scannedBefore[nearCount] = scannedSides[leftBorder];
00776 scanDirection[nearCount++].y = -awayFromEdgeFactor;
00777 awayFromEdgeFactor *= -1.0;
00778 }
00779 if (horizonInfo.maxImageCoordinates.y-focus.y <= maxBorderDistance)
00780 {
00781
00782 nearBorder[bottomBorder] = true;
00783 scannedBefore[nearCount] = scannedSides[bottomBorder];
00784 scanDirection[nearCount++].x = -awayFromEdgeFactor;
00785 awayFromEdgeFactor *= -1.0;
00786 }
00787 if (horizonInfo.maxImageCoordinates.x-focus.x <= maxBorderDistance)
00788 {
00789
00790 nearBorder[rightBorder] = true;
00791 scannedBefore[nearCount] = scannedSides[rightBorder];
00792 scanDirection[nearCount++].y = awayFromEdgeFactor;
00793
00794 }
00795
00796
00797
00798 if (nearBorder[topBorder] && nearBorder[rightBorder])
00799 {
00800 scanDirection[0] = -scanDirection[0];
00801 scanDirection[1] = -scanDirection[1];
00802 }
00803
00804
00805 switch (nearCount)
00806 {
00807 case 0:
00808
00809 return noBorder;
00810
00811 case 1:
00812 {
00813
00814
00815
00816 double dotProduct = targetDirection*scanDirection[0];
00817 if (scannedBefore[0] || fabs(dotProduct) < 0.5)
00818 return noBorder;
00819
00820
00821 scanDirection[0] *= sgn(dotProduct);
00822 break;
00823 }
00824 case 2:
00825 default:
00826
00827
00828
00829 if (!scannedBefore[0] && targetDirection*scanDirection[0] > 0.5)
00830 {
00831
00832 }
00833 else if (!scannedBefore[1] && targetDirection*scanDirection[1] > 0.5)
00834 {
00835 scanDirection[0] = scanDirection[1];
00836 }
00837 else
00838 {
00839
00840 return noBorder;
00841 }
00842 }
00843 ASSERT(scanDirection[0].x*scanDirection[0].y==0.0);
00844
00845
00846 Vector2<int> newFocus = focus;
00847 Vector2<int> scanOffset(0, 0);
00848 GT2005GoalRecognizer::ImageBorderSide scanSide;
00849
00850 if (nearBorder[topBorder] && scanDirection[0].y==0.0)
00851 {
00852
00853 newFocus.y = 0;
00854 scanOffset.y = 1;
00855 scanSide = topBorder;
00856 }
00857 else if (nearBorder[leftBorder] && scanDirection[0].x==0.0)
00858 {
00859
00860 newFocus.x = 0;
00861 scanOffset.x = 1;
00862 scanSide = leftBorder;
00863 }
00864 else if (nearBorder[bottomBorder] && scanDirection[0].y==0.0)
00865 {
00866
00867 newFocus.y = horizonInfo.maxImageCoordinates.y;
00868 scanOffset.y = -1;
00869 scanSide = bottomBorder;
00870 }
00871 else
00872 {
00873
00874 newFocus.x = horizonInfo.maxImageCoordinates.x;
00875 scanOffset.x = -1;
00876 scanSide = rightBorder;
00877 }
00878
00879
00880
00881
00882
00883
00884 BresenhamLineScan scanLine(scanDirection[0]);
00885 int scannedCount;
00886 scanAlongLine(detector, newFocus, scanLine, scanOffset, 1024, scannedCount);
00887
00888
00889 if (scannedCount < 0)
00890 {
00891
00892 return noBorder;
00893 }
00894 else
00895 {
00896
00897 scannedSides[scanSide] = true;
00898
00899
00900 LINE(imageProcessor_flagsAndGoals, focus.x, focus.y, newFocus.x, newFocus.y, 1, Drawings::ps_solid, Drawings::orange);
00901 DOT(imageProcessor_flagsAndGoals, focus.x, focus.y, Drawings::white, Drawings::white);
00902 DOT(imageProcessor_flagsAndGoals, newFocus.x, newFocus.y, Drawings::orange, Drawings::orange);
00903 focus = newFocus;
00904 return scanSide;
00905 }
00906 }
00907
00908
00909
00910 void GT2005GoalRecognizer::scanCrossBar(GT2005GoalRecognizer::EdgeDetector* detector,
00911 Vector2<int>& focus,
00912 GT2005GoalRecognizer::GoalHypothesis& result,
00913 bool* imageBorderScanned)
00914 {
00915 DOT(imageProcessor_flagsAndGoals, focus.x, focus.y, Drawings::orange, Drawings::orange);
00916
00917
00918 result.crossBarInImage = false;
00919
00920
00921 if (nearImageBorder(focus, 3))
00922 {
00923
00924 scanOnImageBorder(detector, focus, horizonInfo.horizon.direction, 3, imageBorderScanned);
00925 }
00926 bool borderScan = false;
00927 bool nonBorderScanDone = false;
00928
00929 DOT(imageProcessor_flagsAndGoals, focus.x, focus.y, pink, pink);
00930
00931
00932
00933 BresenhamLineScan upwardsScan(-horizonInfo.vertLine.direction);
00934
00935
00936 bool calculateScanLine = false;
00937
00938
00939 Vector2<int> lastEdgePoint = focus;
00940
00941
00942
00943 Vector2<double> scanDirection = horizonInfo.horizon.direction;
00944 BresenhamLineScan horizontalScan(scanDirection);
00945 Vector2<int> horizontalScanOffset(static_cast<int>(floor(horizonInfo.vertLine.direction.x+0.5)),
00946 static_cast<int>(floor(horizonInfo.vertLine.direction.y+0.5)));
00947 focus += horizontalScanOffset;
00948 detector[0].clear(focus, true);
00949 detector[1].clear(focus, true);
00950 int scanned;
00951
00952 switch (scanAlongLine(detector, focus, horizontalScan, horizontalScanOffset, 12, scanned))
00953 {
00954 case GT2005GoalRecognizer::imageBorder:
00955
00956 borderScan = true;
00957
00958 case GT2005GoalRecognizer::edge:
00959 case GT2005GoalRecognizer::deviationWithoutEdge:
00960 if (scanned < 6)
00961 {
00962
00963 focus = lastEdgePoint + Vector2<int>(static_cast<int>(floor(horizonInfo.vertLine.direction.x*5 + horizonInfo.horizon.direction.x*2 + 0.5)),
00964 static_cast<int>(floor(horizonInfo.vertLine.direction.y*5 + horizonInfo.horizon.direction.y*2 + 0.5)));
00965 if (scanAlongLine(detector, focus, horizontalScan, horizontalScanOffset, 12, scanned) == imageBorder)
00966 {
00967
00968 borderScan = true;
00969 }
00970 }
00971
00972
00973 calculateScanLine = true;
00974 }
00975
00976
00977
00978 for (int i = 0; !borderScan && i<25; ++i)
00979 {
00980 DEBUG_RESPONSE ("gt05-ip:GoalRecognizer:assert loop termination",
00981 ASSERT(i<23);
00982 );
00983
00984
00985
00986 nonBorderScanDone = true;
00987
00988
00989 detector[1].clear();
00990 int scanTillEdgeLength;
00991 if (detectEdge(detector[1], focus, upwardsScan, 10, scanTillEdgeLength) == imageBorder)
00992 {
00993
00994 if (borderScan = nearImageBorder(focus, 1))
00995 break;
00996
00997
00998
00999
01000 }
01001 else
01002 {
01003
01004 result.crossBarInImage = true;
01005 }
01006
01007
01008 if (calculateScanLine || scanTillEdgeLength>2)
01009 {
01010
01011
01012
01013
01014 horizontalScan = BresenhamLineScan(lastEdgePoint, focus);
01015 scanDirection.x = static_cast<double>(focus.x-lastEdgePoint.x);
01016 scanDirection.y = static_cast<double>(focus.y-lastEdgePoint.y);
01017 scanDirection.normalize();
01018 Vector2<int> horizontalScanOffset(static_cast<int>(floor(-scanDirection.y+0.5)),
01019 static_cast<int>(floor( scanDirection.x+0.5)));
01020
01021
01022
01023
01024 if (horizonInfo.horizon.direction*scanDirection < 0.5)
01025 break;
01026 }
01027
01028
01029 focus += horizontalScanOffset;
01030 detector[0].clear();
01031 detector[1].clear();
01032 switch (scanAlongLine(detector, focus, horizontalScan, horizontalScanOffset, 12, scanned))
01033 {
01034 case GT2005GoalRecognizer::imageBorder:
01035
01036 borderScan = true;
01037 break;
01038
01039 case GT2005GoalRecognizer::edge:
01040 case GT2005GoalRecognizer::deviationWithoutEdge:
01041
01042
01043
01044
01045
01046
01047
01048
01049
01050 focus = focus - Vector2<int>(static_cast<int>(scanDirection.x*4),
01051 static_cast<int>(scanDirection.y*4));
01052 result.crossBarEndpoint = focus;
01053 result.crossBarEndpointH = horizonInfo.toHorizonAligned(focus);
01054 return;
01055 }
01056 }
01057
01058
01059
01060
01061 result.crossBarEndpoint = focus;
01062 result.crossBarEndpointH = horizonInfo.toHorizonAligned(focus);
01063
01064
01065
01066 if (nonBorderScanDone && borderScan)
01067 scanOnImageBorder(detector, focus, horizonInfo.horizon.direction, 3, imageBorderScanned);
01068 }
01069
01070 GT2005GoalRecognizer::EdgeType GT2005GoalRecognizer::detectEdge(GT2005GoalRecognizer::EdgeDetector& detector,
01071 Vector2<int>& focus,
01072 BresenhamLineScan& scanLine,
01073 int maxScanLength,
01074 int& pixelsUntilEdge)
01075 {
01076
01077 scanLine.init();
01078
01079
01080 int i;
01081 for (i = 0; i<maxScanLength && focus.x>=0 && focus.y>=0 && focus.x<=horizonInfo.maxImageCoordinates.x && focus.y<=horizonInfo.maxImageCoordinates.y; ++i)
01082 {
01083
01084 unsigned char color[3] =
01085 {
01086 colorCorrector.correct(focus.x, focus.y, 0, interfaces.image.image[focus.y][0][focus.x]),
01087 colorCorrector.correct(focus.x, focus.y, 1, interfaces.image.image[focus.y][1][focus.x]),
01088 colorCorrector.correct(focus.x, focus.y, 2, interfaces.image.image[focus.y][2][focus.x])
01089 };
01090 colorClass colorCls = COLOR_CLASS(color[0], color[1], color[2], interfaces.colorTable);
01091
01092 switch (detector.inspectPixel(focus, i, colorCls, Vector3<int>(static_cast<int>(color[0]), static_cast<int>(color[1]), static_cast<int>(color[2]))))
01093 {
01094 case edge:
01095 DOT(imageProcessor_flagsAndGoals, detector.getLastPointInClass().x, detector.getLastPointInClass().y, Drawings::black, Drawings::green);
01096 STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, focus.x, focus.y, GREEN);
01097 N_STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, focus.x, focus.y, green);
01098
01099
01100 detector.getLastPointInClass(focus, pixelsUntilEdge);
01101 return edge;
01102 break;
01103
01104 case deviationWithoutEdge:
01105 DOT(imageProcessor_flagsAndGoals, detector.getLastPointInClass().x, detector.getLastPointInClass().y, Drawings::black, Drawings::white);
01106 STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, focus.x, focus.y, RED);
01107 N_STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, focus.x, focus.y, yellowOrange);
01108
01109
01110 detector.getLastPointInClass(focus, pixelsUntilEdge);
01111 return deviationWithoutEdge;
01112 break;
01113
01114 default:
01115 STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, focus.x, focus.y, BLACK);
01116 N_STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, focus.x, focus.y, colorCls==goalColor? goalColor : ( goalColor==skyblue?blue:orange ));
01117 }
01118
01119
01120 scanLine.getNext(focus);
01121 }
01122
01123 if (i==maxScanLength)
01124 {
01125
01126 DOT(imageProcessor_flagsAndGoals, detector.getLastPointInClass().x, detector.getLastPointInClass().y, Drawings::black, Drawings::red);
01127 return none;
01128 }
01129 else
01130 {
01131
01132 detector.getLastPointInClass(focus, pixelsUntilEdge);
01133 return imageBorder;
01134 }
01135 }
01136
01137 GT2005GoalRecognizer::EdgeType GT2005GoalRecognizer::scanAlongLine(
01138 GT2005GoalRecognizer::EdgeDetector* detector,
01139 Vector2<int>& focus,
01140 BresenhamLineScan& scanLine,
01141 const Vector2<int>& scanLineOffset,
01142 int maximumScan,
01143 int& pixelsScanned)
01144 {
01145
01146
01147 bool edge[2];
01148 bool withinBounds[2];
01149
01150
01151 unsigned char color[3];
01152 colorClass colorCls;
01153 Vector2<int> focus2;
01154
01155
01156 for (int i = 0; i <= maximumScan; ++i)
01157 {
01158
01159 if (withinBounds[0] = focus.x>=0 && focus.y>=0 && focus.x<=horizonInfo.maxImageCoordinates.x && focus.y<=horizonInfo.maxImageCoordinates.y)
01160 {
01161
01162 color[0] = colorCorrector.correct(focus.x, focus.y, 0, interfaces.image.image[focus.y][0][focus.x]);
01163 color[1] = colorCorrector.correct(focus.x, focus.y, 1, interfaces.image.image[focus.y][1][focus.x]);
01164 color[2] = colorCorrector.correct(focus.x, focus.y, 2, interfaces.image.image[focus.y][2][focus.x]);
01165 colorCls = COLOR_CLASS(color[0], color[1], color[2], interfaces.colorTable);
01166
01167
01168 edge[0] = none != detector[0].inspectPixel(focus, i, colorCls, Vector3<int>(static_cast<int>(color[0]), static_cast<int>(color[1]), static_cast<int>(color[2])));
01169
01170 if (edge[0])
01171 {
01172 STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, focus.x, focus.y, WHITE);
01173 N_STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, focus.x, focus.y, black);
01174 }
01175 else
01176 {
01177 STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, focus.x, focus.y, BLUE);
01178 N_STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, focus.x, focus.y, colorCls==goalColor? goalColor : ( goalColor==skyblue?blue:orange ));
01179 }
01180 }
01181
01182
01183 focus2 = focus + scanLineOffset;
01184 if (withinBounds[1] = focus2.x>=0 && focus2.y>=0 && focus2.x<=horizonInfo.maxImageCoordinates.x && focus2.y<=horizonInfo.maxImageCoordinates.y)
01185 {
01186
01187 color[0] = colorCorrector.correct(focus2.x, focus2.y, 0, interfaces.image.image[focus2.y][0][focus2.x]);
01188 color[1] = colorCorrector.correct(focus2.x, focus2.y, 1, interfaces.image.image[focus2.y][1][focus2.x]);
01189 color[2] = colorCorrector.correct(focus2.x, focus2.y, 2, interfaces.image.image[focus2.y][2][focus2.x]);
01190 colorCls = COLOR_CLASS(color[0], color[1], color[2], interfaces.colorTable);
01191
01192
01193 edge[1] = none != detector[1].inspectPixel(focus2, i, colorCls, Vector3<int>(static_cast<int>(color[0]), static_cast<int>(color[1]), static_cast<int>(color[2])));
01194
01195 if (edge[1])
01196 {
01197 STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, focus2.x, focus2.y, WHITE);
01198 N_STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, focus2.x, focus2.y, black);
01199 }
01200 else
01201 {
01202 STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, focus2.x, focus2.y, BLUE);
01203 N_STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, focus2.x, focus2.y, colorCls==goalColor? goalColor : ( goalColor==skyblue?blue:orange ));
01204 }
01205 }
01206
01207
01208 if (withinBounds[0] && withinBounds[1])
01209 {
01210 if (edge[0] && edge[1])
01211 {
01212
01213 int scannedFurther = detector[0].getLastIndexInClass()>detector[1].getLastIndexInClass() ? 0 : 1;
01214 detector[scannedFurther].getLastPointInClass(focus, pixelsScanned);
01215
01216
01217
01218 return GT2005GoalRecognizer::edge;
01219 }
01220 }
01221 else
01222 {
01223
01224 int scannedFurther = detector[0].getLastIndexInClass()>detector[1].getLastIndexInClass() ? 0 : 1;
01225 detector[scannedFurther].getLastPointInClass(focus, pixelsScanned);
01226 return GT2005GoalRecognizer::imageBorder;
01227 }
01228
01229
01230 scanLine.getNext(focus);
01231 }
01232
01233
01234
01235
01236 int scannedFurther = detector[0].getLastIndexInClass()>detector[1].getLastIndexInClass() ? 0 : 1;
01237 detector[scannedFurther].getLastPointInClass(focus, pixelsScanned);
01238 return GT2005GoalRecognizer::none;
01239 }
01240
01241 bool GT2005GoalRecognizer::detectGreenBelowGoalpost(Vector2<int> focus, const Vector2<double>& directionOfEdge, int goalpostHeight)
01242 {
01243
01244 int scanLineCount = 1 + max(2, goalpostHeight/16);
01245
01246
01247
01248
01249
01250 BresenhamLineScan scanLine(-directionOfEdge);
01251
01252 focus.y += static_cast<int>(directionOfEdge.y * 5);
01253
01254
01255 Vector2<int> verticalOffset(static_cast<int>(floor(horizonInfo.vertLine.direction.x*2+0.5)),
01256 static_cast<int>(floor(horizonInfo.vertLine.direction.y*2+0.5)));
01257
01258
01259 int greenCount = 0, pixelCount = 0;
01260 colorClass color;
01261 for (int i = 0; i < scanLineCount; ++i)
01262 {
01263
01264 focus += verticalOffset;
01265
01266
01267 greenCount = pixelCount = 0;
01268 scanLine.init();
01269 Vector2<int> focus2 = focus;
01270 for (int j = 0; pixelCount < 5; ++j)
01271 {
01272
01273 scanLine.getNext(focus2);
01274
01275
01276 if (focus2.x >= 0 && focus2.y >= 0 && focus2.x <= horizonInfo.maxImageCoordinates.x && focus2.y <= horizonInfo.maxImageCoordinates.y)
01277 {
01278 ++pixelCount;
01279
01280
01281 color = CORRECTED_COLOR_CLASS(focus2.x, focus2.y, interfaces.image.image[focus2.y][0][focus2.x], interfaces.image.image[focus2.y][1][focus2.x], interfaces.image.image[focus2.y][2][focus2.x], interfaces.colorTable, colorCorrector);
01282 if (color == green)
01283 {
01284 ++greenCount;
01285 STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, focus2.x, focus2.y, YELLOW);
01286 N_STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, focus2.x, focus2.y, green);
01287 }
01288 else
01289 {
01290 STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, focus2.x, focus2.y, GRAY);
01291 N_STUPID_DEBUG_IMAGE_SET_PIXEL(goalColor, focus2.x, focus2.y, white);
01292 }
01293 }
01294 else
01295 {
01296
01297 if (j>=8)
01298 break;
01299 }
01300 }
01301
01302
01303 if (greenCount >= 3)
01304 return true;
01305 }
01306
01307
01308 return false;
01309 }
01310
01311 void GT2005GoalRecognizer::lockArea(double x, double y, bool onGreen)
01312 {
01313
01314 if (onGreen)
01315 y = 1024.0;
01316
01317
01318
01319
01320
01321 while (lockAreaCount>0)
01322 {
01323 if (x >= lockAreaStack[lockAreaCount-1].x && y >= lockAreaStack[lockAreaCount-1].y+8)
01324
01325 --lockAreaCount;
01326 else
01327 break;
01328 }
01329
01330
01331 if (lockAreaCount == lockAreaStackSize-1)
01332 {
01333
01334 lockAreaStack[lockAreaCount].x = 1024.0;
01335 lockAreaStack[lockAreaCount].y = y;
01336 }
01337 else if (lockAreaCount < lockAreaStackSize-1)
01338 {
01339
01340 lockAreaStack[lockAreaCount].x = x;
01341 lockAreaStack[lockAreaCount].y = y;
01342 ++lockAreaCount;
01343 }
01344
01345 }
01346
01347 void GT2005GoalRecognizer::calculateLockedPixels(const Vector2<int>& scanLineStart)
01348 {
01349
01350 Vector2<double> startH = horizonInfo.toHorizonAligned(scanLineStart);
01351
01352
01353 if (frameNumberImage == frameNumberFlags)
01354 for (int i = 0; i < flagLockCount; i++)
01355 {
01356 if (flagLock[i].isInside(startH.x))
01357 {
01358
01359 lockedPixels = 1024;
01360 return;
01361 }
01362 }
01363
01364
01365
01366 while (lockAreaCount>0)
01367 {
01368 if (lockAreaStack[lockAreaCount-1].x < startH.x)
01369
01370 --lockAreaCount;
01371 else
01372 break;
01373 }
01374
01375
01376 lockedPixels = 0;
01377 if (lockAreaCount>0)
01378 {
01379
01380
01381
01382
01383
01384
01385
01386
01387
01388 double segmentLength = lockAreaStack[lockAreaCount-1].y - startH.y;
01389 if (segmentLength <= 0)
01390 return;
01391 Vector2<int> segment = horizonInfo.toImageCoordinates(0.0, segmentLength);
01392 lockedPixels = max(abs(segment.x), abs(segment.y));
01393 }
01394 }
01395
01396 void GT2005GoalRecognizer::recalculateLockedPixels(const Vector2<int>& currentPoint)
01397 {
01398
01399
01400
01401
01402 lockedPixels = 0;
01403 if (lockAreaCount>0)
01404 {
01405 double segmentLength = lockAreaStack[lockAreaCount-1].y - horizonInfo.horizonAlignedYOf(currentPoint);
01406 if (segmentLength <= 0)
01407 return;
01408 Vector2<int> segment = horizonInfo.toImageCoordinates(0.0, segmentLength);
01409 lockedPixels = max(abs(segment.x), abs(segment.y));
01410 }
01411 }
01412
01413 void GT2005GoalRecognizer::mergeFragments(bool* deleted)
01414 {
01415
01416 for (int i=0; i<maxHypothesises; ++i)
01417 deleted[i] = false;
01418
01419
01420 for (int i=0; i<hypothesisCount; ++i)
01421 if (!deleted[i])
01422 {
01423 for (int j=i+1; j<hypothesisCount; ++j)
01424 if (!deleted[j])
01425 {
01426
01427 int volumeLeft = hypothesis[i].height*hypothesis[i].width;
01428 int volumeRight = hypothesis[j].height*hypothesis[j].width;
01429 const int& heightLeft = hypothesis[i].height;
01430 const int& heightRight = hypothesis[j].height;
01431 int verticalOffset = abs(static_cast<int>(hypothesis[j].goalpost[0].topPointH.y
01432 - hypothesis[i].goalpost[1].topPointH.y));
01433 int horizontalGap = static_cast<int>(hypothesis[j].goalpost[0].edgePointH.x
01434 - hypothesis[i].goalpost[1].edgePointH.x);
01435
01436
01437 bool sizeSimilar = volumeLeft*3>volumeRight && volumeRight*3>volumeLeft
01438 || heightLeft*3>heightRight*2 && heightRight*3>heightLeft*2;
01439
01440 if (sizeSimilar)
01441 {
01442
01443 bool verticalDifferenceAcceptable = verticalOffset < horizontalGap
01444 || verticalOffset < min(hypothesis[i].height, hypothesis[j].height);
01445
01446
01447
01448 int newWidth = static_cast<int>(hypothesis[j].goalpost[1].edgePointH.x
01449 - hypothesis[i].goalpost[0].edgePointH.x);
01450
01451
01452 if (hypothesis[j].goalpost[0].nonExistant)
01453 horizontalGap -= 8;
01454 if (hypothesis[i].goalpost[1].nonExistant)
01455 horizontalGap -= 8;
01456
01457 if (verticalDifferenceAcceptable
01458 && horizontalGap*3<newWidth)
01459 {
01460
01461 HORIZON_ALIGNED_RECTANGLE(horizonInfo, "GoalRecognizer:goal fragments:merged",
01462 hypothesis[i].goalpost[0].edgePointH.x, min(min(hypothesis[i].goalpost[0].topPointH.y, hypothesis[i].goalpost[1].topPointH.y), hypothesis[i].crossBarEndpointH.y),
01463 hypothesis[i].goalpost[1].edgePointH.x, max(hypothesis[i].goalpost[0].bottomPointH.y, hypothesis[i].goalpost[1].bottomPointH.y),
01464 1, Drawings::ps_solid, Drawings::orange, false);
01465 HORIZON_ALIGNED_RECTANGLE(horizonInfo, "GoalRecognizer:goal fragments:merged",
01466 hypothesis[j].goalpost[0].edgePointH.x, min(min(hypothesis[j].goalpost[0].topPointH.y, hypothesis[j].goalpost[1].topPointH.y), hypothesis[j].crossBarEndpointH.y),
01467 hypothesis[j].goalpost[1].edgePointH.x, max(hypothesis[j].goalpost[0].bottomPointH.y, hypothesis[j].goalpost[1].bottomPointH.y),
01468 1, Drawings::ps_solid, Drawings::orange, false);
01469
01470
01471 hypothesis[i].width = newWidth;
01472 hypothesis[i].height = max(hypothesis[i].height, hypothesis[j].height);
01473 hypothesis[i].onGreen = hypothesis[i].onGreen || hypothesis[j].onGreen;
01474 hypothesis[i].goalpost[1] = hypothesis[j].goalpost[1];
01475 deleted[j] = true;
01476 }
01477 }
01478 }
01479 }
01480 }
01481
01482
01483 void GT2005GoalRecognizer::interpretResults(GT2005GoalRecognizer::EdgeDetector* detector, bool* deleted)
01484 {
01485
01486 int selected = -1;
01487
01488
01489 bool visibleGoalpost[2];
01490 GT2005GoalRecognizer::FreeSide freeSideOfGoal = noFreeSide;
01491 Vector2<int> endpointOfFreePart;
01492 bool freePartOnImageBorder[2] = {false, false};
01493
01494
01495 for (int i = 0; i<hypothesisCount; i++)
01496 if (!deleted[i])
01497 {
01498 MODIFY("GoalRecognizer:Hypothesis", hypothesis[i]);
01499
01500
01501 bool possibleGoalPart[2];
01502 bool strongGoalpost[2];
01503
01504 for (int side=0; side<2; ++side)
01505 {
01506 possibleGoalPart[side] = hypothesis[i].goalpost[side].nonExistant == false
01507 && hypothesis[i].goalpost[side].height > minGoalpostHeight
01508 && hypothesis[i].goalpost[side].height*5 > hypothesis[i].width
01509
01510 && hypothesis[i].goalpost[side].bottomPointH.y - horizonInfo.distanceTopLeftCorner > -8
01511 && hypothesis[i].goalpost[side].weakEdgeCount <= hypothesis[i].goalpost[side].strongEdgeCount/8 + 1;
01512 visibleGoalpost[side] = possibleGoalPart[side]
01513 && hypothesis[i].goalpost[side].visibleHeight > 0
01514 && hypothesis[i].goalpost[side].height*2 > hypothesis[i].height;
01515
01516
01517 strongGoalpost[side] = visibleGoalpost[side]
01518 && hypothesis[i].goalpost[side].onGreen;
01519 }
01520
01521
01522 bool minimalWidth = hypothesis[i].width > 10;
01523 bool reasonableWidth = hypothesis[i].width > 20;
01524 bool largeWidth = hypothesis[i].width > 100;
01525 bool reasonableHeight = hypothesis[i].height > 30;
01526 bool largeHeight = hypothesis[i].height > 50;
01527
01528
01529
01530 if (( (strongGoalpost[0] || strongGoalpost[1])
01531 && (reasonableWidth || (reasonableHeight && minimalWidth)))
01532 || (possibleGoalPart[0] || possibleGoalPart[1])
01533 && largeWidth && largeHeight)
01534 {
01535
01536 selected = i;
01537
01538
01539 int freeWidth;
01540 freeSideOfGoal = detectFreePartOfGoal(detector, hypothesis[selected], freeWidth, endpointOfFreePart, freePartOnImageBorder);
01541
01542 DEBUG_RESPONSE("gt05-ip:GoalRecognizer:print if free part on image border",
01543 OUTPUT(idText, text, "Free side: " << (freeSideOfGoal==bothSides ? "both" : (freeSideOfGoal==leftSide ? "left" : "right"))
01544 << ", on border: " << (freePartOnImageBorder[0] ? "left " : "") << (freePartOnImageBorder[1] ? "right " : ""));
01545 );
01546
01547
01548 if (visibleGoalpost[0] && visibleGoalpost[1])
01549 {
01550
01551 if (freeWidth*3 < hypothesis[i].width)
01552 freeSideOfGoal = noFreeSide;
01553 }
01554 else
01555 {
01556
01557 if (freeWidth*2 < hypothesis[i].height)
01558 freeSideOfGoal = noFreeSide;
01559 }
01560
01561 break;
01562 }
01563 }
01564
01565
01566 HORIZON_ALIGNED_RECTANGLE(horizonInfo, "GoalRecognizer:goal fragments:result",
01567 hypothesis[selected].goalpost[0].edgePointH.x, min(min(hypothesis[selected].goalpost[0].topPointH.y, hypothesis[selected].goalpost[1].topPointH.y), hypothesis[selected].crossBarEndpointH.y),
01568 hypothesis[selected].goalpost[1].edgePointH.x, max(hypothesis[selected].goalpost[0].bottomPointH.y, hypothesis[selected].goalpost[1].bottomPointH.y),
01569 1, Drawings::ps_solid, Drawings::green, false);
01570
01571
01572 if (-1 != selected)
01573 publishResults(hypothesis[selected], visibleGoalpost, freeSideOfGoal, endpointOfFreePart, freePartOnImageBorder);
01574 }
01575
01576
01577 void GT2005GoalRecognizer::publishResults(const GT2005GoalRecognizer::GoalHypothesis& h,
01578 const bool* visibleGoalpost,
01579 const GT2005GoalRecognizer::FreeSide freeSide,
01580 const Vector2<int>& endpointOfFreePart,
01581 const bool* freePartOnImageBorder)
01582 {
01583
01584 Vector2<double> top, bottom, left, right;
01585
01586 Geometry::calculateAnglesForPoint((h.goalpost[0].topPoint+h.goalpost[1].topPoint)/2, interfaces.cameraMatrix, horizonInfo.previousCameraMatrix, interfaces.image.cameraInfo, top);
01587 if (h.goalpost[0].bottomPointH.y > h.goalpost[1].bottomPointH.y)
01588 Geometry::calculateAnglesForPoint(h.goalpost[0].bottomPoint, interfaces.cameraMatrix, horizonInfo.previousCameraMatrix, interfaces.image.cameraInfo, bottom);
01589 else
01590 Geometry::calculateAnglesForPoint(h.goalpost[1].bottomPoint, interfaces.cameraMatrix, horizonInfo.previousCameraMatrix, interfaces.image.cameraInfo, bottom);
01591 Geometry::calculateAnglesForPoint(h.goalpost[0].edgePoint, interfaces.cameraMatrix, horizonInfo.previousCameraMatrix, interfaces.image.cameraInfo, left);
01592 Geometry::calculateAnglesForPoint(h.goalpost[1].edgePoint, interfaces.cameraMatrix, horizonInfo.previousCameraMatrix, interfaces.image.cameraInfo, right);
01593
01594 ConditionalBoundary boundary;
01595 boundary.addY(top.y, true);
01596 boundary.addY(bottom.y, true);
01597 boundary.addX(left.x, !visibleGoalpost[0]);
01598 boundary.addX(right.x, !visibleGoalpost[1]);
01599
01600
01601 double topHY = (h.goalpost[0].topPointH.y + h.goalpost[1].topPointH.y)/2;
01602 double bottomHY = max(h.goalpost[0].bottomPointH.y, h.goalpost[1].bottomPointH.y);
01603 Vector2<int> topLeftCorner = horizonInfo.toImageCoordinates(h.goalpost[0].edgePointH.x, topHY);
01604 Vector2<int> bottomLeftCorner = horizonInfo.toImageCoordinates(h.goalpost[0].edgePointH.x, bottomHY);
01605 Vector2<int> bottomRightCorner = horizonInfo.toImageCoordinates(h.goalpost[1].edgePointH.x, bottomHY);
01606 Vector2<int> topRightCorner = horizonInfo.toImageCoordinates(h.goalpost[1].edgePointH.x, topHY);
01607
01608
01609 interfaces.landmarksPercept.addGoal(goalColor, boundary, topLeftCorner, topRightCorner, bottomLeftCorner, bottomRightCorner, interfaces.cameraMatrix);
01610
01611
01612
01613 switch (freeSide)
01614 {
01615 case bothSides:
01616
01617 interfaces.obstaclesPercept.setFreePartOfGoal(goalColor, left.x, right.x,
01618 !freePartOnImageBorder[0], !freePartOnImageBorder[1],
01619 horizonInfo.toImageCoordinates(h.goalpost[0].edgePointH.x, bottomHY),
01620 horizonInfo.toImageCoordinates(h.goalpost[1].edgePointH.x, bottomHY));
01621 break;
01622
01623 case leftSide:
01624 case rightSide:
01625 {
01626
01627
01628
01629
01630
01631 Vector2<double> otherEndpointAngle;
01632 Geometry::calculateAnglesForPoint(endpointOfFreePart, interfaces.cameraMatrix, horizonInfo.previousCameraMatrix, interfaces.image.cameraInfo, otherEndpointAngle);
01633
01634
01635 double otherEndpointHX = horizonInfo.horizonAlignedXOf(endpointOfFreePart);
01636
01637
01638 if (freeSide == leftSide)
01639 interfaces.obstaclesPercept.setFreePartOfGoal(goalColor, left.x, otherEndpointAngle.x,
01640 !freePartOnImageBorder[0], !freePartOnImageBorder[1],
01641 horizonInfo.toImageCoordinates(h.goalpost[0].edgePointH.x, bottomHY),
01642 horizonInfo.toImageCoordinates(otherEndpointHX, bottomHY));
01643 else
01644 interfaces.obstaclesPercept.setFreePartOfGoal(goalColor, otherEndpointAngle.x, right.x,
01645 !freePartOnImageBorder[0], !freePartOnImageBorder[1],
01646 horizonInfo.toImageCoordinates(otherEndpointHX, bottomHY),
01647 horizonInfo.toImageCoordinates(h.goalpost[1].edgePointH.x, bottomHY));
01648 }
01649 }
01650 }
01651
01652
01653 GT2005GoalRecognizer::FreeSide GT2005GoalRecognizer::detectFreePartOfGoal(GT2005GoalRecognizer::EdgeDetector* detector,
01654 const GT2005GoalRecognizer::GoalHypothesis& goal,
01655 int& freeWidth,
01656 Vector2<int>& otherSide,
01657 bool* onImageBorder)
01658 {
01659
01660
01661 const double freePartScanHeight = 0.5;
01662
01663
01664
01665 const int freePartScanOffsetFraction = 16;
01666
01667
01668
01669
01670
01671
01672 if (goal.goalpost[0].height*3 >= goal.height*2 && goal.goalpost[1].height*2 >= goal.height)
01673 {
01674
01675 Vector2<int> focus[2];
01676
01677 for (int i = 0; i<2; ++i)
01678 {
01679
01680
01681
01682
01683
01684 focus[i] = horizonInfo.toImageCoordinates(goal.goalpost[i].edgePointH.x - goal.goalpost[i].height/freePartScanOffsetFraction * (2*i-1),
01685 goal.goalpost[i].bottomPointH.y - static_cast<double>(goal.goalpost[i].height)*freePartScanHeight);
01686
01687
01688 horizonInfo.clipToImageBoundary(focus[i]);
01689 }
01690
01691
01692 Vector2<int> scanLineOffset(static_cast<int>(floor(horizonInfo.vertLine.direction.x*2.0+0.5)),
01693 static_cast<int>(floor(horizonInfo.vertLine.direction.y*2.0+0.5)));
01694
01695
01696 BresenhamLineScan scanLine0(focus[0], focus[1]);
01697 BresenhamLineScan scanLine1(focus[1], focus[0]);
01698
01699
01700
01701
01702
01703 int goalieEdgeIndex[2] = {0, 0};
01704 bool scanEndOnImageBorder[2] = {false, false};
01705
01706
01707 detector[0].clear(focus[0], false);
01708 detector[1].clear(focus[0], false);
01709 detector[0].setReferenceColor(goal.goalpost[0].color, 2);
01710 detector[1].setReferenceColor(goal.goalpost[0].color, 2);
01711 scanEndOnImageBorder[0] = (imageBorder == scanAlongLine(detector, focus[0], scanLine0, scanLineOffset, scanLine0.numberOfPixels+10, goalieEdgeIndex[0]));
01712
01713
01714 if (goalieEdgeIndex[0]*8 < scanLine0.numberOfPixels*7)
01715 {
01716
01717 detector[0].clear(focus[1], false);
01718 detector[1].clear(focus[1], false);
01719 detector[0].setReferenceColor(goal.goalpost[1].color, 2);
01720 detector[1].setReferenceColor(goal.goalpost[1].color, 2);
01721 scanEndOnImageBorder[1] = (imageBorder == scanAlongLine(detector, focus[1], scanLine1, scanLineOffset, scanLine1.numberOfPixels+10-goalieEdgeIndex[0], goalieEdgeIndex[1]));
01722 }
01723 if (goalieEdgeIndex[0]+goalieEdgeIndex[1] >= scanLine1.numberOfPixels)
01724 {
01725
01726 onImageBorder[0] = goal.goalpost[0].visibleHeight==0;
01727 onImageBorder[1] = goal.goalpost[1].visibleHeight==0;
01728
01729
01730 freeWidth = goal.width;
01731 return bothSides;
01732 }
01733 else if (goalieEdgeIndex[0] > goalieEdgeIndex[1])
01734 {
01735
01736
01737 onImageBorder[0] = goal.goalpost[0].visibleHeight==0;
01738 onImageBorder[1] = scanEndOnImageBorder[0];
01739
01740
01741
01742 freeWidth = goalieEdgeIndex[0]*goal.width/scanLine0.numberOfPixels;
01743 otherSide = focus[0];
01744 return leftSide;
01745 }
01746 else
01747 {
01748
01749 onImageBorder[0] = scanEndOnImageBorder[1];
01750 onImageBorder[1] = goal.goalpost[1].visibleHeight==0;
01751
01752
01753 freeWidth = goalieEdgeIndex[1]*goal.width/scanLine1.numberOfPixels;
01754 otherSide = focus[1];
01755 return rightSide;
01756 }
01757 }
01758 else
01759 {
01760
01761
01762 int longerGoalpost = goal.goalpost[0].height > goal.goalpost[1].height ? 0 : 1;
01763
01764
01765 Vector2<int> focus = horizonInfo.toImageCoordinates(goal.goalpost[longerGoalpost].edgePointH.x - goal.goalpost[longerGoalpost].height/freePartScanOffsetFraction * (2*longerGoalpost-1),
01766 goal.goalpost[longerGoalpost].bottomPointH.y - static_cast<double>(goal.goalpost[longerGoalpost].height)*freePartScanHeight);
01767
01768
01769 BresenhamLineScan scanLine(longerGoalpost==0 ? horizonInfo.horizon.direction
01770 : -horizonInfo.horizon.direction);
01771
01772
01773 Vector2<int> scanLineOffset(static_cast<int>(floor(horizonInfo.vertLine.direction.x*2.0+0.5)),
01774 static_cast<int>(floor(horizonInfo.vertLine.direction.y*2.0+0.5)));
01775
01776
01777
01778
01779 int goalieEdgeIndex = 0;
01780
01781
01782 detector[0].clear(focus, false);
01783 detector[1].clear(focus, false);
01784 detector[0].setReferenceColor(goal.goalpost[longerGoalpost].color, 2);
01785 detector[1].setReferenceColor(goal.goalpost[longerGoalpost].color, 2);
01786 bool scanEndOnImageBorder = (imageBorder == scanAlongLine(detector, focus, scanLine, scanLineOffset, scanLine.numberOfPixels+10, goalieEdgeIndex));
01787
01788
01789 onImageBorder[longerGoalpost] = goal.goalpost[longerGoalpost].visibleHeight==0;
01790 onImageBorder[1-longerGoalpost] = scanEndOnImageBorder;
01791
01792
01793
01794
01795 freeWidth = goalieEdgeIndex*1024/scanLine.numberOfPixels;
01796 otherSide = focus;
01797 return longerGoalpost==0 ? leftSide : rightSide;
01798 }
01799 }
01800
01801
01802
01803
01804 Matrix3x3<int> GT2005GoalRecognizer::EdgeDetector::inverseCovarianceMatrix(colorClass goalColor)
01805 {
01806 switch (goalColor)
01807 {
01808 case yellow:
01809 return Matrix3x3<int>(Vector3<int>(279,-89,240),
01810 Vector3<int>(-89,1272,-82),
01811 Vector3<int>(240,-82,586));
01812 case skyblue:
01813 return Matrix3x3<int>(Vector3<int>(186,19,-3)*2,
01814 Vector3<int>(19,470,-29)*2,
01815 Vector3<int>(-3,-29,257)*2);
01816 default:
01817 return Matrix3x3<int>();
01818 }
01819 }
01820
01821
01822 GT2005GoalRecognizer::EdgeDetector::EdgeDetector(colorClass target)
01823 : targetColorClass(target),
01824 ignoredClassification(target==skyblue ? blue : noColor),
01825 invCovar(inverseCovarianceMatrix(target)),
01826 referenceColor(),
01827 referenceColorBase(0),
01828 deviationCounter(0),
01829 edgeCounter(0),
01830 lastPointInClass(),
01831 lastPointInClassIndex(-1),
01832 connectedToClass(false)
01833 {
01834 }
01835
01836 GT2005GoalRecognizer::EdgeDetector::EdgeDetector(colorClass target, Vector3<int> referenceColor, int referenceColorBase)
01837 : targetColorClass(target),
01838 ignoredClassification(target==skyblue ? blue : noColor),
01839 invCovar(inverseCovarianceMatrix(target)),
01840 referenceColor(referenceColor),
01841 referenceColorBase(referenceColorBase),
01842 deviationCounter(0),
01843 edgeCounter(0),
01844 lastPointInClass(),
01845 lastPointInClassIndex(-1),
01846 connectedToClass(false)
01847 {
01848 }
01849
01850
01851 const float GT2005GoalRecognizer::EdgeDetector::classThreshold = 300000;
01852 const float GT2005GoalRecognizer::EdgeDetector::edgeThreshold = 600000;
01853