物体轮廓检测

  1. int cvFindContours( CvArr* image, CvMemStorage* storage, CvSeq** first_contour,int header_size=sizeof(CvContour),int mode=CV_RETR_LIST,int method=CV_CHAIN_APPROX_SIMPLE, CvPoint offset=cvPoint(0,0) );

这个函数用起来很方便,但是随着你使用的深入,你会发现有一些迷惑在这里。比如当你提取轮廓时只需要最外围的一个轮廓,但是你会发现当轮廓画出来时是好几个;当你需要找一个最大轮廓时却发现找出来的却根本就不是你想要的那个。带着这样问题我们再来仔细看看cvFindContours这个函数。

下边的是一位仁兄写的测试程序和测试图片,说明提取轮廓的两种方法及绘制轮廓中最大等级分析的问题,

  1. /************************************************************************/
  2. /* 提取轮廓两种方法对比及绘制轮廓‘最大等级‘分析                         */
  3. /************************************************************************/
  4. #include "stdafx.h"
  5. #include "cv.h"
  6. #include "highgui.h"
  7. int main()
  8. {
  9. IplImage* img = cvLoadImage("lena.jpg", CV_LOAD_IMAGE_GRAYSCALE);
  10. IplImage* img_temp = cvCreateImage(cvGetSize(img), 8, 1);
  11. cvThreshold(img, img, 128, 255, CV_THRESH_BINARY);
  12. CvMemStorage* mem_storage = cvCreateMemStorage(0);
  13. CvSeq *first_contour = NULL, *c = NULL;
  14. //////////////////////////////////////////////////////////////////////////
  15. // 1、
  16. cvNamedWindow("contour1");
  17. cvCopyImage(img, img_temp);
  18. double t = (double)cvGetTickCount();
  19. cvFindContours(img_temp, mem_storage, &first_contour);
  20. cvZero(img_temp);
  21. cvDrawContours(
  22. img_temp,
  23. first_contour,
  24. cvScalar(100),
  25. cvScalar(100),
  26. 1
  27. );
  28. t = (double)cvGetTickCount() - t;
  29. cvShowImage("contour1", img_temp);
  30. printf("run1 = %gms\n", t/(cvGetTickFrequency()*1000.));
  31. cvClearMemStorage(mem_storage);
  32. //////////////////////////////////////////////////////////////////////////
  33. // 2、
  34. cvNamedWindow("contour2");
  35. cvCopyImage(img, img_temp);
  36. t = (double)cvGetTickCount();
  37. CvContourScanner scanner = cvStartFindContours(img_temp, mem_storage);
  38. while (cvFindNextContour(scanner));
  39. first_contour = cvEndFindContours(&scanner);
  40. cvZero(img_temp);
  41. cvDrawContours(
  42. img_temp,
  43. first_contour,
  44. cvScalar(100),
  45. cvScalar(100),
  46. 1
  47. );
  48. t = (double)cvGetTickCount() - t;
  49. cvShowImage("contour2", img_temp);
  50. printf("run2 = %gms\n", t/(cvGetTickFrequency()*1000.));
  51. cvClearMemStorage(mem_storage);
  52. cvReleaseImage(&img);
  53. cvReleaseImage(&img_temp);
  54. cvWaitKey();
  55. /************************************************************************/
  56. /* 经测试 run1 = 16.1431ms run2 = 15.8677ms (参考)
  57. 不过可以肯定这两中算法时间复杂度是相同的                                     */
  58. /************************************************************************/
  59. //////////////////////////////////////////////////////////////////////////
  60. // 上述两种方法完成了对轮廓的提取,如想绘制轮廓都得配合cvDrawContours来使用
  61. // 而cvDrawContours 函数第5个参数为 max_level 经查ICVL含义如下:
  62. //
  63. // 绘制轮廓的最大等级。如果等级为0,绘制单独的轮廓。如果为1,绘制轮廓及在其后的相同的级别下轮廓。
  64. // 如果值为2,所有的轮廓。如果等级为2,绘制所有同级轮廓及所有低一级轮廓,诸此种种。如果值为负数,
  65. // 函数不绘制同级轮廓,但会升序绘制直到级别为abs(max_level)-1的子轮廓。
  66. //
  67. // 相信好多读者初次都无法理解等级的含义,而且测试时候输入>=1 的整数效果几乎一样
  68. // 只有提取轮廓时候的提取模式设为 CV_RETR_CCOMP CV_RETR_TREE 时这个参数才有意义
  69. //
  70. // 经查FindContours 函数里面这样介绍提取模式(mode)的这两个参数:
  71. // CV_RETR_CCOMP - 提取所有轮廓,并且将其组织为两层的 hierarchy: 顶层为连通域的外围边界,次层为洞的内层边界。
  72. // CV_RETR_TREE - 提取所有轮廓,并且重构嵌套轮廓的全部 hierarchy
  73. //
  74. // 下面用第一种方法进行测试
  75. cvNamedWindow("contour_test");
  76. cvNamedWindow("contour_raw");
  77. img = cvLoadImage("contour.jpg", CV_LOAD_IMAGE_GRAYSCALE);
  78. cvShowImage("contour_raw", img);
  79. cvThreshold(img, img, 128, 255, CV_THRESH_BINARY);
  80. img_temp = cvCloneImage(img);
  81. cvFindContours(
  82. img_temp,
  83. mem_storage,
  84. &first_contour,
  85. sizeof(CvContour),
  86. CV_RETR_CCOMP           //#1 需更改区域
  87. );
  88. cvZero(img_temp);
  89. cvDrawContours(
  90. img_temp,
  91. first_contour,
  92. cvScalar(100),
  93. cvScalar(100),
  94. 1                       //#2 需更改区域
  95. );
  96. cvShowImage("contour_test", img_temp);
  97. /************************************************************************/
  98. /* (1, 2) = (CV_RETR_CCOMP, 1)  如图1
  99. (1, 2) = (CV_RETR_CCOMP, 2)  如图2
  100. (1, 2) = (CV_RETR_TREE, 1)   如图3
  101. (1, 2) = (CV_RETR_TREE, 2)   如图4
  102. (1, 2) = (CV_RETR_TREE, 6)   如图5
  103. 经分析CV_RETR_CCOMP 只把图像分为两个层次,顶层和次层,一等级轮廓只匹配与其最接近
  104. 的内侧轮廓即2等级
  105. CV_RETR_TREE 则从轮廓外到内按等级1 - n 全部分配
  106. CV_RETR_LIST 全部轮廓均为1级                        */
  107. /************************************************************************/
  108. cvWaitKey();
  109. cvReleaseImage(&img);
  110. cvReleaseImage(&img_temp);
  111. cvReleaseMemStorage(&mem_storage);
  112. cvDestroyAllWindows();
  113. return 0;
  114. }

