http://blog.csdn.net/timidsmile/article/details/8519751

环境: vs2008 + opencv2.1

先看,这两个函数的用法(参考 opencv手册):

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

FindContours        在二值图像中寻找轮廓 

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) );

image 
输入的 8-比特、单通道图像. 非零元素被当成 1, 0 象素值保留为 0 - 从而图像被看成二值的。为了从灰度图像中得到这样的二值图像,可以使用 cvThreshold, cvAdaptiveThreshold 或 cvCanny. 本函数改变输入图像内容。

storage 
得到的轮廓的存储容器

first_contour 
输出参数:包含第一个输出轮廓的指针

header_size 
如果 method=CV_CHAIN_CODE,则序列头的大小 >=sizeof(CvChain),否则 >=sizeof(CvContour) .

mode 
提取模式. 
CV_RETR_EXTERNAL - 只提取最外层的轮廓 
CV_RETR_LIST - 提取所有轮廓,并且放置在 list 中 
CV_RETR_CCOMP - 提取所有轮廓,并且将其组织为两层的 hierarchy: 顶层为连通域的外围边界,次层为洞的内层边界。 
CV_RETR_TREE - 提取所有轮廓,并且重构嵌套轮廓的全部 hierarchy

method 
逼近方法 (对所有节点, 不包括使用内部逼近的 CV_RETR_RUNS). 
CV_CHAIN_CODE - Freeman 链码的输出轮廓. 其它方法输出多边形(定点序列). 
CV_CHAIN_APPROX_NONE - 将所有点由链码形式翻译(转化)为点序列形式 
CV_CHAIN_APPROX_SIMPLE - 压缩水平、垂直和对角分割,即函数只保留末端的象素点; 
CV_CHAIN_APPROX_TC89_L1, 
CV_CHAIN_APPROX_TC89_KCOS - 应用 Teh-Chin 链逼近算法. CV_LINK_RUNS - 通过连接为 1 的水平碎片使用完全不同的轮廓提取算法。仅有 CV_RETR_LIST 提取模式可以在本方法中应用.

offset 
每一个轮廓点的偏移量. 当轮廓是从图像 ROI 中提取出来的时候,使用偏移量有用,因为可以从整个图像上下文来对轮廓做分析. 
函数 cvFindContours 从二值图像中提取轮廓,并且返回提取轮廓的数目。指针 first_contour 的内容由函数填写。它包含第一个最外层轮廓的指针,如果指针为 NULL,则没有检测到轮廓(比如图像是全黑的)。其它轮廓可以从 first_contour 利用 h_next 和 v_next 链接访问到。 在 cvDrawContours 的样例显示如何使用轮廓来进行连通域的检测。轮廓也可以用来做形状分析和对象识别 - 见CVPR2001 教程中的 squares 样例。该教程可以在 SourceForge 网站上找到。

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

DrawContours    在图像中绘制外部和内部的轮廓。 

void cvDrawContours( CvArr *img, CvSeq* contour,
                     CvScalar external_color, CvScalar hole_color,
                     int max_level, int thickness=1,
                     int line_type=8, CvPoint offset=cvPoint(0,0) );
img 
用以绘制轮廓的图像。和其他绘图函数一样,边界图像被感兴趣区域(ROI)所剪切。

contour  指针指向第一个轮廓。

external_color   外层轮廓的颜色。

hole_color   内层轮廓的颜色。

max_level   绘制轮廓的最大等级。如果等级为0,绘制单独的轮廓。如果为1,绘制轮廓及在其后的相同的级别下轮廓。如果值为2,所有的轮廓。如果等级为2,绘制所有同级轮廓及所有低一级轮廓,诸此种种。如果值为负数,函数不绘制同级轮廓,但会升序绘制直到级别为abs(max_level)-1的子轮廓。

thickness   绘制轮廓时所使用的线条的粗细度。如果值为负(e.g. =CV_FILLED),绘制内层轮廓。

line_type   线条的类型。参考cvLine.

offset  照给出的偏移量移动每一个轮廓点坐标.当轮廓是从某些感兴趣区域(ROI)中提取的然后需要在运算中考虑ROI偏移量时,将会用到这个参数。

当thickness>=0,函数cvDrawContours在图像中绘制轮廓,或者当thickness<0时,填充轮廓所限制的区域。

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

