00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include "SeaBee/CrossRecognizer.H"
00037
00038
00039
00040 CrossRecognizer::CrossRecognizer()
00041 {
00042
00043 houghThreshold = 40;
00044 minThreshold = 10;
00045 maxThreshold = 50;
00046 sigma = 1.1;
00047 tlow = 0.3;
00048 thigh = 1.1;
00049 linescale = 80;
00050 avgCrossCenterX = 0;
00051 avgCrossCenterY = 0;
00052 avgCrossAngle = 0.0;
00053 stdDevCrossX = 0.0;
00054 stdDevCrossY = 0.0;
00055 stdDevCrossAngle = 0.0;
00056
00057 stdDevAngleCount = 0;
00058 stdDevCenterCount = 0;
00059
00060 foundCount = 0;
00061
00062 itsSetupOrangeTracker = false;
00063 }
00064
00065
00066 CrossRecognizer::~CrossRecognizer()
00067 { }
00068
00069
00070 void CrossRecognizer::getCrossLocation(rutz::shared_ptr<Image<byte> > colorSegmentedImage,
00071 rutz::shared_ptr<Image<PixRGB <byte> > > outputImage,
00072 CrossRecognizeMethod method,
00073 rutz::shared_ptr<Point2D<int> > crossCenterPoint,
00074 rutz::shared_ptr<float> crossAngle,
00075 rutz::shared_ptr<uint> stalePointCount
00076 )
00077 {
00078 switch(method)
00079 {
00080 case HOUGH:
00081 calculateHoughTransform(colorSegmentedImage, outputImage, crossCenterPoint, crossAngle, stalePointCount);
00082 break;
00083
00084 default:
00085 LERROR("Invalid pipe recognizer method specified");
00086 }
00087
00088 }
00089
00090
00091
00092 void CrossRecognizer::calculateHoughTransform(rutz::shared_ptr<Image<byte> > colorSegmentedImage,
00093 rutz::shared_ptr<Image<PixRGB <byte> > > outputImage,
00094 rutz::shared_ptr<Point2D<int> > crossCenterPoint,
00095 rutz::shared_ptr<float> crossAngle,
00096 rutz::shared_ptr<uint> stalePointCount
00097 )
00098 {
00099 std::vector<LineSegment2D> lines = getHoughLines(colorSegmentedImage, outputImage);
00100 std::vector<LineSegment2D> centerPointLines;
00101 *crossCenterPoint = getCrossCenter(lines, centerPointLines, stalePointCount);
00102
00103 LDEBUG("Center Point X: %d, Y: %d", crossCenterPoint->i, crossCenterPoint->j);
00104
00105 *crossAngle = getCrossDir(centerPointLines);
00106 }
00107
00108
00109
00110 std::vector<LineSegment2D> CrossRecognizer::pruneLines (std::vector<LineSegment2D> lines)
00111 {
00112 uint nLines = lines.size();
00113
00114 float sumLength = 0.0; float meanLength = 0.0;
00115 for (uint i = 0; i < nLines; i++) sumLength += lines[i].length();
00116 meanLength = sumLength / nLines;
00117
00118
00119 float sumsqr = 0.0;
00120 for (uint i = 0; i < nLines; i++)
00121 sumsqr += pow((float)(lines[i].length()) - meanLength, 2.0);
00122 float stdevLength = sqrt(sumsqr / (nLines-1));
00123
00124
00125 LDEBUG ("Mean: %f StdDev: %f", meanLength, stdevLength);
00126 std::vector<LineSegment2D> nlines;
00127 for(uint i = 0; i < nLines; i++)
00128 {
00129 if (lines[i].length() > (meanLength - stdevLength) &&
00130 lines[i].length() < (meanLength + stdevLength) )
00131 nlines.push_back(lines[i]);
00132 }
00133
00134 if (nlines.size() == 0)
00135 { LDEBUG("*** NO LINE LENGTH WITHIN STD DEV ***"); return lines; }
00136
00137 std::vector<LineSegment2D> flines = nlines;
00138 LDEBUG("\n\n\n\nSum the Average...");
00139 double sumAngle = 0;
00140 std::vector<LineSegment2D>::iterator itr = flines.begin();
00141 while(itr < flines.end())
00142 {
00143 float angle = (*itr).angle();
00144 LDEBUG("Line Angle[%4d,%4d][%4d,%4d]: %f",
00145 (*itr).point1().i, (*itr).point1().j,
00146 (*itr).point2().i, (*itr).point2().j,
00147 angle * 180/M_PI);
00148
00149
00150 if(!isnan(angle)) { sumAngle += angle; itr++; }
00151 else
00152 {
00153 sumAngle += 90.0;
00154
00155 LDEBUG("Drop a line");
00156 }
00157 }
00158 if (flines.size() == 0) return lines;
00159
00160
00161
00162 float meanAngle = sumAngle / flines.size();
00163 float stdAngleSum = 0.0;
00164 float stdAngleSqr = 0.0;
00165 for(uint i = 0; i < flines.size(); i++)
00166 {
00167 float angle = flines[i].angle();
00168 stdAngleSum = angle - meanAngle;
00169 stdAngleSqr += (stdAngleSum * stdAngleSum);
00170 }
00171 float stdevAngle = stdAngleSqr / flines.size();
00172 double stdtemp = sqrt((double)stdevAngle);
00173 stdevAngle = (float)stdtemp;
00174
00175
00176 sumAngle = 0.0; itr = flines.begin();
00177 while(itr < flines.end())
00178 {
00179 float angle = (*itr).angle();
00180 if(angle >= (meanAngle - stdevAngle) &&
00181 angle <= (meanAngle + stdevAngle))
00182 { sumAngle += angle; itr++; }
00183 else itr = flines.erase(itr);
00184 }
00185
00186 return flines;
00187 }
00188
00189
00190
00191 Point2D<int> CrossRecognizer::getCrossCenter (const std::vector<LineSegment2D> lines,
00192 std::vector<LineSegment2D> ¢erPointLines,
00193 rutz::shared_ptr<uint> stalePointCount)
00194 {
00195
00196 Point2D<int> avgCrossCenterPoint = Point2D<int>(avgCrossCenterX,avgCrossCenterY);
00197
00198 uint nLines = lines.size();
00199 if(nLines == 0)
00200 {
00201 LDEBUG("*** NO LINES ***");
00202 ++(*stalePointCount);
00203 return avgCrossCenterPoint;
00204 }
00205
00206 std::vector<LineSegment2D> posAngleLines, negAngleLines;
00207
00208 for(uint i = 0; i < nLines; i++)
00209 {
00210 if(lines[i].angle() >= 0)
00211 {
00212 posAngleLines.push_back(lines[i]);
00213 }
00214 else
00215 {
00216 negAngleLines.push_back(lines[i]);
00217 }
00218 }
00219
00220 std::vector<LineSegment2D> fPosAngleLines = posAngleLines;
00221 std::vector<LineSegment2D> fNegAngleLines = negAngleLines;
00222
00223 std::vector<LineSegment2D> flines;
00224
00225 for(uint i = 0; i < fPosAngleLines.size(); i++)
00226 {
00227 flines.push_back(fPosAngleLines[i]);
00228 }
00229
00230 for(uint i = 0; i < fNegAngleLines.size(); i++)
00231 {
00232 flines.push_back(fNegAngleLines[i]);
00233 }
00234
00235
00236 std::vector<std::vector<Point2D<int> > > intersectPts;
00237
00238
00239 for(uint r = 0; r < flines.size(); r++)
00240 {
00241 for(uint s = 0; s < flines.size(); s++)
00242 {
00243 if(r != s)
00244 {
00245 double xIntersect, yIntersect = 0.0;
00246
00247 if(flines[r].intersects(flines[s],xIntersect,yIntersect)
00248 && (fabs(flines[r].angleBetween(flines[s])) >= 0.95 * M_PI/2)
00249 && (fabs(flines[r].angleBetween(flines[s])) <= 1.05 * M_PI/2))
00250 {
00251 int ptIndex = -1;
00252
00253 centerPointLines.push_back(flines[r]);
00254 centerPointLines.push_back(flines[s]);
00255
00256
00257
00258 for(uint c = 0; c < intersectPts.size(); c++)
00259 {
00260 if(xIntersect < intersectPts[c][0].i + 5 &&
00261 xIntersect > intersectPts[c][0].i - 5 &&
00262 yIntersect < intersectPts[c][0].j + 5 &&
00263 yIntersect > intersectPts[c][0].j - 5 )
00264 {
00265 ptIndex = c;
00266 }
00267
00268 }
00269
00270
00271 if(ptIndex > 0)
00272 {
00273 int x = (int)(xIntersect);
00274 int y = (int)(yIntersect);
00275
00276 intersectPts[ptIndex].push_back(Point2D<int>(x,y));
00277
00278
00279 intersectPts[ptIndex][0].i += x;
00280 intersectPts[ptIndex][0].i /= 2;
00281 intersectPts[ptIndex][0].j += y;
00282 intersectPts[ptIndex][0].j /= 2;
00283
00284 }
00285
00286 else
00287 {
00288 int x = (int)(xIntersect);
00289 int y = (int)(yIntersect);
00290
00291 std::vector<Point2D<int> > newIntPts;
00292 newIntPts.push_back(Point2D<int>(x,y));
00293 intersectPts.push_back(newIntPts);
00294 }
00295 }
00296 }
00297 }
00298 }
00299
00300 if(intersectPts.size() == 0)
00301 {
00302 LDEBUG("*** NO INTERSECT POINTS FOUND ***");
00303 ++(*stalePointCount);
00304 return avgCrossCenterPoint;
00305 }
00306
00307 uint maxPts = intersectPts[0].size();
00308 uint maxIndex = 0;
00309
00310 for(uint i = 0; i < intersectPts.size(); i++)
00311 {
00312 if(intersectPts[i].size() > maxPts)
00313 {
00314 maxPts = intersectPts[i].size();
00315 maxIndex = i;
00316 }
00317 }
00318
00319 Point2D<int> finalPoint = intersectPts[maxIndex][0];
00320
00321
00322
00323
00324
00325 bool isWithinStdDev = false;
00326
00327 if(centerPointBuff.size() < 2
00328 || (abs(finalPoint.i - avgCrossCenterX) <= stdDevCrossX
00329 && abs(finalPoint.j - avgCrossCenterY) <= stdDevCrossY)
00330 )
00331 {
00332 stdDevCenterCount = 0;
00333 isWithinStdDev = true;
00334 }
00335 else
00336 {
00337 stdDevCenterCount++;
00338
00339 LINFO("Current X: %d Y: %d",finalPoint.i, finalPoint.j);
00340 LINFO("AVG X: %d Y: %d",avgCrossCenterX, avgCrossCenterY);
00341 LINFO("STDDEV CROSS (X,Y):(%f,%f)",stdDevCrossX, stdDevCrossY);
00342
00343 }
00344
00345 *stalePointCount = 0;
00346
00347 uint centerBuffSize = centerPointBuff.size();
00348
00349 if(stdDevCenterCount > 5)
00350 {
00351 isWithinStdDev = true;
00352 centerPointBuff.clear();
00353 }
00354
00355 if(isWithinStdDev)
00356 {
00357 if(centerBuffSize >= 30)
00358 {
00359 centerPointBuff.pop_front();
00360 }
00361
00362 centerPointBuff.push_back(finalPoint);
00363
00364 centerBuffSize = centerPointBuff.size();
00365 }
00366
00367 if(centerBuffSize > 0)
00368 {
00369 int sumX = 0, sumY = 0;
00370 std::list<Point2D<int> >::iterator it;
00371
00372 for(it = centerPointBuff.begin(); it != centerPointBuff.end(); ++it)
00373 {
00374 sumX += (*it).i;
00375 sumY += (*it).j;
00376 }
00377
00378 avgCrossCenterX = sumX / centerBuffSize;
00379 avgCrossCenterY = sumY / centerBuffSize;
00380
00381 avgCrossCenterPoint = Point2D<int>(avgCrossCenterX,avgCrossCenterY);
00382
00383 float sqrStdCenterX = 0.0;
00384 float sqrStdCenterY = 0.0;
00385
00386
00387 for(it = centerPointBuff.begin(); it != centerPointBuff.end(); ++it)
00388 {
00389 sqrStdCenterX += (((*it).i - avgCrossCenterX) * ((*it).i - avgCrossCenterX));
00390 sqrStdCenterY += (((*it).j - avgCrossCenterY) * ((*it).j - avgCrossCenterY));
00391 }
00392
00393 float stdevCenterX = sqrStdCenterX / centerBuffSize;
00394 float stdevCenterY = sqrStdCenterY / centerBuffSize;
00395 double stdTempCenterX = sqrt((double)stdevCenterX);
00396 double stdTempCenterY = sqrt((double)stdevCenterY);
00397 stdDevCrossX = (float)stdTempCenterX;
00398 stdDevCrossY = (float)stdTempCenterY;
00399 }
00400 else
00401 {
00402 avgCrossCenterPoint = finalPoint;
00403 }
00404
00405 return avgCrossCenterPoint;
00406 }
00407
00408
00409
00410 float CrossRecognizer::getCrossDir(const std::vector<LineSegment2D> lines)
00411 {
00412 uint nLines = lines.size();
00413 if(nLines == 0) { LDEBUG("*** NO LINES ***"); return avgCrossAngle;}
00414
00415
00416 std::vector<LineSegment2D> posAngleLines, negAngleLines;
00417
00418 for(uint i = 0; i < nLines; i++)
00419 {
00420 if(lines[i].angle() >= 0)
00421 {
00422 posAngleLines.push_back(lines[i]);
00423 }
00424 else
00425 {
00426 negAngleLines.push_back(lines[i]);
00427 }
00428 }
00429
00430 std::vector<LineSegment2D> fPosAngleLines = posAngleLines;
00431 std::vector<LineSegment2D> fNegAngleLines = negAngleLines;
00432
00433 double avgPosAngle, avgNegAngle;
00434
00435
00436
00437 double sumPosAngle = 0;
00438 std::vector<LineSegment2D>::iterator itr = fPosAngleLines.begin();
00439 while(itr < fPosAngleLines.end())
00440 {
00441 float angle = (*itr).angle();
00442 LDEBUG("Line Angle[%4d,%4d][%4d,%4d]: %f",
00443 (*itr).point1().i, (*itr).point1().j,
00444 (*itr).point2().i, (*itr).point2().j,
00445 angle * 180/M_PI);
00446
00447
00448 if(!isnan(angle)) { sumPosAngle += angle; itr++; }
00449 else
00450 { sumPosAngle += 90.0;
00451
00452 LDEBUG("Drop a line");
00453 }
00454 }
00455
00456 if (fPosAngleLines.size() == 0)
00457 {
00458 return avgCrossAngle;
00459 }
00460 else
00461 {
00462 avgPosAngle = sumPosAngle / fPosAngleLines.size();
00463 }
00464
00465
00466 double sumNegAngle = 0;
00467 itr = fNegAngleLines.begin();
00468 while(itr < fNegAngleLines.end())
00469 {
00470 float angle = (*itr).angle();
00471 LDEBUG("Line Angle[%4d,%4d][%4d,%4d]: %f",
00472 (*itr).point1().i, (*itr).point1().j,
00473 (*itr).point2().i, (*itr).point2().j,
00474 angle * 180/M_PI);
00475
00476
00477 if(!isnan(angle)) { sumNegAngle += angle; itr++; }
00478 else
00479 { sumNegAngle += 90.0;
00480
00481
00482 LDEBUG("Drop a line");
00483 }
00484 }
00485
00486
00487 if (fNegAngleLines.size() == 0)
00488 {
00489 return avgCrossAngle;
00490 }
00491 else
00492 {
00493 avgNegAngle = sumNegAngle / fNegAngleLines.size();
00494 }
00495
00496
00497 int posAngleDistance = abs(90 - (int)avgPosAngle);
00498 int negAngleDistance = abs(90 - abs((int)avgNegAngle));
00499
00500 float meanAngle = 0.0;
00501 float sumAngle = 0.0;
00502 std::vector<LineSegment2D> flines;
00503
00504 if(posAngleDistance <= negAngleDistance)
00505 {
00506 flines = fPosAngleLines;
00507 meanAngle = avgPosAngle;
00508 sumAngle = sumPosAngle;
00509 }
00510 else
00511 {
00512 flines = fNegAngleLines;
00513 meanAngle = avgNegAngle;
00514 sumAngle = sumNegAngle;
00515 }
00516
00517
00518
00519
00520
00521
00522
00523
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542 float avgAngle = 0.0;
00543
00544 if (flines.size() != 0)
00545 {
00546 avgAngle = sumAngle / flines.size();
00547 }
00548 else
00549 {
00550 return avgCrossAngle;
00551 }
00552
00553 if (avgAngle > 0) avgAngle -= M_PI;
00554
00555
00556
00557 uint angleBuffSize = angleBuff.size();
00558
00559 bool isWithinStdDev = false;
00560
00561 if(angleBuff.size() < 2
00562 || (fabs(avgAngle - avgCrossAngle) <= stdDevCrossAngle ))
00563 {
00564 stdDevAngleCount = 0;
00565 isWithinStdDev = true;
00566 }
00567 else
00568 {
00569 stdDevAngleCount++;
00570 }
00571
00572 if(stdDevAngleCount > 10)
00573 {
00574 angleBuff.clear();
00575 isWithinStdDev = true;
00576 }
00577
00578 if(isWithinStdDev)
00579 {
00580 if(angleBuffSize >= 50)
00581 {
00582 angleBuff.pop_front();
00583 }
00584
00585 angleBuff.push_back(avgAngle);
00586
00587 angleBuffSize = angleBuff.size();
00588 }
00589
00590 if(angleBuffSize > 0)
00591 {
00592 float sumBuffAngle = 0.0;
00593 std::list<float>::iterator it;
00594
00595 for(it = angleBuff.begin(); it != angleBuff.end(); ++it)
00596 {
00597 sumBuffAngle += (*it);
00598 }
00599
00600 avgCrossAngle = sumBuffAngle / angleBuffSize;
00601
00602
00603
00604
00605 float sqrStdDevAngle = 0.0;
00606 for(it = angleBuff.begin(); it != angleBuff.end(); ++it)
00607 {
00608 sqrStdDevAngle = ((*it - avgCrossAngle) * (*it - avgCrossAngle));
00609 }
00610
00611 float stdevAngle = sqrStdDevAngle / angleBuffSize;
00612 double stdTempAngle = sqrt((double)stdevAngle);
00613 stdDevCrossAngle = (float)stdTempAngle;
00614 }
00615 else
00616 {
00617 avgCrossAngle = avgAngle;
00618 }
00619
00620 return avgCrossAngle;
00621 }
00622
00623
00624
00625 std::vector<LineSegment2D> CrossRecognizer::getHoughLines
00626 (rutz::shared_ptr< Image< byte > > colorSegmentedImage,
00627 rutz::shared_ptr< Image< PixRGB <byte> > > outputImage)
00628 {
00629
00630 #ifndef HAVE_OPENCV
00631 LFATAL("OpenCV must be installed in order to use this function");
00632 #else
00633
00634
00635 IplImage *edge = cvCreateImage( cvGetSize(img2ipl(*colorSegmentedImage)), 8, 1 );
00636 cvCanny( img2ipl(luminance(*colorSegmentedImage)), edge, 100, 150, 3 );
00637
00638
00639 outputImage->clear();
00640 *outputImage = toRGB(ipl2gray(edge));
00641
00642
00643 CvMemStorage* storage = cvCreateMemStorage(0);
00644
00645
00646 CvSeq* cvLines = cvHoughLines2(edge, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 30, 20, 10);
00647
00648 LINFO("cvLines %d",cvLines->total);
00649
00650
00651 std::vector <LineSegment2D> lineSegments;
00652
00653
00654 for(int i = 0; i < cvLines->total; i++ )
00655 {
00656
00657 CvPoint* line = (CvPoint*)cvGetSeqElem(cvLines,i);
00658
00659 Point2D<int> pt1 = Point2D<int>(line[0].x,line[0].y);
00660 Point2D<int> pt2 = Point2D<int>(line[1].x,line[1].y);
00661
00662
00663
00664 lineSegments.push_back(LineSegment2D(pt1,pt2));
00665
00666
00667 drawLine(*outputImage, pt1, pt2, PixRGB<byte>(255,0,0));
00668 }
00669
00670
00671
00672
00673
00674
00675
00676
00677 #endif // HAVE_OPENCV
00678
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688 std::vector <LineSegment2D> lines;
00689
00690 #ifndef HAVE_OPENCV
00691 LFATAL("OpenCV must be installed in order to use this function");
00692 #else
00693
00694 IplImage *edge = cvCreateImage( cvGetSize(img2ipl(cameraImage)), 8, 1 );
00695 cvCanny( img2ipl(luminance(cameraImage)), edge, 100, 150, 3 );
00696
00697
00698
00699
00700
00701
00702
00703 CvMemStorage* storage = cvCreateMemStorage(0);
00704
00705
00706
00707 outputImage = ipl2gray(edge);
00708
00709 CvSeq* cvlines = cvHoughLines2(edge, storage, CV_HOUGH_STANDARD,
00710 1, CV_PI/180, 40 , 0, 0);
00711
00712
00713
00714 for(int i = 0; i < MIN(cvlines->total,10); i++ )
00715 {
00716 float* line = (float*)cvGetSeqElem(cvlines,i);
00717 float rho = line[0];
00718 float theta = line[1];
00719 CvPoint pt1, pt2;
00720 double a = cos(theta), b = sin(theta);
00721 double x0 = a*rho, y0 = b*rho;
00722 pt1.x = cvRound(x0 + 1000*(-b));
00723 pt1.y = cvRound(y0 + 1000*(a));
00724 pt2.x = cvRound(x0 - 1000*(-b));
00725 pt2.y = cvRound(y0 - 1000*(a));
00726 lines.push_back(LineSegment2D(Point2D<int>(pt1.x,pt1.y),
00727 Point2D<int>(pt2.x,pt2.y)));
00728 drawLine(outputImage, Point2D<int>(pt1.x,pt1.y),
00729 Point2D<int>(pt2.x, pt2.y), PixRGB<byte>(255,0,0));
00730 }
00731
00732 cvReleaseMemStorage(&storage);
00733 cvReleaseImage(&edge);
00734
00735
00736
00737
00738
00739 #endif // HAVE_OPENCV
00740
00741 return lines;
00742 }
00743
00744
00745
00746
00747