这是OpenCV的经典一个例子:

[cpp] view plain copy

  1. #include "cv.h"
  2. #include "cxcore.h"
  3. #include "highgui.h"
  4. #include <math.h>
  5. #endif
  6. #pragma   comment(lib,"cv.lib")
  7. #pragma   comment(lib,"highgui.lib")
  8. #pragma   comment(lib,"cxcore.lib")
  9. #define w 500
  10. int levels = 3;
  11. CvSeq* contours = 0;
  12. void on_trackbar(int pos)
  13. {
  14. IplImage* cnt_img = cvCreateImage( cvSize(w,w), 8, 3 );
  15. CvSeq* _contours = contours;
  16. int _levels = levels - 3;
  17. if( _levels <= 0 ) // get to the nearest face to make it look more funny
  18. _contours = _contours->h_next->h_next->h_next->h_next->h_next->h_next->h_next->v_next->h_next->h_next;
  19. //_contours = _contours->v_next;
  20. cvZero( cnt_img );
  21. cvDrawContours( cnt_img, _contours, CV_RGB(255,0,0), CV_RGB(0,255,0), _levels);//, 3, CV_AA, cvPoint(0,0) );
  22. /*_levels:
  23. 3,所有外轮廓及包含的内轮廓及里面的内轮廓
  24. 2:所有外轮廓及包含的内轮廓
  25. 1:所有外轮廓
  26. 0,第一个外轮廓
  27. -1:第一个外轮廓及包含的内轮廓
  28. -2:第一个外轮廓及包含的内轮廓及里面的内轮廓
  29. _contours->h_next:同级的下一个轮廓
  30. _contours->v_next父级下的下层区域;
  31. */
  32. cvShowImage( "contours", cnt_img );
  33. cvReleaseImage( &cnt_img );
  34. }
  35. int main( int argc, char** argv )
  36. {
  37. int i, j;
  38. CvMemStorage* storage = cvCreateMemStorage(0);
  39. IplImage* img = cvCreateImage( cvSize(w,w), 8, 1 );
  40. cvZero( img );
  41. for( i=0; i < 6; i++ )
  42. {
  43. int dx = (i%2)*250 - 30;//0%2=0;
  44. int dy = (i/2)*150;
  45. CvScalar white = cvRealScalar(255);
  46. CvScalar black = cvRealScalar(0);
  47. if( i == 0 )
  48. {
  49. for( j = 0; j <= 10; j++ )
  50. {
  51. double angle = (j+5)*CV_PI/21;
  52. cvLine(img, cvPoint(cvRound(dx+100+j*10-80*cos(angle)),
  53. cvRound(dy+100-90*sin(angle))),
  54. cvPoint(cvRound(dx+100+j*10-30*cos(angle)),
  55. cvRound(dy+100-30*sin(angle))), white, 1, 8, 0);
  56. }
  57. }
  58. cvEllipse( img, cvPoint(dx+150, dy+100), cvSize(100,70), 0, 0, 360, white, -1, 8, 0 );
  59. cvEllipse( img, cvPoint(dx+115, dy+70), cvSize(30,20), 0, 0, 360, black, -1, 8, 0 );
  60. cvEllipse( img, cvPoint(dx+185, dy+70), cvSize(30,20), 0, 0, 360, black, -1, 8, 0 );
  61. cvEllipse( img, cvPoint(dx+115, dy+70), cvSize(15,15), 0, 0, 360, white, -1, 8, 0 );
  62. cvEllipse( img, cvPoint(dx+185, dy+70), cvSize(15,15), 0, 0, 360, white, -1, 8, 0 );
  63. cvEllipse( img, cvPoint(dx+115, dy+70), cvSize(5,5), 0, 0, 360, black, -1, 8, 0 );
  64. cvEllipse( img, cvPoint(dx+185, dy+70), cvSize(5,5), 0, 0, 360, black, -1, 8, 0 );
  65. cvEllipse( img, cvPoint(dx+150, dy+100), cvSize(10,5), 0, 0, 360, black, -1, 8, 0 );
  66. cvEllipse( img, cvPoint(dx+150, dy+150), cvSize(40,10), 0, 0, 360, black, -1, 8, 0 );
  67. cvEllipse( img, cvPoint(dx+27, dy+100), cvSize(20,35), 0, 0, 360, white, -1, 8, 0 );
  68. cvEllipse( img, cvPoint(dx+273, dy+100), cvSize(20,35), 0, 0, 360, white, -1, 8, 0 );
  69. }
  70. cvNamedWindow( "image", 1 );
  71. cvShowImage( "image", img );
  72. cvFindContours( img, storage, &contours, sizeof(CvContour),
  73. 2, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
  74. // comment this out if you do not want approximation
  75. contours = cvApproxPoly( contours, sizeof(CvContour), storage, CV_POLY_APPROX_DP, 3, 1 );
  76. //cvApproxPoly:                                                 逼近方法     精度 逼近曲线是否封闭
  77. cvNamedWindow( "contours", 1 );
  78. cvCreateTrackbar( "levels+3", "contours", &levels, 7, on_trackbar );
  79. on_trackbar(0);
  80. cvWaitKey(0);
  81. cvReleaseMemStorage( &storage );
  82. cvReleaseImage( &img );
  83. return 0;
  84. }

主要还是理解下int mode=CV_RETR_LIST,int method=CV_CHAIN_APPROX_SIMPLE,CvPoint offset=cvPoint(0,0));

