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: */