00001 #include "Robots/SeaBeeIII/BinFinder.H"
00002
00003 #include "Component/ModelParam.H"
00004 #include "Component/ModelOptionDef.H"
00005 #include "Image/OpenCVUtil.H"
00006 #include "Raster/Raster.H"
00007 #include "MBARI/Geometry2D.H"
00008 #include "Image/Pixels.H"
00009
00010 #include "Image/ColorOps.H"
00011
00012
00013 #include "Media/MediaOpts.H"
00014
00015
00016 #ifndef BINFINDER_C
00017 #define BINFINDER_C
00018
00019 using namespace std;
00020
00021 #define MIN_CENTER_DIST 15
00022 #define MIN_AREA 150
00023 #define CORNER_TOLERANCE 4
00024
00025 int thresh = 42;
00026 double angle_thresh = 0.3;
00027
00028
00029
00030 BinFinder::BinFinder
00031 ( OptionManager& mgr,
00032 const std::string& descrName,
00033 const std::string& tagName) :
00034 VisionBrainComponentI(mgr, descrName, tagName)
00035 {
00036
00037 itsWidth = 320;
00038 itsHeight = 240;
00039
00040
00041 itsDisp.resize(2*itsWidth, 2*itsHeight);
00042 }
00043
00044
00045 BinFinder::~BinFinder()
00046 {
00047 }
00048
00049
00050 void BinFinder::registerTopics()
00051 {
00052 LINFO("Registering BinFinder Message");
00053 this->registerPublisher("BinFinderMessageTopic");
00054 registerVisionTopics();
00055 }
00056
00057
00058
00059
00060
00061
00062 double BinFinder::angle( CvPoint* pt1, CvPoint* pt2, CvPoint* pt0 ) {
00063 double dx1 = pt1->x - pt0->x;
00064 double dy1 = pt1->y - pt0->y;
00065 double dx2 = pt2->x - pt0->x;
00066 double dy2 = pt2->y - pt0->y;
00067 return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
00068 }
00069
00070
00071
00072
00073
00074 CvSeq* BinFinder::findSquares4( IplImage* img, CvMemStorage* storage ) {
00075 CvSeq* contours;
00076 int i, c, l, N = 11;
00077
00078 CvSize sz = cvSize( img->width & -2, img->height & -2 );
00079
00080 IplImage* timg = cvCloneImage( img );
00081 IplImage* gray = cvCreateImage( sz, 8, 1 );
00082 IplImage* pyr = cvCreateImage( cvSize(sz.width/2, sz.height/2), 8, 3 );
00083 IplImage* tgray;
00084
00085 CvSeq* result;
00086
00087 double s, t;
00088
00089
00090 CvSeq* squares = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvPoint), storage );
00091
00092 double img_area = sz.width * sz.height;
00093 double max_area = img_area * 0.5;
00094
00095
00096
00097
00098 cvSetImageROI( timg, cvRect( 0, 0, sz.width, sz.height ));
00099
00100
00101 cvPyrDown( timg, pyr, 7 );
00102 cvPyrUp( pyr, timg, 7 );
00103 tgray = cvCreateImage( sz, 8, 1 );
00104
00105
00106 itsDisp.clear();
00107 inplacePaste(itsDisp, ipl2rgb(img), Point2D<int>(0, 0));
00108
00109
00110 for( c = 0; c < 3; c++ ) {
00111
00112 cvSetImageCOI( timg, c+1 );
00113 cvCopy( timg, tgray, 0 );
00114
00115
00116 for( l = 0; l < N; l++ ) {
00117
00118
00119 if( l == 0 ) {
00120
00121
00122 cvCanny( tgray, gray, 0, thresh, 5 );
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136 cvDilate( gray, gray, 0, 1 );
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146 } else {
00147
00148
00149 cvThreshold( tgray, gray, (l+1)*255/N, 255, CV_THRESH_BINARY );
00150
00151
00152
00153
00154
00155
00156 }
00157
00158
00159 cvFindContours( gray, storage, &contours, sizeof(CvContour),
00160 CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
00161
00162
00163 uint count =0;
00164 while( contours ) {
00165
00166
00167
00168 result = cvApproxPoly( contours, sizeof(CvContour), storage,
00169 CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0 );
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179 double rect_area = fabs(cvContourArea(result,CV_WHOLE_SEQ));
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219 if (
00220 result->total == 4
00221 && rect_area > MIN_AREA
00222 && rect_area < max_area
00223 && cvCheckContourConvexity(result)
00224 && !inCorner(result)
00225 )
00226 {
00227 s = 0;
00228
00229 for( i = 0; i < 5; i++ ) {
00230
00231
00232 if( i >= 2 ) {
00233 t = fabs(angle(
00234 (CvPoint*)cvGetSeqElem( result, i ),
00235 (CvPoint*)cvGetSeqElem( result, i-2 ),
00236 (CvPoint*)cvGetSeqElem( result, i-1 )));
00237 s = s > t ? s : t;
00238 }
00239 }
00240
00241
00242
00243
00244 if( s < angle_thresh)
00245 {
00246
00247 for( i = 0; i < 4; i++ )
00248 cvSeqPush( squares, (CvPoint*)cvGetSeqElem( result, i ));
00249 }
00250 }
00251
00252
00253 contours = contours->h_next;
00254 count++;
00255 }
00256
00257
00258
00259 }
00260 }
00261
00262
00263
00264 cvReleaseImage( &gray );
00265 cvReleaseImage( &pyr );
00266 cvReleaseImage( &tgray );
00267 cvReleaseImage( &timg );
00268
00269 return squares;
00270 }
00271
00272
00273 void BinFinder::updateFrame(Image<PixRGB<byte> > img, std::string cameraId)
00274 {
00275 LINFO("Image Received: %d", itsFrameCount);
00276 bool isFwdCamera = false;
00277 if(cameraId == "FwdCamera")
00278 isFwdCamera = true;
00279
00280 if(img.initialized())
00281 {
00282
00283 CvMemStorage* storage = cvCreateMemStorage(0);
00284 IplImage* img0 = img2ipl(img);
00285
00286 CvSeq *cvsquares = findSquares4( img0, storage );
00287
00288 cvReleaseImage( &img0 );
00289
00290 RobotSimEvents::QuadrilateralIceVector quadVect;
00291 ImageIceMod::QuadrilateralIce quad;
00292 Point2D<int> avgCenter(0,0);
00293 std::multimap<int,Point2D<int> > tempPoints;
00294
00295
00296 for(int i=0; i < cvsquares->total; i++) {
00297
00298
00299 Point2D<int> quadPoint;
00300 quadPoint.i = ((CvPoint*)cvGetSeqElem(cvsquares, i))->x;
00301 quadPoint.j = ((CvPoint*)cvGetSeqElem(cvsquares, i))->y;
00302
00303
00304 avgCenter += Point2D<int>(quadPoint.i,quadPoint.j);
00305
00306
00307 tempPoints.insert(make_pair(quadPoint.j,quadPoint));
00308
00309
00310
00311
00312 if (tempPoints.size() == 4)
00313 {
00314 std::vector<Point2D<int> > tempVec;
00315
00316 for(std::map<int,Point2D<int > >::const_iterator it = tempPoints.begin();
00317 it != tempPoints.end(); ++it)
00318 {
00319 tempVec.push_back(it->second);
00320 }
00321
00322
00323
00324
00325 if(tempVec[0].i < tempVec[1].i)
00326 {
00327 quad.tl.i = tempVec[0].i;
00328 quad.tr.i = tempVec[1].i;
00329
00330 quad.tl.j = tempVec[0].j;
00331 quad.tr.j = tempVec[1].j;
00332 }
00333 else
00334 {
00335 quad.tr.i = tempVec[0].i;
00336 quad.tl.i = tempVec[1].i;
00337
00338 quad.tr.j = tempVec[0].j;
00339 quad.tl.j = tempVec[1].j;
00340 }
00341
00342
00343
00344 if(tempVec[2].i < tempVec[3].i)
00345 {
00346 quad.bl.i = tempVec[2].i;
00347 quad.br.i = tempVec[3].i;
00348
00349 quad.bl.j = tempVec[2].j;
00350 quad.br.j = tempVec[3].j;
00351 }
00352 else
00353 {
00354 quad.br.i = tempVec[2].i;
00355 quad.bl.i = tempVec[3].i;
00356
00357 quad.br.j = tempVec[2].j;
00358 quad.bl.j = tempVec[3].j;
00359 }
00360
00361
00362
00363
00364 avgCenter /= Point2D<int>(4,4);
00365 quad.center.i = avgCenter.i;
00366 quad.center.j = avgCenter.j;
00367
00368
00369 bool isDupe = false;
00370
00371
00372
00373 for(uint j = 0; j < quadVect.size(); j++)
00374 {
00375 if(avgCenter.distance(Point2D<int>(quadVect[j].center.i,quadVect[j].center.j))
00376 < MIN_CENTER_DIST)
00377 {
00378 isDupe = true;
00379 }
00380 }
00381
00382
00383 if(!isDupe)
00384 {
00385 LineSegment2D vertLine = LineSegment2D((Point2D<int>(quad.tr.i,quad.tr.j) +
00386 Point2D<int>(quad.tl.i,quad.tl.j))/2,
00387 (Point2D<int>(quad.br.i,quad.br.j) +
00388 Point2D<int>(quad.bl.i,quad.bl.j))/2);
00389
00390 LineSegment2D horizLine = LineSegment2D((Point2D<int>(quad.tl.i,quad.tl.j) +
00391 Point2D<int>(quad.bl.i,quad.bl.j))/2,
00392 (Point2D<int>(quad.tr.i,quad.tr.j) +
00393 Point2D<int>(quad.br.i,quad.br.j))/2);
00394 float ratio = 0.0;
00395 float angle = 0.0;
00396 if(vertLine.length() > horizLine.length())
00397 {
00398 if(horizLine.length() > 0)
00399 ratio = vertLine.length() / horizLine.length();
00400
00401 angle = vertLine.angle();
00402 }
00403 else
00404 {
00405 if(vertLine.length() > 0)
00406 ratio = horizLine.length() / vertLine.length();
00407
00408 angle = horizLine.angle();
00409 }
00410
00411
00412
00413 angle = angle * (180/M_PI);
00414
00415
00416
00417
00418 if(angle < 0)
00419 angle += 90;
00420 else
00421 angle += -90;
00422
00423 quad.ratio = ratio;
00424 quad.angle = angle;
00425 quadVect.push_back(quad);
00426
00427
00428 drawLine(img,Point2D<int>(quad.tr.i,quad.tr.j),
00429 Point2D<int>(quad.br.i,quad.br.j),
00430 PixRGB<byte>(0,255,0),2);
00431 drawLine(img,Point2D<int>(quad.br.i,quad.br.j),
00432 Point2D<int>(quad.bl.i,quad.bl.j),
00433 PixRGB<byte>(0,255,0),2);
00434 drawLine(img,Point2D<int>(quad.bl.i,quad.bl.j),
00435 Point2D<int>(quad.tl.i,quad.tl.j),
00436 PixRGB<byte>(0,255,0),2);
00437 drawLine(img,Point2D<int>(quad.tl.i,quad.tl.j),
00438 Point2D<int>(quad.tr.i,quad.tr.j),
00439 PixRGB<byte>(0,255,0),2);
00440
00441 char* str = new char[20];
00442 sprintf(str,"%1.2f, %2.1f",ratio,angle);
00443 writeText(img,Point2D<int>(quad.center.i,quad.center.j),str);
00444 delete [] str;
00445
00446 }
00447
00448
00449 quad = ImageIceMod::QuadrilateralIce();
00450 avgCenter = Point2D<int>(0,0);
00451 tempPoints.clear();
00452
00453 }
00454 }
00455
00456 itsOfs->writeRGB(img, "Vision Rectangle Image",
00457 FrameInfo("Vision Rectangle Image", SRC_POS));
00458
00459 itsOfs->updateNext();
00460
00461 if (quadVect.size() > 0) {
00462 RobotSimEvents::VisionRectangleMessagePtr msg = new RobotSimEvents::VisionRectangleMessage;
00463 msg->quads = quadVect;
00464 msg->isFwdCamera = isFwdCamera;
00465 this->publish("VisionRectangleMessageTopic", msg);
00466 }
00467
00468 cvReleaseMemStorage( &storage );
00469 }
00470 }
00471
00472
00473 bool BinFinder::inCorner(CvSeq* result)
00474 {
00475
00476 for(int pti=0; pti < result->total; pti++)
00477 {
00478 Point2D<uint> pt;
00479 pt.i = ((CvPoint*)cvGetSeqElem(result, pti))->x;
00480 pt.j = ((CvPoint*)cvGetSeqElem(result, pti))->y;
00481
00482
00483 uint right_edge = itsWidth - 2;
00484 uint bottom_edge = itsHeight - 2;
00485
00486 if((pt.i <= CORNER_TOLERANCE && pt.j <= CORNER_TOLERANCE) ||
00487 (pt.i >= right_edge-CORNER_TOLERANCE && pt.j <= CORNER_TOLERANCE) ||
00488 (pt.i <= CORNER_TOLERANCE && pt.j >= bottom_edge-CORNER_TOLERANCE) ||
00489 (pt.i >= right_edge-CORNER_TOLERANCE && pt.j >= bottom_edge-CORNER_TOLERANCE)
00490 )
00491 return true;
00492 }
00493
00494 return false;
00495 }
00496
00497 #endif