当mode 为CV_RETR_CCOMP 只把图像分为两个层次,顶层和次层,一等级轮廓只匹配与其最接近  ;

cvDrawContours 函数第5个参数为 max_level=0时,笑脸图像会显示第一个找到的轮廓,左边的白色耳朵一只;

max_level=1时,所有白色区域的轮廓都会被显示出来,因为他们都属于等级1;

max_level=2时;每个白色区域里面的黑色区域会被显示出来,可能一个白色区域下面有多个黑色区域,但他们都是同级的;

这里你要注意的的是每个白色区域下的黑色区域,如脸下面有4个黑色区域,白色眼珠下有一个黑色区域,这个黑色区域与脸下的那三个区域时同级的,也就是说他不属于脸的内区域,他是白色眼珠的内区域;

当mode为       CV_RETR_LIST 全部轮廓均为1级

时间: 2024-10-12 07:52:58

物体轮廓检测的相关文章

OPENCV图像轮廓检测

前面在图像转换的时候学到canny算子,可以检测出图像的轮廓信息,但是,该算子检测到的轮廓信息还需要我们手动的用眼睛去识别,而实际工程应用中,我们需要得到轮廓的具体数学信息,这就涉及到今天的主题,图像轮廓检测. 一.图像轮廓检测 在opencv中,轮廓对应着一系列的点的集合,opencv提供了一个函数,用来获得这些点的集合 API:void finContours(输入图像,输出轮廓点集,输出向量,int 轮廓检索模式,int 轮廓近似方法,Point 轮廓点的可选偏移量) 注:1.输入图像,是

