00001 /*!@file SeaBee/test-CVcontour.C test opencv contour function */ 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: Michael Montalbo <montalbo@usc.edu> 00033 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/SeaBee/test-CVcontour.C $ 00034 // $Id: test-CVcontour.C 10794 2009-02-08 06:21:09Z itti $ 00035 00036 #include "Component/ModelManager.H" 00037 00038 #include "Media/FrameSeries.H" 00039 #include "Transport/FrameIstream.H" 00040 #include "Transport/FrameInfo.H" 00041 #include "Media/MediaOpts.H" 00042 00043 #include "Image/Image.H" 00044 #include "Image/Pixels.H" 00045 #include "Raster/Raster.H" 00046 #include "Image/CutPaste.H" 00047 #include "Image/OpenCVUtil.H" 00048 00049 #include "BeoSub/IsolateColor.H" 00050 #include "Image/DrawOps.H" 00051 #include "Image/ColorOps.H" 00052 00053 #include "GUI/XWinManaged.H" 00054 00055 #ifndef HAVE_OPENCV 00056 LFATAL("OpenCV must be installed in order to use this function"); 00057 #else 00058 00059 int thresh = 50; 00060 00061 // helper function: 00062 // finds a cosine of angle between vectors 00063 // from pt0->pt1 and from pt0->pt2 00064 double angle( CvPoint* pt1, CvPoint* pt2, CvPoint* pt0 ) 00065 { 00066 double dx1 = pt1->x - pt0->x; 00067 double dy1 = pt1->y - pt0->y; 00068 double dx2 = pt2->x - pt0->x; 00069 double dy2 = pt2->y - pt0->y; 00070 return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10); 00071 } 00072 00073 // returns sequence of squares detected on the image. 00074 // the sequence is stored in the specified memory storage 00075 CvSeq* findSquares4( IplImage* img, CvMemStorage* storage ) 00076 { 00077 CvSeq* contours; 00078 int i, c, l, N = 11; 00079 CvSize sz = cvSize( img->width & -2, img->height & -2 ); 00080 IplImage* timg = cvCloneImage( img ); // make a copy of input image 00081 IplImage* gray = cvCreateImage( sz, 8, 1 ); 00082 IplImage* pyr = cvCreateImage( cvSize(sz.width/2, sz.height/2), 8, 3 ); 00083 IplImage* tgray; 00084 CvSeq* result; 00085 double s, t; 00086 // create empty sequence that will contain points - 00087 // 4 points per square (the square's vertices) 00088 CvSeq* squares = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvPoint), storage ); 00089 00090 // select the maximum ROI in the image 00091 // with the width and height divisible by 2 00092 cvSetImageROI( timg, cvRect( 0, 0, sz.width, sz.height )); 00093 00094 // down-scale and upscale the image to filter out the noise 00095 cvPyrDown( timg, pyr, 7 ); 00096 cvPyrUp( pyr, timg, 7 ); 00097 tgray = cvCreateImage( sz, 8, 1 ); 00098 00099 // find squares in every color plane of the image 00100 for( c = 0; c < 3; c++ ) 00101 { 00102 // extract the c-th color plane 00103 cvSetImageCOI( timg, c+1 ); 00104 cvCopy( timg, tgray, 0 ); 00105 00106 // try several threshold levels 00107 for( l = 0; l < N; l++ ) 00108 { 00109 // hack: use Canny instead of zero threshold level. 00110 // Canny helps to catch squares with gradient shading 00111 if( l == 0 ) 00112 { 00113 // apply Canny. Take the upper threshold from slider 00114 // and set the lower to 0 (which forces edges merging) 00115 cvCanny( tgray, gray, 0, thresh, 5 ); 00116 // dilate canny output to remove potential 00117 // holes between edge segments 00118 cvDilate( gray, gray, 0, 1 ); 00119 } 00120 else 00121 { 00122 // apply threshold if l!=0: 00123 // tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0 00124 cvThreshold( tgray, gray, (l+1)*255/N, 255, CV_THRESH_BINARY ); 00125 } 00126 00127 // find contours and store them all as a list 00128 cvFindContours( gray, storage, &contours, sizeof(CvContour), 00129 CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) ); 00130 00131 // test each contour 00132 while( contours ) 00133 { 00134 // approximate contour with accuracy proportional 00135 // to the contour perimeter 00136 result = cvApproxPoly( contours, sizeof(CvContour), storage, 00137 CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0 ); 00138 // square contours should have 4 vertices after approximation 00139 // relatively large area (to filter out noisy contours) 00140 // and be convex. 00141 // Note: absolute value of an area is used because 00142 // area may be positive or negative - in accordance with the 00143 // contour orientation 00144 if( result->total == 4 && 00145 fabs(cvContourArea(result,CV_WHOLE_SEQ)) > 1000 && 00146 cvCheckContourConvexity(result) ) 00147 { 00148 s = 0; 00149 00150 for( i = 0; i < 5; i++ ) 00151 { 00152 // find minimum angle between joint 00153 // edges (maximum of cosine) 00154 if( i >= 2 ) 00155 { 00156 t = fabs(angle( 00157 (CvPoint*)cvGetSeqElem( result, i ), 00158 (CvPoint*)cvGetSeqElem( result, i-2 ), 00159 (CvPoint*)cvGetSeqElem( result, i-1 ))); 00160 s = s > t ? s : t; 00161 } 00162 } 00163 00164 // if cosines of all angles are small 00165 // (all angles are ~90 degree) then write quandrange 00166 // vertices to resultant sequence 00167 if( s < 0.3 ) 00168 for( i = 0; i < 4; i++ ) 00169 cvSeqPush( squares, 00170 (CvPoint*)cvGetSeqElem( result, i )); 00171 } 00172 00173 // take the next contour 00174 contours = contours->h_next; 00175 } 00176 } 00177 } 00178 00179 // release all the temporary images 00180 cvReleaseImage( &gray ); 00181 cvReleaseImage( &pyr ); 00182 cvReleaseImage( &tgray ); 00183 cvReleaseImage( &timg ); 00184 00185 return squares; 00186 } 00187 00188 00189 // the function draws all the squares in the image 00190 void drawSquares( IplImage* img, IplImage* out, CvSeq* squares ) 00191 { 00192 CvSeqReader reader; 00193 int i; 00194 00195 // initialize reader of the sequence 00196 cvStartReadSeq( squares, &reader, 0 ); 00197 00198 // read 4 sequence elements at a time (all vertices of a square) 00199 for( i = 0; i < squares->total; i += 4 ) 00200 { 00201 CvPoint pt[4], *rect = pt; 00202 int count = 4; 00203 00204 // read 4 vertices 00205 CV_READ_SEQ_ELEM( pt[0], reader ); 00206 CV_READ_SEQ_ELEM( pt[1], reader ); 00207 CV_READ_SEQ_ELEM( pt[2], reader ); 00208 CV_READ_SEQ_ELEM( pt[3], reader ); 00209 00210 // draw the square as a closed polyline 00211 cvPolyLine( out, &rect, &count, 1, 1, CV_RGB(0,255,0), 3, CV_AA, 0 ); 00212 } 00213 00214 } 00215 00216 int main(int argc, char* argv[]) 00217 { 00218 00219 MYLOGVERB = LOG_INFO; 00220 00221 ModelManager manager("CVContour Tester"); 00222 00223 nub::soft_ref<InputFrameSeries> ifs(new InputFrameSeries(manager)); 00224 manager.addSubComponent(ifs); 00225 00226 nub::soft_ref<OutputFrameSeries> ofs(new OutputFrameSeries(manager)); 00227 manager.addSubComponent(ofs); 00228 00229 manager.exportOptions(MC_RECURSE); 00230 00231 // Parse command-line: 00232 if (manager.parseCommandLine(argc, argv, 00233 "[image {*.ppm}]", 00234 0, 1) 00235 == false) return(1); 00236 00237 int w = ifs->getWidth(), h = ifs->getHeight(); 00238 std::string dims = convertToString(Dims(w, h)); 00239 LINFO("image size: [%dx%d]", w, h); 00240 manager.setOptionValString(&OPT_InputFrameDims, dims); 00241 00242 manager.setModelParamVal("InputFrameDims", Dims(w, h), 00243 MC_RECURSE | MC_IGNORE_MISSING); 00244 00245 manager.start(); 00246 00247 bool goforever = true; 00248 00249 rutz::shared_ptr<XWinManaged> dispWin; 00250 dispWin.reset(new XWinManaged(Dims(w*2,h*2), 0, 0, "Contour Display")); 00251 00252 // input image 00253 Image< PixRGB<byte> > img(w,h, ZEROS); 00254 00255 00256 IplImage* iplImg = 0; 00257 IplImage* out = 0; 00258 CvMemStorage* storage = 0; 00259 00260 bool createMovie = true; 00261 00262 while(goforever) 00263 { 00264 Image< PixRGB<byte> > dispImg(w*2,h*2, ZEROS); 00265 00266 ifs->updateNext(); img = ifs->readRGB(); 00267 if(!img.initialized()) {Raster::waitForKey(); break; } 00268 00269 // create memory storage that will contain all the dynamic data 00270 storage = cvCreateMemStorage(0); 00271 00272 iplImg = cvCloneImage( img2ipl(img) ); 00273 out = cvCloneImage( img2ipl(img) ); 00274 00275 // find and draw the squares 00276 drawSquares( iplImg, out, findSquares4( iplImg, storage ) ); 00277 00278 inplacePaste(dispImg, ipl2rgb(out), Point2D<int>(w,0)); 00279 dispWin->drawImage(dispImg, 0, 0); 00280 00281 00282 if(createMovie) 00283 { 00284 ofs->writeRGB(ipl2rgb(out), "test-CVcontour_Display", 00285 FrameInfo("test-CVcontour_Display", SRC_POS)); 00286 } 00287 00288 // wait for key. 00289 // Raster::waitForKey(); 00290 00291 // release both images 00292 cvReleaseImage( &iplImg ); 00293 cvReleaseImage( &out ); 00294 00295 // clear memory storage - reset free space position 00296 cvClearMemStorage( storage ); 00297 } 00298 00299 // get ready to terminate: 00300 manager.stop(); 00301 return 0; 00302 } 00303 #endif // HAVE_OPENCV 00304 00305 00306 // ###################################################################### 00307 /* So things look consistent in everyone's emacs... */ 00308 /* Local Variables: */ 00309 /* indent-tabs-mode: nil */ 00310 /* End: */