From 15c02439915a7a8757e8520b6a610163a72d6a80 Mon Sep 17 00:00:00 2001 From: Antoine Villeret Date: Wed, 26 Dec 2012 19:54:45 +0000 Subject: add convexity defects ouput add convexhull output add contours hierarchy svn path=/trunk/externals/pix_opencv/; revision=16759 --- pix_opencv_contours.cc | 304 +++++++++++++++++++++++++++++++++++++++---------- pix_opencv_contours.h | 10 +- 2 files changed, 252 insertions(+), 62 deletions(-) diff --git a/pix_opencv_contours.cc b/pix_opencv_contours.cc index 2697fcb..6032d1b 100644 --- a/pix_opencv_contours.cc +++ b/pix_opencv_contours.cc @@ -33,7 +33,12 @@ CPPEXTERN_NEW(pix_opencv_contours) // Constructor // ///////////////////////////////////////////////////////// -pix_opencv_contours :: pix_opencv_contours() : m_area_threshold(30) +pix_opencv_contours :: pix_opencv_contours() : m_area_threshold(30), \ + m_epsilon(2), \ + m_enable_contours(1), \ + m_enable_hulls(1), \ + m_enable_defects(1), \ + m_hierarchy_level(-1) { m_dataout_middle = outlet_new(this->x_obj, 0); m_dataout_right = outlet_new(this->x_obj, 0); @@ -72,87 +77,243 @@ void pix_opencv_contours :: processGrayImage(imageStruct &image) cv::Mat imgMat2( image.ysize, image.xsize, CV_8UC1, image.data, image.csize*image.xsize); // just transform imageStruct to IplImage without copying data - cv::Mat imgMat = imgMat2.clone(); + cv::Mat imgMat = imgMat2.clone(); // copy data because findContours need it... + + m_contours.clear(); + m_convexhulls.clear(); + + /*****************/ + /* Find Contours */ + /*****************/ std::vector > contours; - std::vector one_contour; + std::vector hierarchy; - contours.clear(); - m_contours.clear(); + cv::findContours(imgMat, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE); + + /* + std::cout << "hierarchy : \n" << std::endl; + std::cout << "id\tnext\tprev\tchild\tparent" << std::endl; + for ( size_t i = 0; i < contours.size(); i++ ) + { + std::cout << i << "\t" << hierarchy[i][0] << "\t" << hierarchy[i][1] << "\t" << hierarchy[i][2] << "\t" << hierarchy[i][3] << std::endl; + } + */ + + if ( m_hierarchy_level != -1 ) + { + int i=0; + int hierarchy_level=0; + + while ( i < (int) contours.size() && i!=-1 && hierarchy_level != -1 ) + { + if ( cv::contourArea(contours[i], false) > m_area_threshold && hierarchy_level == m_hierarchy_level ) + { + std::vector one_contour; + cv::approxPolyDP(contours[i], one_contour, m_epsilon, true); + m_contours.push_back(one_contour); // push contour if it's big enough + } + if ( hierarchy_level < m_hierarchy_level && hierarchy[i][2] != -1 ){ // si on n'a pas atteint le niveau choisi et qu'il y a un enfant on le prend + hierarchy_level++; + int j = i; + i=hierarchy[j][2]; // get the first child + } else if ( hierarchy[i][0] != -1 ) { + i=hierarchy[i][0]; // get the next contour at this hierarchy level if it exists + } else { + while ( hierarchy_level != -1 ) + { + hierarchy_level--; + i=hierarchy[i][3]; + if ( i < 0 ) break; // pas de parent... + if ( hierarchy[i][0] != -1 ) { + i=hierarchy[i][0]; // next du parent + break; + } + } - cv::findContours(imgMat, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE); + } + } + } else { + for ( size_t i = 0; i < contours.size(); i++ ) + { + if ( cv::contourArea(contours[i], false) > m_area_threshold ){ + std::vector one_contour; + cv::approxPolyDP(contours[i], one_contour, m_epsilon, true); + m_contours.push_back(one_contour); + } + } + } + + if ( m_enable_contours ) + { + t_atom*info; + info = new t_atom[(int) m_contours.size()*20+2]; + // info : 20x(contour_nb) matrix + // info for each contour : area, rotrect corner (8 float), rotrect center, rotrect size, rotation angle, segments number, other are reserved for future use + int count(0); + SETFLOAT(info+1, 20.); + int info_offset(2); - for( size_t i = 0; i < contours.size(); i++ ) { - if ( cv::contourArea(contours[i], false) > m_area_threshold ){ - one_contour.clear(); - cv::approxPolyDP(contours[i], one_contour, m_epsilon, true); - m_contours.push_back(one_contour); + for( size_t i = 0 ; i < m_contours.size(); i++ ) + { + if (!m_contours[i].empty() && m_contours[i].size() > 2) { + SETFLOAT(info+info_offset, (float) cv::contourArea(m_contours[i])); + + cv::RotatedRect rot_rect = cv::minAreaRect(m_contours[i]); + cv::Point2f corners[4]; + rot_rect.points(corners); + for (int j=0;j<4;j++) { + SETFLOAT(info+info_offset+j*2+1, corners[j].x/image.xsize); + SETFLOAT(info+info_offset+j*2+2, corners[j].y/image.ysize); + } + + SETFLOAT(info+info_offset+9, rot_rect.center.x/image.xsize); + SETFLOAT(info+info_offset+10, rot_rect.center.y/image.ysize); + SETFLOAT(info+info_offset+11, rot_rect.size.width/image.xsize); + SETFLOAT(info+info_offset+12, rot_rect.size.height/image.xsize); + SETFLOAT(info+info_offset+13, rot_rect.angle); + SETFLOAT(info+info_offset+14, m_contours[i].size()); // number of points in segment + SETFLOAT(info+info_offset+15, 0); + SETFLOAT(info+info_offset+16, 0); + SETFLOAT(info+info_offset+17, 0); + SETFLOAT(info+info_offset+18, 0); + SETFLOAT(info+info_offset+19, 0); + + info_offset+=20; + count++; + } } + SETFLOAT(info, (float) count); + if (count) outlet_anything(m_dataout_right, gensym("info"), count*20+2, info); + else outlet_float(m_dataout_right, 0); + + for( size_t i = 0 ; i < m_contours.size() ; i++ ) + { + if (!m_contours[i].empty() && m_contours[i].size() > 2) { + int size = 2+m_contours[i].size()*2; + t_atom*ap = new t_atom[size]; + SETFLOAT(ap, static_cast(m_contours[i].size())); + SETFLOAT(ap+1, 2.0); + + int offset(2); + + for ( size_t j = 0 ; j < m_contours[i].size() ; j++){ + cv::Point pt = m_contours[i][j]; + SETFLOAT(ap+offset,(float) pt.x/image.xsize); + SETFLOAT(ap+offset+1,(float) pt.y/image.ysize); + offset+=2; + } + outlet_anything(m_dataout_middle, gensym("contour"), size, ap); + if(ap)delete[]ap;ap=NULL; + + } + } + + if (info) delete info; + info = NULL; } + //~ cv::drawContours(imgMat2, m_contours, -1, cv::Scalar(128,255,255), 3); - - t_atom*info; - info = new t_atom[(int) m_contours.size()*20+2]; - // info : 20x(contour_nb) matrix - // info for each contour : area, rotrect corner (8 float), rotrect center, rotrect size, rotation angle, segments number, other are reserved for future use - int count(0); - SETFLOAT(info+1, 20.); - int info_offset(2); - for( std::vector >::iterator it = m_contours.begin(); it != m_contours.end(); ++it ) { - if (!it->empty() && it->size() > 2) { - SETFLOAT(info+info_offset, (float) cv::contourArea(*it)); + /**********************/ + /* Compute Convexhull */ + /**********************/ + if ( m_enable_defects || m_enable_hulls ) + { + for ( size_t i = 0; i < m_contours.size(); i++ ) + { + std::vector convexhull; + cv::convexHull(m_contours[i], convexhull); + m_convexhulls.push_back(convexhull); + } + } + + if ( m_enable_hulls ) + { + for ( size_t i = 0 ; i < m_convexhulls.size() ; i++ ) + { + int list_size=(int) m_convexhulls[i].size()*2+2; - cv::RotatedRect rot_rect = cv::minAreaRect(*it); - cv::Point2f corners[4]; - rot_rect.points(corners); - for (int j=0;j<4;j++) { - SETFLOAT(info+info_offset+j*2+1, corners[j].x/image.xsize); - SETFLOAT(info+info_offset+j*2+2, corners[j].y/image.ysize); - } + t_atom* data = new t_atom[list_size]; + + SETFLOAT(data,m_convexhulls[i].size()); // nb of points for current convexhull + SETFLOAT(data+1, 2); // each point is represented by 2 values + + t_atom* apt=data+2; - SETFLOAT(info+info_offset+9, rot_rect.center.x/image.xsize); - SETFLOAT(info+info_offset+10, rot_rect.center.y/image.ysize); - SETFLOAT(info+info_offset+11, rot_rect.size.width/image.xsize); - SETFLOAT(info+info_offset+12, rot_rect.size.height/image.xsize); - SETFLOAT(info+info_offset+13, rot_rect.angle); - SETFLOAT(info+info_offset+14, it->size()); // number of points in segment - SETFLOAT(info+info_offset+15, 0); - SETFLOAT(info+info_offset+16, 0); - SETFLOAT(info+info_offset+17, 0); - SETFLOAT(info+info_offset+18, 0); - SETFLOAT(info+info_offset+19, 0); + for ( size_t j = 0 ; j < m_convexhulls[i].size() ; j++){ + int k = m_convexhulls[i][j]; + cv::Point pt = m_contours[i][k]; + SETFLOAT(apt, (float) pt.x/image.xsize); + SETFLOAT(apt+1, (float) pt.y/image.ysize); + apt+=2; + } + outlet_anything(m_dataout_middle, gensym("convexhull"), list_size, data); - info_offset+=20; - count++; + if (data) delete data; + data = NULL; } } - SETFLOAT(info, (float) count); - if (count) outlet_anything(m_dataout_right, gensym("info"), count*20+2, info); - else outlet_float(m_dataout_right, 0); - for( std::vector >::iterator it = m_contours.begin(); it != m_contours.end(); ++it ) { - if (!it->empty() && it->size() > 2) { - int size = 2+it->size()*2; - t_atom*ap = new t_atom[size]; - SETFLOAT(ap, static_cast(it->size())); - SETFLOAT(ap+1, 2.0); + /*****************************/ + /* Compute convexity defects */ + /*****************************/ + if ( m_enable_defects ) + { + for ( size_t i = 0 ; i < m_contours.size() ; i++ ) + { + std::vector defects(m_convexhulls[i].size()); + + cv::Ptr storage = cvCreateMemStorage(); + cv::InputArray _points = m_contours[i]; + cv::InputArray _hull = m_convexhulls[i]; + + cv::Mat points = _points.getMat(); + cv::Mat hull = _hull.getMat(); - int offset(2); + CvMat c_points = points, c_hull = hull; - for ( std::vector::iterator ite=it->begin(); ite!=it->end(); ++ite){ - SETFLOAT(ap+offset,(float) (*ite).x/image.xsize); - SETFLOAT(ap+offset+1,(float) (*ite).y/image.ysize); - offset+=2; + CvSeq* seq = cvConvexityDefects(&c_points, &c_hull, storage); + + double norm = sqrtf( image.xsize*image.ysize ); + + if ( !seq ) { + error("seq undefined..."); + continue; } - outlet_anything(m_dataout_middle, gensym("contour"), size, ap); - if(ap)delete[]ap;ap=NULL; + int list_size=(int) seq->total*7+2; + + if (seq->total > 0) + { + t_atom* data = new t_atom[list_size]; + + SETFLOAT(data, seq->total); // number of defect for current contour + SETFLOAT(data+1, 7); // a defect is represented by 7 values : start point (x,y), end point (x,y), farthest point (x,y) and defect depth + + cv::SeqIterator it = cv::Seq(seq).begin(); // TODO : crash sometimes but don't know why yet... + t_atom* apt = data+2; + + for ( int j = 0 ; j < seq->total ; j++, ++it ) + { + CvConvexityDefect& defect = *it; + SETFLOAT(apt, (float) defect.start->x/image.xsize); + SETFLOAT(apt+1, (float) defect.start->y/image.ysize); + SETFLOAT(apt+2, (float) defect.end->x/image.xsize); + SETFLOAT(apt+3, (float) defect.end->y/image.ysize); + SETFLOAT(apt+4, (float) defect.depth_point->x/image.xsize); + SETFLOAT(apt+5, (float) defect.depth_point->y/image.ysize); + SETFLOAT(apt+6, (float) defect.depth/norm); + apt+=7; + } + + outlet_anything(m_dataout_middle, gensym("convexitydefects"), list_size, data); + + if (data) delete data; + data = NULL; + } } } - - if (info) delete info; - info = NULL; } ///////////////////////////////////////////////////////// @@ -163,6 +324,10 @@ void pix_opencv_contours :: obj_setupCallback(t_class *classPtr) { CPPEXTERN_MSG1(classPtr, "epsilon", epsilonMess, double); CPPEXTERN_MSG1(classPtr, "area", areaMess, double); + CPPEXTERN_MSG1(classPtr, "contours", contoursMess, double); + CPPEXTERN_MSG1(classPtr, "convexhulls", convexhullsMess, double); + CPPEXTERN_MSG1(classPtr, "convexitydefects", convexitydefectsMess, double); + CPPEXTERN_MSG1(classPtr, "hierarchy_level", hierarchyMess, double); } ///////////////////////////////////////////////////////// @@ -183,3 +348,20 @@ void pix_opencv_contours :: areaMess(double arg) SETFLOAT(&data_out, (float) m_area_threshold); outlet_anything( m_dataout_right, gensym("area"), 1, &data_out); } +void pix_opencv_contours :: contoursMess(double arg) +{ + m_enable_contours = arg > 0; +} +void pix_opencv_contours :: convexhullsMess(double arg) +{ + m_enable_hulls = arg > 0; +} +void pix_opencv_contours :: convexitydefectsMess(double arg) +{ + m_enable_defects = arg > 0; +} +void pix_opencv_contours :: hierarchyMess(double arg) +{ + m_hierarchy_level = arg < -1 ? -1 : arg; + m_mode = m_hierarchy_level == -1 ? CV_RETR_LIST : CV_RETR_TREE; +} diff --git a/pix_opencv_contours.h b/pix_opencv_contours.h index 5882b5a..fb1ebe1 100644 --- a/pix_opencv_contours.h +++ b/pix_opencv_contours.h @@ -61,14 +61,22 @@ class GEM_EXTERN pix_opencv_contours : public GemPixObj // Messages handling void epsilonMess(double arg); void areaMess(double arg); + void contoursMess(double arg); + void convexhullsMess(double arg); + void convexitydefectsMess(double arg); + void hierarchyMess(double arg); private: t_outlet *m_dataout_middle; // contour outlet t_outlet *m_dataout_right; // info outlet - std::vector > m_contours; + std::vector > m_contours; + std::vector > m_convexhulls; + //~ std::vector m_hierarchy; double m_area_threshold; // min area for contour double m_epsilon; + + int m_enable_contours, m_enable_hulls, m_enable_defects, m_hierarchy_level, m_mode; }; #endif // for header file -- cgit v1.2.1