VisionRecognizer.C

Go to the documentation of this file.
00001 /*!@file SeaBee/VisionRecognizer.C */
00002 // //////////////////////////////////////////////////////////////////// //
00003 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the //
00004 // University of Southern California (USC) and the iLab at USC.         //
00005 // See http://iLab.usc.edu for information about this project.          //
00006 // //////////////////////////////////////////////////////////////////// //
00007 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00008 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00009 // in Visual Environments, and Applications'' by Christof Koch and      //
00010 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00011 // pending; application number 09/912,225 filed July 23, 2001; see      //
00012 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00013 // //////////////////////////////////////////////////////////////////// //
00014 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00015 //                                                                      //
00016 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00017 // redistribute it and/or modify it under the terms of the GNU General  //
00018 // Public License as published by the Free Software Foundation; either  //
00019 // version 2 of the License, or (at your option) any later version.     //
00020 //                                                                      //
00021 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00022 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00023 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00024 // PURPOSE.  See the GNU General Public License for more details.       //
00025 //                                                                      //
00026 // You should have received a copy of the GNU General Public License    //
00027 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00028 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00029 // Boston, MA 02111-1307 USA.                                           //
00030 // //////////////////////////////////////////////////////////////////// //
00031 //
00032 // Primary maintainer for this file: Kevin Greene <kgreene@usc.edu> & Josh Villbrandt <josh.villbrandt@usc.edu>
00033 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/SeaBee/VisionRecognizer.C $
00034 // $Id: VisionRecognizer.C 12962 2010-03-06 02:13:53Z irock $
00035 
00036 #ifdef HAVE_OPENCV
00037 
00038 #include "SeaBee/VisionRecognizer.H"
00039 
00040 // ######################################################################
00041 
00042 VisionRecognizer::VisionRecognizer()
00043 {
00044   //  itsDispWin.reset(new XWinManaged(Dims(320*2,240*2), 0, 0, "Contour Recognizer Display"));
00045 }
00046 
00047 std::vector<LineSegment2D> VisionRecognizer::getHoughLines( IplImage cannyImage )
00048 {
00049 #ifndef HAVE_OPENCV
00050   LFATAL("OpenCV must be installed in order to use this function");
00051 #else
00052   // Storage for use in hough transform.
00053   CvMemStorage* storage = cvCreateMemStorage(0);
00054 
00055   // Perform hough transform and store hough lines.
00056   CvSeq* cvLines = cvHoughLines2(&cannyImage, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 30, 20, 10);
00057 
00058 
00059   // Storage for hough line segments.
00060   std::vector <LineSegment2D> lineSegments;
00061 
00062   // Loop through hough lines, store them as line segments, and draw lines in output image.
00063   for(int i = 0; i < cvLines->total; i++ )
00064   {
00065     // Get a line.
00066     CvPoint* line = (CvPoint*)cvGetSeqElem(cvLines,i);
00067 
00068     // Get line end points.
00069     Point2D<int> pt1 = Point2D<int>(line[0].x,line[0].y);
00070     Point2D<int> pt2 = Point2D<int>(line[1].x,line[1].y);
00071 
00072     // Create line segment from endpoints and store.
00073     lineSegments.push_back(LineSegment2D(pt1,pt2));
00074   }
00075   cvReleaseMemStorage( &storage );
00076 
00077   return lineSegments;
00078 #endif // HAVE_OPENCV
00079 }
00080 
00081 // ######################################################################
00082 
00083 std::vector<LineSegment2D> VisionRecognizer::pruneHoughLines (const std::vector<LineSegment2D> lineSegments)
00084 {
00085   uint numLines = lineSegments.size();
00086   if(numLines == 0) { LDEBUG("No hough lines to prune"); }
00087 
00088   std::vector< std::vector<LineSegment2D> > pipeLines;
00089 
00090   //Go through all the lines
00091   for(uint r = 0; r < numLines; r++)
00092     {
00093       int lnIndex = -1;
00094 
00095       //check to see if the current lines fits into a bucket
00096       for(uint c = 0; c < pipeLines.size(); c++)
00097         {
00098           LineSegment2D pipeLine = pipeLines[c][0];
00099 
00100           if(pipeLine.isValid() && lineSegments[r].angleBetween(pipeLine) < 5*(M_PI/180))//convert 5 degrees to radians
00101           {
00102             lnIndex = c;
00103             break;
00104           }
00105         }
00106 
00107       //if the line fits into a pre-existing bucket, add it to the bucket
00108       if( lnIndex > 0 )
00109         {
00110           pipeLines[lnIndex].push_back(lineSegments[r]);
00111           //average the old bucket's value with the new line added
00112           //so as to create a moving bucket
00113           Point2D<int> newPt1 =
00114             Point2D<int>(((lineSegments[r].point1().i + pipeLines[lnIndex][0].point1().i)/2),
00115                          ((lineSegments[r].point1().j + pipeLines[lnIndex][0].point1().j)/2));
00116 
00117           Point2D<int> newPt2 = Point2D<int>(((lineSegments[r].point2().i + pipeLines[lnIndex][0].point2().i)/2),
00118                             ((lineSegments[r].point2().j + pipeLines[lnIndex][0].point2().j)/2));
00119 
00120           pipeLines[lnIndex][0] = LineSegment2D(newPt1,newPt2);
00121 
00122         }
00123       //otherwise, create a new bucket
00124       else
00125         {
00126           std::vector<LineSegment2D> newCntrLines;
00127           newCntrLines.push_back(lineSegments[r]);
00128           pipeLines.push_back(newCntrLines);
00129         }
00130     }
00131 
00132   std::vector<LineSegment2D> centerPipeLines;
00133 
00134   uint pipeLineSize = pipeLines.size();
00135 
00136   for(uint c = 0; c < pipeLineSize; c++)
00137     {
00138       centerPipeLines.push_back(pipeLines[c][0]);
00139     }
00140 //  std::vector<LineSegment2D> centerPipeLines;
00141 
00142 //   Point2D<int> two = Point2D<int>(2,2);
00143 
00144 //   for(uint c = 0; c < pipeLines.size(); c++)
00145 //     {
00146 //       if(pipeLines[c].size() == 2)
00147 //         {
00148 //           Point2D<int> endPoint1 = Point2D<int>((pipeLines[c][0].point1()+pipeLines[c][1].point1())/two);
00149 //           Point2D<int> endPoint2 = Point2D<int>((pipeLines[c][0].point2()+pipeLines[c][1].point2())/two);
00150 
00151 //           centerPipeLines.push_back(LineSegment2D(endPoint1,endPoint2));
00152 //         }
00153 //     }
00154 
00155   return centerPipeLines;
00156 }
00157 // ######################################################################
00158 
00159 CvSeq* VisionRecognizer::getContours( IplImage* img )
00160 {
00161 
00162   Image< PixRGB<byte> > dispImg(320*2,240*2, ZEROS);
00163 
00164   int thresh = 50;
00165 
00166   CvMemStorage* storage = 0;
00167 
00168   // create memory storage that will contain all the dynamic data
00169   storage = cvCreateMemStorage(0);
00170 
00171   CvSeq* contours;
00172   int i, c, l, N = 11;
00173   CvSize sz = cvSize( img->width & -2, img->height & -2 );
00174   IplImage* timg = cvCloneImage( img ); // make a copy of input image
00175   IplImage* gray = cvCreateImage( sz, 8, 1 );
00176   IplImage* pyr = cvCreateImage( cvSize(sz.width/2, sz.height/2), 8, 3 );
00177   IplImage* tgray;
00178   CvSeq* result;
00179   double s, t;
00180   // create empty sequence that will contain points -
00181   // 4 points per square (the square's vertices)
00182   CvSeq* squares = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvPoint), storage );
00183 
00184   // select the maximum ROI in the image
00185   // with the width and height divisible by 2
00186   cvSetImageROI( timg, cvRect( 0, 0, sz.width, sz.height ));
00187 
00188   // down-scale and upscale the image to filter out the noise
00189   cvPyrDown( timg, pyr, 7 );
00190   cvPyrUp( pyr, timg, 7 );
00191   tgray = cvCreateImage( sz, 8, 1 );
00192 
00193   // find squares in every color plane of the image
00194   for( c = 0; c < 3; c++ )
00195     {
00196       // extract the c-th color plane
00197       cvSetImageCOI( timg, c+1 );
00198       cvCopy( timg, tgray, 0 );
00199 
00200       // try several threshold levels
00201       for( l = 0; l < N; l++ )
00202         {
00203           // hack: use Canny instead of zero threshold level.
00204           // Canny helps to catch squares with gradient shading
00205           if( l == 0 )
00206             {
00207               // apply Canny. Take the upper threshold from slider
00208               // and set the lower to 0 (which forces edges merging)
00209               cvCanny( tgray, gray, 0, thresh, 5 );
00210               // dilate canny output to remove potential
00211               // holes between edge segments
00212               cvDilate( gray, gray, 0, 1 );
00213             }
00214           else
00215             {
00216               // apply threshold if l!=0:
00217               //     tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0
00218               cvThreshold( tgray, gray, (l+1)*255/N, 255, CV_THRESH_BINARY );
00219             }
00220 
00221           // find contours and store them all as a list
00222           cvFindContours( gray, storage, &contours, sizeof(CvContour),
00223                           CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
00224 
00225           // test each contour
00226           while( contours )
00227             {
00228               // approximate contour with accuracy proportional
00229               // to the contour perimeter
00230               result = cvApproxPoly( contours, sizeof(CvContour), storage,
00231                                      CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0 );
00232               // square contours should have 4 vertices after approximation
00233               // relatively large area (to filter out noisy contours)
00234               // and be convex.
00235               // Note: absolute value of an area is used because
00236               // area may be positive or negative - in accordance with the
00237               // contour orientation
00238               if( result->total == 4 &&
00239                   fabs(cvContourArea(result,CV_WHOLE_SEQ)) > 1000 &&
00240                   fabs(cvContourArea(result,CV_WHOLE_SEQ)) < 2500 &&
00241                   cvCheckContourConvexity(result) )
00242                 {
00243                   s = 0;
00244 
00245                   //LINFO("AREA: %d",int(fabs(cvContourArea(result,CV_WHOLE_SEQ))));
00246 
00247                   for( i = 0; i < 5; i++ )
00248                     {
00249                       // find minimum angle between joint
00250                       // edges (maximum of cosine)
00251                       if( i >= 2 )
00252                         {
00253                           t = fabs(angle(
00254                                          (CvPoint*)cvGetSeqElem( result, i ),
00255                                          (CvPoint*)cvGetSeqElem( result, i-2 ),
00256                                          (CvPoint*)cvGetSeqElem( result, i-1 )));
00257                           s = s > t ? s : t;
00258                         }
00259                     }
00260 
00261                   // if cosines of all angles are small
00262                   // (all angles are ~90 degree) then write quandrange
00263                   // vertices to resultant sequence
00264                   if( s < 0.3 )
00265                     for( i = 0; i < 4; i++ )
00266                       cvSeqPush( squares,
00267                                  (CvPoint*)cvGetSeqElem( result, i ));
00268                 }
00269 
00270               // take the next contour
00271               contours = contours->h_next;
00272             }
00273         }
00274     }
00275 
00276 
00277   inplacePaste(dispImg, toRGB(ipl2gray(gray)), Point2D<int>(0,0));
00278   inplacePaste(dispImg, ipl2rgb(pyr), Point2D<int>(320,0));
00279   inplacePaste(dispImg, toRGB(ipl2gray(tgray)), Point2D<int>(0,240));
00280   inplacePaste(dispImg, ipl2rgb(timg), Point2D<int>(320,240));
00281 
00282   //  itsDispWin->drawImage(dispImg, 0, 0);
00283 
00284   // release all the temporary images
00285   cvReleaseImage( &gray );
00286   cvReleaseImage( &pyr );
00287   cvReleaseImage( &tgray );
00288   cvReleaseImage( &timg );
00289 
00290   return squares;
00291 }
00292 
00293       // ######################################################################
00294 
00295 IplImage VisionRecognizer::getCannyImage( Image<byte> colorSegmentedImage )
00296 {
00297 #ifndef HAVE_OPENCV
00298   LFATAL("OpenCV must be installed in order to use this function");
00299 #else
00300         // Find edges of segmented image using canny.
00301   IplImage *edge = cvCreateImage( cvGetSize( img2ipl( colorSegmentedImage ) ), 8, 1 );
00302   cvCanny( img2ipl( luminance( colorSegmentedImage ) ), edge, 100, 150, 3 );//150,200,3
00303 
00304         return *edge;
00305 #endif // HAVE_OPENCV
00306 }
00307 
00308 // helper function:
00309 // finds a cosine of angle between vectors
00310 // from pt0->pt1 and from pt0->pt2
00311 double VisionRecognizer::angle( CvPoint* pt1, CvPoint* pt2, CvPoint* pt0 )
00312 {
00313     double dx1 = pt1->x - pt0->x;
00314     double dy1 = pt1->y - pt0->y;
00315     double dx2 = pt2->x - pt0->x;
00316     double dy2 = pt2->y - pt0->y;
00317     return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
00318 }
00319 
00320 #endif
00321 
00322 // ######################################################################
00323 /* So things look consistent in everyone's emacs... */
00324 /* Local Variables: */
00325 /* indent-tabs-mode: nil */
00326 /* End: */
Generated on Sun May 8 08:42:15 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3