EnvSegmenterCannyContour.C

Go to the documentation of this file.
00001 /*!@file Neuro/EnvSegmenterCannyContour.C */
00002 
00003 // //////////////////////////////////////////////////////////////////// //
00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005   //
00005 // by the University of Southern California (USC) and the iLab at USC.  //
00006 // See http://iLab.usc.edu for information about this project.          //
00007 // //////////////////////////////////////////////////////////////////// //
00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00010 // in Visual Environments, and Applications'' by Christof Koch and      //
00011 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00012 // pending; application number 09/912,225 filed July 23, 2001; see      //
00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00014 // //////////////////////////////////////////////////////////////////// //
00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00016 //                                                                      //
00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00018 // redistribute it and/or modify it under the terms of the GNU General  //
00019 // Public License as published by the Free Software Foundation; either  //
00020 // version 2 of the License, or (at your option) any later version.     //
00021 //                                                                      //
00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00025 // PURPOSE.  See the GNU General Public License for more details.       //
00026 //                                                                      //
00027 // You should have received a copy of the GNU General Public License    //
00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00030 // Boston, MA 02111-1307 USA.                                           //
00031 // //////////////////////////////////////////////////////////////////// //
00032 //
00033 // Primary maintainer for this file: Rob Peters <rjpeters at usc dot edu>
00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Neuro/EnvSegmenterCannyContour.C $
00035 // $Id: EnvSegmenterCannyContour.C 12782 2010-02-05 22:14:30Z irock $
00036 //
00037 
00038 #include "Image/OpenCVUtil.H"  // must be first to avoid conflicting defs of int64, uint64
00039 
00040 #include "Neuro/EnvSegmenterCannyContour.H"
00041 
00042 #include "Image/CutPaste.H"
00043 #include "Image/DrawOps.H"
00044 #include "Image/Image.H"
00045 #include "Image/Pixels.H"
00046 #include "GUI/DebugWin.H"
00047 
00048 #ifdef HAVE_OPENCV
00049 
00050 namespace
00051 {
00052   const int thresh = 50;
00053 
00054   // helper function:
00055   // finds a cosine of angle between vectors
00056   // from pt0->pt1 and from pt0->pt2
00057   double angle( CvPoint* pt1, CvPoint* pt2, CvPoint* pt0 )
00058   {
00059     double dx1 = pt1->x - pt0->x;
00060     double dy1 = pt1->y - pt0->y;
00061     double dx2 = pt2->x - pt0->x;
00062     double dy2 = pt2->y - pt0->y;
00063     return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
00064   }
00065 
00066   CvSeq* findSquares(const Image<PixRGB<byte> >& in, CvMemStorage* storage,
00067                   const int minarea, const int maxarea, const double mincos)
00068   {
00069     const int N = 11;
00070 
00071     IplImage* img = img2ipl(in);
00072 
00073     CvSize sz = cvSize( img->width & -2, img->height & -2 );
00074     IplImage* timg = cvCloneImage( img ); // make a copy of input image
00075     IplImage* gray = cvCreateImage( sz, 8, 1 );
00076     IplImage* pyr = cvCreateImage( cvSize(sz.width/2, sz.height/2), 8, 3 );
00077     // create empty sequence that will contain points -
00078     // 4 points per square (the square's vertices)
00079     CvSeq* squares = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvPoint), storage );
00080 
00081     // select the maximum ROI in the image
00082     // with the width and height divisible by 2
00083     cvSetImageROI( timg, cvRect( 0, 0, sz.width, sz.height ));
00084 
00085     // down-scale and upscale the image to filter out the noise
00086     cvPyrDown( timg, pyr, 7 );
00087     cvPyrUp( pyr, timg, 7 );
00088     IplImage* tgray = cvCreateImage( sz, 8, 1 );
00089 
00090     // find squares in every color plane of the image
00091     for (int c = 0; c < 3; ++c)
00092       {
00093         // extract the c-th color plane
00094         cvSetImageCOI( timg, c+1 );
00095         cvCopy( timg, tgray, 0 );
00096 
00097         // try several threshold levels
00098         for (int l = 0; l < N; ++l)
00099           {
00100             // hack: use Canny instead of zero threshold level.
00101             // Canny helps to catch squares with gradient shading
00102             if( l == 0 )
00103               {
00104                 // apply Canny. Take the upper threshold from slider
00105                 // and set the lower to 0 (which forces edges merging)
00106                 cvCanny( tgray, gray, 0, thresh, 5 );
00107                 // dilate canny output to remove potential
00108                 // holes between edge segments
00109                 cvDilate( gray, gray, 0, 1 );
00110               }
00111             else
00112               {
00113                 // apply threshold if l!=0:
00114                 //     tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0
00115                 cvThreshold( tgray, gray, (l+1)*255/N, 255, CV_THRESH_BINARY );
00116               }
00117 
00118             // find contours and store them all as a list
00119             CvSeq* contours = 0;
00120             cvFindContours( gray, storage, &contours, sizeof(CvContour),
00121                             CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
00122 
00123             // test each contour
00124             while( contours )
00125               {
00126                 // approximate contour with accuracy proportional
00127                 // to the contour perimeter
00128                 CvSeq* result =
00129                   cvApproxPoly( contours, sizeof(CvContour), storage,
00130                                 CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0 );
00131                 // square contours should have 4 vertices after approximation
00132                 // relatively large area (to filter out noisy contours)
00133                 // and be convex.
00134                 // Note: absolute value of an area is used because
00135                 // area may be positive or negative - in accordance with the
00136                 // contour orientation
00137                 const double area = fabs(cvContourArea(result,CV_WHOLE_SEQ));
00138                 if (result->total == 4 &&
00139                     area >= minarea && area <= maxarea &&
00140                     cvCheckContourConvexity(result))
00141                   {
00142                     double s = 0;
00143 
00144                     for (int i = 0; i < 4; ++i)
00145                       {
00146                         // find minimum angle between joint
00147                         // edges (maximum of cosine)
00148                         const double t =
00149                           fabs(angle((CvPoint*)cvGetSeqElem( result, i % 4 ),
00150                                      (CvPoint*)cvGetSeqElem( result, (i-2) % 4 ),
00151                                      (CvPoint*)cvGetSeqElem( result, (i-1) % 4 )));
00152                         s = s > t ? s : t;
00153                       }
00154 
00155 
00156                     // if cosines of all angles are small
00157                     // (all angles are ~90 degree) then write quandrangle
00158                     // vertices to resultant sequence
00159                     if (s < mincos)
00160                     {
00161                       for (int i = 0; i < 4; ++i)
00162                         cvSeqPush(squares,
00163                                   (CvPoint*)cvGetSeqElem( result, i ));
00164                     //  LINFO("area=%f, mincos=%f", area, s);
00165                     }
00166                   }
00167 
00168                 // take the next contour
00169                 contours = contours->h_next;
00170               }
00171           }
00172       }
00173 
00174     // release all the temporary images
00175     cvReleaseImage( &gray );
00176     cvReleaseImage( &pyr );
00177     cvReleaseImage( &tgray );
00178     cvReleaseImage( &timg );
00179     cvReleaseImageHeader( &img );
00180 
00181     return squares;
00182   }
00183 
00184   // the function draws all the squares in the image
00185   Image<PixRGB<byte> > drawSquares(const Image<PixRGB<byte> >&in, CvSeq* squares)
00186   {
00187     Image<PixRGB<byte> > out(in);
00188 
00189     CvSeqReader reader;
00190 
00191     // initialize reader of the sequence
00192     cvStartReadSeq(squares, &reader, 0);
00193 
00194     // read 4 sequence elements at a time (all vertices of a square)
00195     for (int i = 0; i < squares->total; i += 4)
00196       {
00197         CvPoint pt[4];
00198 
00199         // read 4 vertices
00200         CV_READ_SEQ_ELEM( pt[0], reader );
00201         CV_READ_SEQ_ELEM( pt[1], reader );
00202         CV_READ_SEQ_ELEM( pt[2], reader );
00203         CV_READ_SEQ_ELEM( pt[3], reader );
00204 
00205         for (int j = 0; j < 4; ++j)
00206           drawLine(out,
00207                    Point2D<int>(pt[j].x, pt[j].y),
00208                    Point2D<int>(pt[(j+1)%4].x, pt[(j+1)%4].y),
00209                    PixRGB<byte>(0, 255, 0),
00210                    2);
00211       }
00212 
00213     return out;
00214   }
00215 
00216   Rectangle getRectangle(CvSeq* cards)
00217   {
00218     CvSeqReader reader;
00219     // initialize reader of the sequence
00220     cvStartReadSeq( cards, &reader, 0 );
00221 
00222     Rectangle result;
00223 
00224     if (cards->total > 0)
00225       {
00226         CvPoint pt[4];
00227         // read 4 vertices
00228         CV_READ_SEQ_ELEM( pt[0], reader );
00229         CV_READ_SEQ_ELEM( pt[1], reader );
00230         CV_READ_SEQ_ELEM( pt[2], reader );
00231         CV_READ_SEQ_ELEM( pt[3], reader );
00232 
00233         //Find the bounding box
00234         Point2D<int> tl(pt[0].x, pt[0].y), br(pt[0].x, pt[0].y);
00235         for(int i=1; i<4; i++)
00236           {
00237             if (pt[i].x < tl.i) tl.i = pt[i].x;
00238             else if (pt[i].x > br.i) br.i = pt[i].x;
00239 
00240             if (pt[i].y < tl.j) tl.j = pt[i].y;
00241             else if (pt[i].y > br.j) br.j = pt[i].y;
00242           }
00243         tl.i -= 10; tl.j -= 10;
00244         br.i += 10; br.j += 10;
00245 
00246         result = Rectangle::tlbrO(tl.j, tl.i, br.j, br.i);
00247       }
00248 
00249     return result;
00250   }
00251 }
00252 
00253 #endif //HAVE_OPENCV
00254 
00255 // ######################################################################
00256 EnvSegmenterCannyContour::EnvSegmenterCannyContour(OptionManager& mgr)
00257   :
00258   EnvSegmenter(mgr, "Embeddable Canny Contour FOA Segmenter",
00259                "EnvSegmenterCannyContour"),
00260   itsMinArea("CannyMinArea", this, 500, ALLOW_ONLINE_CHANGES),
00261   itsMaxArea("CannyMaxArea", this, 7000, ALLOW_ONLINE_CHANGES),
00262   itsMinCos("CannyMinCos", this, 0.1, ALLOW_ONLINE_CHANGES)
00263 #ifdef HAVE_OPENCV
00264   ,
00265   itsStorage(cvCreateMemStorage(0))
00266 #endif
00267 {
00268 #ifndef HAVE_OPENCV
00269   LFATAL("OpenCV must be installed in order to use this function");
00270 #else
00271   ASSERT(itsStorage != 0);
00272 #endif
00273 }
00274 
00275 // ######################################################################
00276 EnvSegmenterCannyContour::~EnvSegmenterCannyContour()
00277 {
00278 #ifndef HAVE_OPENCV
00279   LERROR("OpenCV must be installed in order to use this function");
00280 #else
00281   cvReleaseMemStorage(&itsStorage);
00282 #endif
00283 }
00284 
00285 // ######################################################################
00286 Rectangle EnvSegmenterCannyContour::getFoa(const Image<PixRGB<byte> >& rgbin,
00287                                            const Point2D<int>& center,
00288                                            Image<byte>* foamask,
00289                                            Image<PixRGB<byte> >* segmentdisp) const
00290 {
00291 #ifndef HAVE_OPENCV
00292   LFATAL("OpenCV must be installed in order to use this function");
00293   return Rectangle();
00294 #else
00295 
00296   CvSeq* cards = findSquares(rgbin, itsStorage, itsMinArea.getVal(), itsMaxArea.getVal(), itsMinCos.getVal());
00297 
00298   const Rectangle result = getRectangle(cards);
00299 
00300   const Image<PixRGB<byte> > out = drawSquares(rgbin, cards);
00301 
00302   if (foamask)
00303     {
00304       *foamask = Image<byte>(rgbin.getDims(), ZEROS);
00305 
00306       if (result.isValid())
00307         inplaceClearRegion(*foamask, result, byte(255));
00308       else
00309         foamask->setVal(center, 255);
00310     }
00311 
00312   if (segmentdisp)
00313     *segmentdisp = out;
00314 
00315   cvClearMemStorage(itsStorage);
00316 
00317   return result.getOverlap(rgbin.getBounds());
00318 #endif
00319 }
00320 
00321 // ######################################################################
00322 std::vector<Rectangle> EnvSegmenterCannyContour::getSquares(const Image<PixRGB<byte> >& rgbin, Image<PixRGB<byte> >* segmentDisp)
00323 {
00324 #ifndef HAVE_OPENCV
00325   LFATAL("OpenCV must be installed in order to use this function");
00326   return std::vector<Rectangle>();
00327 #else
00328 
00329   CvSeq* squares = findSquares(rgbin, itsStorage, itsMinArea.getVal(), itsMaxArea.getVal(), itsMinCos.getVal());
00330 
00331 
00332   if (segmentDisp)
00333     *segmentDisp = drawSquares(rgbin, squares);
00334 
00335   //const Rectangle square = getRectangle(squares);
00336   std::vector<Rectangle> results;
00337 
00338 
00339   CvSeqReader reader;
00340   // initialize reader of the sequence
00341   cvStartReadSeq(squares, &reader, 0);
00342 
00343   // read 4 sequence elements at a time (all vertices of a square)
00344   for (int i = 0; i < squares->total; i += 4)
00345   {
00346     CvPoint pt[4];
00347 
00348     // read 4 vertices
00349     CV_READ_SEQ_ELEM( pt[0], reader );
00350     CV_READ_SEQ_ELEM( pt[1], reader );
00351     CV_READ_SEQ_ELEM( pt[2], reader );
00352     CV_READ_SEQ_ELEM( pt[3], reader );
00353 
00354     //Find the bounding box
00355     Point2D<int> tl(pt[0].x, pt[0].y), br(pt[0].x, pt[0].y);
00356     for(int i=1; i<4; i++)
00357     {
00358       if (pt[i].x < tl.i) tl.i = pt[i].x;
00359       else if (pt[i].x > br.i) br.i = pt[i].x;
00360 
00361       if (pt[i].y < tl.j) tl.j = pt[i].y;
00362       else if (pt[i].y > br.j) br.j = pt[i].y;
00363     }
00364     tl.i -= 10; tl.j -= 10;
00365     br.i += 10; br.j += 10;
00366     Rectangle rect  = Rectangle::tlbrO(tl.j, tl.i, br.j, br.i);
00367     results.push_back(rect.getOverlap(rgbin.getBounds()));
00368   }
00369 
00370   cvClearMemStorage(itsStorage);
00371 
00372   return results;
00373 #endif
00374 }
00375 
00376 // ######################################################################
00377 /* So things look consistent in everyone's emacs... */
00378 /* Local Variables: */
00379 /* mode: c++ */
00380 /* indent-tabs-mode: nil */
00381 /* End: */
Generated on Sun May 8 08:41:02 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3