00001 #include "Robots/SeaBeeIII/VisionRectangle.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 VISIONRECTANGLE_C
00017 #define VISIONRECTANGLE_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 VisionRectangle::VisionRectangle
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 VisionRectangle::~VisionRectangle()
00046 {
00047 }
00048
00049
00050 void VisionRectangle::registerTopics()
00051 {
00052 LINFO("Registering VisionRectangle Message");
00053 this->registerPublisher("VisionRectangleMessageTopic");
00054 registerVisionTopics();
00055 }
00056
00057
00058
00059
00060
00061
00062 double VisionRectangle::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* VisionRectangle::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 VisionRectangle::updateFrame(Image<PixRGB<byte> > img, std::string cameraId)
00274 {
00275 bool isFwdCamera = false;
00276 if(cameraId == "FwdCamera")
00277 isFwdCamera = true;
00278
00279 LINFO("Image Received: %d", itsFrameCount);
00280
00281 if(img.initialized())
00282 {
00283
00284 CvMemStorage* storage = cvCreateMemStorage(0);
00285 IplImage* img0 = img2ipl(img);
00286
00287 CvSeq *cvsquares = findSquares4( img0, storage );
00288
00289 cvReleaseImage( &img0 );
00290
00291 RobotSimEvents::QuadrilateralIceVector quadVect;
00292 ImageIceMod::QuadrilateralIce quad;
00293 Point2D<int> avgCenter(0,0);
00294 std::multimap<int,Point2D<int> > tempPoints;
00295
00296
00297 for(int i=0; i < cvsquares->total; i++) {
00298
00299
00300 Point2D<int> quadPoint;
00301 quadPoint.i = ((CvPoint*)cvGetSeqElem(cvsquares, i))->x;
00302 quadPoint.j = ((CvPoint*)cvGetSeqElem(cvsquares, i))->y;
00303
00304
00305 avgCenter += Point2D<int>(quadPoint.i,quadPoint.j);
00306
00307
00308 tempPoints.insert(make_pair(quadPoint.j,quadPoint));
00309
00310
00311
00312
00313 if (tempPoints.size() == 4)
00314 {
00315 std::vector<Point2D<int> > tempVec;
00316
00317 for(std::map<int,Point2D<int > >::const_iterator it = tempPoints.begin();
00318 it != tempPoints.end(); ++it)
00319 {
00320 tempVec.push_back(it->second);
00321 }
00322
00323
00324
00325
00326 if(tempVec[0].i < tempVec[1].i)
00327 {
00328 quad.tl.i = tempVec[0].i;
00329 quad.tr.i = tempVec[1].i;
00330
00331 quad.tl.j = tempVec[0].j;
00332 quad.tr.j = tempVec[1].j;
00333 }
00334 else
00335 {
00336 quad.tr.i = tempVec[0].i;
00337 quad.tl.i = tempVec[1].i;
00338
00339 quad.tr.j = tempVec[0].j;
00340 quad.tl.j = tempVec[1].j;
00341 }
00342
00343
00344
00345 if(tempVec[2].i < tempVec[3].i)
00346 {
00347 quad.bl.i = tempVec[2].i;
00348 quad.br.i = tempVec[3].i;
00349
00350 quad.bl.j = tempVec[2].j;
00351 quad.br.j = tempVec[3].j;
00352 }
00353 else
00354 {
00355 quad.br.i = tempVec[2].i;
00356 quad.bl.i = tempVec[3].i;
00357
00358 quad.br.j = tempVec[2].j;
00359 quad.bl.j = tempVec[3].j;
00360 }
00361
00362
00363
00364
00365 avgCenter /= Point2D<int>(4,4);
00366 quad.center.i = avgCenter.i;
00367 quad.center.j = avgCenter.j;
00368
00369
00370 bool isDupe = false;
00371
00372
00373
00374 for(uint j = 0; j < quadVect.size(); j++)
00375 {
00376 if(avgCenter.distance(Point2D<int>(quadVect[j].center.i,quadVect[j].center.j))
00377 < MIN_CENTER_DIST)
00378 {
00379 isDupe = true;
00380 }
00381 }
00382
00383
00384 if(!isDupe)
00385 {
00386 LineSegment2D vertLine = LineSegment2D((Point2D<int>(quad.tr.i,quad.tr.j) +
00387 Point2D<int>(quad.tl.i,quad.tl.j))/2,
00388 (Point2D<int>(quad.br.i,quad.br.j) +
00389 Point2D<int>(quad.bl.i,quad.bl.j))/2);
00390
00391 LineSegment2D horizLine = LineSegment2D((Point2D<int>(quad.tl.i,quad.tl.j) +
00392 Point2D<int>(quad.bl.i,quad.bl.j))/2,
00393 (Point2D<int>(quad.tr.i,quad.tr.j) +
00394 Point2D<int>(quad.br.i,quad.br.j))/2);
00395 float ratio = 0.0;
00396 float angle = 0.0;
00397 if(vertLine.length() > horizLine.length())
00398 {
00399 if(horizLine.length() > 0)
00400 ratio = vertLine.length() / horizLine.length();
00401
00402 angle = vertLine.angle();
00403 }
00404 else
00405 {
00406 if(vertLine.length() > 0)
00407 ratio = horizLine.length() / vertLine.length();
00408
00409 angle = horizLine.angle();
00410 }
00411
00412
00413
00414 angle = angle * (180/M_PI);
00415
00416
00417
00418
00419 if(angle < 0)
00420 angle += 90;
00421 else
00422 angle += -90;
00423
00424 quad.ratio = ratio;
00425 quad.angle = angle;
00426 quadVect.push_back(quad);
00427
00428
00429 drawLine(img,Point2D<int>(quad.tr.i,quad.tr.j),
00430 Point2D<int>(quad.br.i,quad.br.j),
00431 PixRGB<byte>(0,255,0),2);
00432 drawLine(img,Point2D<int>(quad.br.i,quad.br.j),
00433 Point2D<int>(quad.bl.i,quad.bl.j),
00434 PixRGB<byte>(0,255,0),2);
00435 drawLine(img,Point2D<int>(quad.bl.i,quad.bl.j),
00436 Point2D<int>(quad.tl.i,quad.tl.j),
00437 PixRGB<byte>(0,255,0),2);
00438 drawLine(img,Point2D<int>(quad.tl.i,quad.tl.j),
00439 Point2D<int>(quad.tr.i,quad.tr.j),
00440 PixRGB<byte>(0,255,0),2);
00441
00442 char* str = new char[20];
00443 sprintf(str,"%1.2f, %2.1f",ratio,angle);
00444 writeText(img,Point2D<int>(quad.center.i,quad.center.j),str);
00445 delete [] str;
00446
00447 }
00448
00449
00450 quad = ImageIceMod::QuadrilateralIce();
00451 avgCenter = Point2D<int>(0,0);
00452 tempPoints.clear();
00453
00454 }
00455 }
00456
00457 itsOfs->writeRGB(img, "Vision Rectangle Image",
00458 FrameInfo("Vision Rectangle Image", SRC_POS));
00459
00460 itsOfs->updateNext();
00461
00462 if (quadVect.size() > 0) {
00463 RobotSimEvents::VisionRectangleMessagePtr msg = new RobotSimEvents::VisionRectangleMessage;
00464 msg->quads = quadVect;
00465 msg->isFwdCamera = isFwdCamera;
00466 this->publish("VisionRectangleMessageTopic", msg);
00467 }
00468
00469 cvReleaseMemStorage( &storage );
00470 }
00471 }
00472
00473
00474 bool VisionRectangle::inCorner(CvSeq* result)
00475 {
00476
00477 for(int pti=0; pti < result->total; pti++)
00478 {
00479 Point2D<uint> pt;
00480 pt.i = ((CvPoint*)cvGetSeqElem(result, pti))->x;
00481 pt.j = ((CvPoint*)cvGetSeqElem(result, pti))->y;
00482
00483
00484 uint right_edge = itsWidth - 2;
00485 uint bottom_edge = itsHeight - 2;
00486
00487 if((pt.i <= CORNER_TOLERANCE && pt.j <= CORNER_TOLERANCE) ||
00488 (pt.i >= right_edge-CORNER_TOLERANCE && pt.j <= CORNER_TOLERANCE) ||
00489 (pt.i <= CORNER_TOLERANCE && pt.j >= bottom_edge-CORNER_TOLERANCE) ||
00490 (pt.i >= right_edge-CORNER_TOLERANCE && pt.j >= bottom_edge-CORNER_TOLERANCE)
00491 )
00492 return true;
00493 }
00494
00495 return false;
00496 }
00497
00498 #endif