code1 :

  1. #include "stdafx.h"
  2. #include "cxcore.h"
  3. #include "cv.h"
  4. #include "highgui.h"
  5. // 内轮廓填充
  6. // 参数:
  7. // 1. pBinary: 输入二值图像,单通道,位深IPL_DEPTH_8U。
  8. // 2. dAreaThre: 面积阈值,当内轮廓面积小于等于dAreaThre时,进行填充。
  9. void FillInternalContours(IplImage *pBinary, double dAreaThre)
  10. {
  11. double dConArea;
  12. CvSeq *pContour = NULL;
  13. CvSeq *pConInner = NULL;
  14. CvMemStorage *pStorage = NULL;
  15. // 执行条件
  16. if (pBinary)
  17. {
  18. // 查找所有轮廓
  19. pStorage = cvCreateMemStorage(0);
  20. cvFindContours(pBinary, pStorage, &pContour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
  21. // 填充所有轮廓
  22. cvDrawContours(pBinary, pContour, CV_RGB(255, 255, 255), CV_RGB(255, 255, 255), 2, CV_FILLED, 8, cvPoint(0, 0));
  23. // 外轮廓循环
  24. int wai = 0;
  25. int nei = 0;
  26. for (; pContour != NULL; pContour = pContour->h_next)
  27. {
  28. wai++;
  29. // 内轮廓循环
  30. for (pConInner = pContour->v_next; pConInner != NULL; pConInner = pConInner->h_next)
  31. {
  32. nei++;
  33. // 内轮廓面积
  34. dConArea = fabs(cvContourArea(pConInner, CV_WHOLE_SEQ));
  35. printf("%f\n", dConArea);
  36. }
  37. CvRect rect = cvBoundingRect(pContour,0);
  38. cvRectangle(pBinary, cvPoint(rect.x, rect.y), cvPoint(rect.x + rect.width, rect.y + rect.height),CV_RGB(255,255, 255), 1, 8, 0);
  39. }
  40. printf("wai = %d, nei = %d", wai, nei);
  41. cvReleaseMemStorage(&pStorage);
  42. pStorage = NULL;
  43. }
  44. }
  45. int Otsu(IplImage* src)
  46. {
  47. int height=src->height;
  48. int width=src->width;
  49. //histogram
  50. float histogram[256] = {0};
  51. for(int i=0; i < height; i++)
  52. {
  53. unsigned char* p=(unsigned char*)src->imageData + src->widthStep * i;
  54. for(int j = 0; j < width; j++)
  55. {
  56. histogram[*p++]++;
  57. }
  58. }
  59. //normalize histogram
  60. int size = height * width;
  61. for(int i = 0; i < 256; i++)
  62. {
  63. histogram[i] = histogram[i] / size;
  64. }
  65. //average pixel value
  66. float avgValue=0;
  67. for(int i=0; i < 256; i++)
  68. {
  69. avgValue += i * histogram[i];  //整幅图像的平均灰度
  70. }
  71. int threshold;
  72. float maxVariance=0;
  73. float w = 0, u = 0;
  74. for(int i = 0; i < 256; i++)
  75. {
  76. w += histogram[i];  //假设当前灰度i为阈值, 0~i 灰度的像素(假设像素值在此范围的像素叫做前景像素) 所占整幅图像的比例
  77. u += i * histogram[i];  // 灰度i 之前的像素(0~i)的平均灰度值: 前景像素的平均灰度值
  78. float t = avgValue * w - u;
  79. float variance = t * t / (w * (1 - w) );
  80. if(variance > maxVariance)
  81. {
  82. maxVariance = variance;
  83. threshold = i;
  84. }
  85. }
  86. return threshold;
  87. }
  88. int main()
  89. {
  90. IplImage *img = cvLoadImage("c://temp.jpg", 0);
  91. IplImage *bin = cvCreateImage(cvGetSize(img), 8, 1);
  92. int thresh = Otsu(img);
  93. cvThreshold(img, bin, thresh, 255, CV_THRESH_BINARY);
  94. FillInternalContours(bin, 200);
  95. cvNamedWindow("img");
  96. cvShowImage("img", img);
  97. cvNamedWindow("result");
  98. cvShowImage("result", bin);
  99. cvWaitKey(-1);
  100. cvReleaseImage(&img);
  101. cvReleaseImage(&bin);
  102. return 0;
  103. }

result1:

这种情况下,大月亮内部的两个内轮廓没有框出来。这个不是因为我的 rect框是 白色的缘故。。。。应该。

我断点试了,就 cvRectangle 了 4次···

code2:

  1. // test.cpp : 定义控制台应用程序的入口点。
  2. //
  3. #include "stdafx.h"
  4. #include "stdio.h"
  5. #include "cv.h"
  6. #include "highgui.h"
  7. #include "Math.h"
  8. int _tmain(int argc, _TCHAR* argv[])
  9. {
  10. IplImage *src = cvLoadImage("c:\\temp.jpg", 0);
  11. IplImage *dsw = cvCreateImage(cvGetSize(src), 8, 1);
  12. IplImage *dst = cvCreateImage(cvGetSize(src), 8, 3);
  13. CvMemStorage *storage = cvCreateMemStorage(0);
  14. CvSeq *first_contour = NULL;
  15. //turn the src image to a binary image
  16. //cvThreshold(src, dsw, 125, 255, CV_THRESH_BINARY_INV);
  17. cvThreshold(src, dsw, 100, 255, CV_THRESH_BINARY);
  18. cvFindContours(dsw, storage, &first_contour, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
  19. cvZero(dst);
  20. int cnt = 0;
  21. for(; first_contour != 0; first_contour = first_contour->h_next)
  22. {
  23. cnt++;
  24. CvScalar color = CV_RGB(rand()&255, rand()&255, rand()&255);
  25. cvDrawContours(dst, first_contour, color, color, 0, 2, CV_FILLED, cvPoint(0, 0));
  26. CvRect rect = cvBoundingRect(first_contour,0);
  27. cvRectangle(dst, cvPoint(rect.x, rect.y), cvPoint(rect.x + rect.width, rect.y + rect.height),CV_RGB(255, 0, 0), 1, 8, 0);
  28. }
  29. printf("the num of contours : %d\n", cnt);
  30. cvNamedWindow( "Source", 1 );
  31. cvShowImage( "Source", src );
  32. cvNamedWindow( "dsw", 1 );
  33. cvShowImage( "dsw", dsw );
  34. cvNamedWindow( "Components", 1 );
  35. cvShowImage( "Components", dst );
  36. cvReleaseMemStorage(&storage);
  37. cvWaitKey(-1);
  38. return 0;
  39. }

resul2:

这种情况下 内轮廓也框出来了。。。。。

=======================================

看来阈值的选择与想要的结果有很大关系哦。

如何适应不同的图片呢?????????????????

=======================================

还有,每幅图片里面,最大的轮廓是整幅图像,可以根据其面积最大,去除 哦~~~修改如下:

area = fabs(cvContourArea(first_contour, CV_WHOLE_SEQ)); //cal the hole's area

++++++++++++++++++++++++++++++++

ps:

在写后面那个 内轮廓填充的时候,才发现, dsw 是我二值化之后的图像,很明显不应该是这样子的呀。

我把 关于 Contours 的函数删除之后 又 恢复正常了。不知道为嘛呢。 很显然查出来的轮廓是 正确二值化之后的吧。 不知道为嘛会这样显示呢。

+++++++++++++++++++++++++++++==

ps:

再看另一个图的结果:

总有 9 个轮廓。

另外,计算了下,每个大轮廓内部的 小轮廓的数目 conner ,结果显示都为0.

看看第一个大五角星。 应该是把 边边作为了一个轮廓, 把 内部 黑色区域作为一个 轮廓 了吧????

还有,这幅图片 没有被当做一个大轮廓,上面那个小猫的,整幅图片被框了一下啊。。。。。。。。。。。。

另外i, 把 关于 cvFindContours && cvDrawContours 两个函数部分删除,二值化结果如下:

opencv查找轮廓---cvFindContours && cvDrawCountours 用法及例子的更多相关文章

  1. OpenCV 查找轮廓

    本文将结合实例代码,介绍 OpenCV 如何查找轮廓.获取边界框. 代码: contours.py OpenCV 提供了 findContours 函数查找轮廓,需要以二值化图像作为输入.并指定些选项 ...

  2. 学习opencv跟轮廓相关的

    查找轮廓 轮廓到底是什么?一个轮廓一般对应一系列的点,也就是图像中的一条曲线.表示的方法可能根据不同情况而有所不同.有多重方法可以表示曲线.在openCV中一般用序列来存储轮廓信息.序列中的每一个元素 ...

  3. OpenCV 矩形轮廓检测

    转载请注明出处:http://blog.csdn.net/wangyaninglm/article/details/44151213, 来自:shiter编写程序的艺术 基础介绍 OpenCV里提取目 ...

  4. 使用OpenCV查找二值图中最大连通区域

    http://blog.csdn.net/shaoxiaohu1/article/details/40272875 使用OpenCV查找二值图中最大连通区域 标签: OpenCVfindCoutour ...

  5. 查找轮廓(cv2.findCountours函数)

    1.输入为二值图像,黑色为背景,白色为目标 2.该函数会修改原图像,因此若想保留原图像在,则需拷贝一份,在拷贝图里修改. 一.查找轮廓 cv2.findContours() 三个输入参数:输入图像(二 ...

  6. 【python+opencv】轮廓发现

    python+opencv---轮廓发现 轮廓发现---是基于图像边缘提取的基础寻找对象轮廓的方法, 所有边缘提取的阈值选定会影响最终轮廓发现的结果. 介绍两种API使用: -cv.findConto ...

  7. OpenCV—Python 轮廓检测 绘出矩形框(findContours\ boundingRect\rectangle

    千万注意opencv的轮廓检测和边缘检测是两码事 本文链接:https://blog.csdn.net/wsp_1138886114/article/details/82945328 1 获取轮廓 O ...

  8. OpenCV中Mat的基本用法:创建、复制

    OpenCV中Mat的基本用法:创建.复制 一.Mat类的创建: 1.方法一: 通过读入一张图像,直接将其转换成Mat对象. Mat image = imread("test.jpg&quo ...

  9. OpenCV——查找、绘制轮廓

    #include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace st ...

随机推荐

  1. halcon学习相关资料(转载)

    https://blog.csdn.net/maweifei/article/details/78162581 论坛.培训 halcon学习网:http://www.ihalcon.com/ 鸟叔机器 ...

  2. 《图解 HTTP 》阅读 —— 第二章

    第2章 简单的http协议 http 协议用于客户端和服务器端的通信. 请求访问文本或图像等资源的一端称为客户端,提供资源响应的一端称为服务器端. 请求报文: 响应报文: 为了能够处理大量的事务,ht ...

  3. 使用Idea工具创建Maven WebApp项目

    (1)New Project,选择模板,配置SDK (2)配置项目名及项目组名 GroupID是项目组织唯一的标识符, 比如我的项目叫test001 那么GroupID应该是 com.lixiaomi ...

  4. how to update product listing price sale price and sale date using mobile App

    Greetings from Amazon Seller Support, Thank you for writing back to us. I have reviewed our previous ...

  5. ie6下,莫名被复制出一段文字解决

    在IE6下使用浮动可能会出现文字重复的情况. 在IE6下,浮动层之间有注释文字的话,之前那个浮动层的内容文字就有可能遭遇一个“隐形”的复制,但是代码里查看文字可并没有多出来. 看个例子: HTML & ...

  6. java不用任何已有方法完全自写的去重法

    package aa; class InsertSort{ private long[] a; private int nElems; //构造方法 public InsertSort(int max ...

  7. 2017年度网络安全服务企业TOP50

    何谓“大安全”? 近几年来,网络安全和信息安全领域不时出现引发社会各界关注的事件. 2014年,政府采购计划对WIN8说“不”,同年,中央网络安全和信息化领导小组成立,将网络安全上升到了国家战略高度, ...

  8. 在PHP中,是以分好结束一条语句的吗

    在PHP中,是以分号结束一条语句的,这个和C语言类似. 但是,有一条例外,对于PHP结束tag之前的语句,是可以不写分号的: <?php if ($a == $b) { echo "R ...

  9. 软工实践-Alpha 冲刺 (9/10)

    队名:起床一起肝活队 组长博客:博客链接 作业博客:班级博客本次作业的链接 组员情况 组员1(队长):白晨曦 过去两天完成了哪些任务 描述: 已经解决登录注册等基本功能的界面. 完成非功能的主界面制作 ...

  10. 软工网络15团队作业4——Alpha阶段敏捷冲刺-7

    一.当天站立式会议照片: 二.项目进展 昨天已完成的工作: 进一步优化功能与完善服务器. 明天计划完成的工作: 服务器是需要完善,后端的配置还需要修改. 工作中遇到的困难: 今日遇到的困难是服务器后端 ...