3D轮廓检测技术讲解

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D实战核心技术详解>电子工业出版社等. CSDN视频网址:http://edu.csdn.net/lecturer/144 今天我们将讨论一种可以检测到3D物体的轮廓方式. 为了使事情变得更清楚,我指的是一个3D对象的轮廓,当光从任意方向落在它上面时. 移动光源可能会相应地改变轮廓. 这完全不同于在图像空

OpenCV入门笔记(六) 轮廓检测(Detect Contours)

轮廓(Contours),指的是有相同颜色或者密度,连接所有连续点的一条曲线.检测轮廓的工作对形状分析和物体检测与识别都非常有用. 在轮廓检测之前,首先要对图片进行二值化或者Canny边缘检测.在OpenCV中,寻找的物体是白色的,而背景必须是黑色的,因此图片预处理时必须保证这一点. cv2.findContours函数 Python版示例如下,也可以参考[OpenCV-Python教程(11.轮廓检测)][Contours : Getting Started] contours, hierar

视频中人体轮廓检测

传统的运动人体轮廓检测算法主要有三类:帧间差分法,背景减除法,光流法. 优点                                                                缺点 帧间差分法                运算简单.                                               对于缓慢运动的前景目标容易产生空洞现象. 背景减除法                简单易行.                        

[转载+原创]Emgu CV on C# (七) —— Emgu CV on 轮廓检测

轮廓检测 对于查找轮廓我们一般要对图像Canny检测.但是对于很特殊的场合其实我们还可以直接对二值化的图像进行轮廓的提取. 关键函数 1. cvFindContours Retrieves contours from the binary image and returns the number of retrieved contours. The pointer firstContour is filled by the function. It will contain pointer to

opengl学习-利用模板测试勾画物体轮廓中出现的一个问题

我在学习OpenGL模板测试勾画物体轮廓的时候,出现了这个问题: 这个出现的原因就是,改变摄像机的时候,每次绘制,上次绘制中模板缓冲区的数据没有清除的原因.也就是在while循环开始的时候,glClear(GL_STENCIL_BUFFER_BIT);没有起作用,原来 我在while的末尾忘记了修改回来模板缓冲区为可写.glStencilMask(0xff); 原文地址:https://www.cnblogs.com/pixs-union/p/8184366.html

【OpenCV入门指南】第六篇 轮廓检测 下

<OpenCV入门指南>系列文章地址:http://blog.csdn.net/morewindows/article/category/863841 上一篇<[OpenCV入门指南]第五篇轮廓检测上>介绍了cvFindContours函数和cvDrawContours函数,并作了一个简单的使用示范.本篇将展示一个实例,让大家对轮廓检测有个更加深入的认识. 代码如下: //图像的轮廓检测下 //By MoreWindows (http://blog.csdn.net/MoreWin

轮廓检测

图像轮廓 cv2.findContours(img,mode,method) mode :轮廓检测的模式 RETR_EXTERNAL :只检索最外面的轮廓: RETR_LIST:检索所有的轮廓,并将其保存到一条链表当中: RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界; RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次; method:轮廓逼近方法 CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方

OpenCV轮廓检测,计算物体旋转角度

效果还是有点问题的,希望大家共同探讨一下 // FindRotation-angle.cpp : 定义控制台应用程序的入口点. // // findContours.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> #include <vector> #include <opencv2/opencv.hpp> #include <opencv2/core/core.h