图像孔洞填充与小连通域的删除

cvFindContours

从二值图像中检索轮廓,并返回检测到的轮廓的个数。first_contour的值由函数填充返回,它的值将为第一个外轮廓的指针,当没有轮廓被检测到时为NULL。其它轮廓可以使用h_next和v_next连接,从first_contour到达。

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像素保存不变。从一个灰度图像得到二值图像的函数有:cvThreshold,cvAdaptiveThreshold和cvCanny。

storage

返回轮廓的容器。

first_contour

输出参数,用于存储指向第一个外接轮廓。

header_size

header序列的尺寸.如果选择method = CV_CHAIN_CODE, 则header_size >= sizeof(CvChain);其他,则

header_size >= sizeof(CvContour)。

mode

CV_RETR_EXTERNAL:只检索最外面的轮廓;

CV_RETR_LIST:检索所有的轮廓,并将其放入list中;

CV_RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界;

CV_RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次。

蓝色表示v_next,绿色表示h_next

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:使用the flavors of Teh-Chin chain近似算法

的一种。

CV_LINK_RUNS:通过连接水平段的1,使用完全不同的边缘提取算法。使用CV_RETR_LIST检索模式能使用此方法。

offset

偏移量,用于移动所有轮廓点。当轮廓是从图像的ROI提取的,并且需要在整个图像中分析时,这个参数将很有用。

讨论部分cvDrawContours中的案例显示了任何使用轮廓检测连通区域。轮廓可以用于形状分析和目标识别——可以参考文件夹OpenCV sample中的squares.c

cvDrawContours

cvDrawContours:在图像上绘制外部和内部轮廓

函数cvDrawContours用于在图像上绘制外部和内部轮廓。当thickness >= 0 时,绘制轮廓线;否则填充由轮廓包围的部分。

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,只绘制contour;如果是1,将绘制contour后和contour同层的所有

轮廓;如果是2,绘制contour后所有同层和低一层的轮廓,以此类推;如果值是负值,则函数并不

绘制contour后的轮廓,但是将画出其子轮廓,一直到abs(max_level) - 1层。

thickness

绘制轮廓线的宽度。如果为负值(例如,等于CV_FILLED),则contour内部将被绘制。

line_type

轮廓线段的类型,具体查看cvLine的描述。

offset

按给定值移动所有点的坐标。

cvContourArea

double cvContourArea( const CvArr* contour, CvSliceslice=CV_WHOLE_SEQ );
contour:轮廓(顶点的序列或数组)。
slice:感兴趣区轮廓部分的起点和终点,默认计算整个轮廓的面积。
函数cvContourArea计算整个或部分轮廓的面积。在计算部分轮廓的情况时,由轮廓弧线和连接两端点的弦
围成的区域总面积被计算

  1. #include <stdio.h>

  2. #include <cv.h>

  3. #include <cxcore.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); //创建一个内存存储器,为0时内存块默认大小为64k,申请一块内存来存储找到的轮廓序列

  20. cvFindContours(pBinary, pStorage, &pContour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE); //从二值图像中检索轮廓,并返回检测到的轮廓的个数

  21. // 源二值图、容器、第一个外轮廓指针、head序列大小、轮廓分两层、边缘只保留终点

  22. cvDrawContours(pBinary, pContour, CV_RGB(255, 255, 255), CV_RGB(255, 255, 255), 2, CV_FILLED, 8, cvPoint(0, 0)); //在图像上绘制外部和内部轮廓

  23. // 源二值图、第一个轮廓的指针、外轮廓的颜色、内轮廓的颜色、画两层轮廓、绘制轮廓线的宽度、轮廓线段的类型、按给定值移动所有点的坐标。

  24. // 外轮廓循环

  25. int wai = 0;

  26. int nei = 0;

  27. for (; pContour != NULL; pContour = pContour->h_next) //对检测到的轮廓进行逐一处理,h_next为下一个轮廓

  28. {

  29. wai++;

  30. // 内轮廓循环

  31. for (pConInner = pContour->v_next; pConInner != NULL; pConInner = pConInner->h_next)

  32. {

  33. nei++;

  34. dConArea = fabs(cvContourArea(pConInner, CV_WHOLE_SEQ));// 内轮廓面积

  35. printf("%f\n", dConArea);

  36. if (dConArea <= dAreaThre) //只处理满足条件的轮廓,不满足条件的轮廓扔掉

  37. {

  38. cvDrawContours(pBinary, pConInner, CV_RGB(255, 255, 255), CV_RGB(255, 255, 255), 0, CV_FILLED, 8, cvPoint(0, 0));

  39. } // 源二值图、第一个轮廓的指针、外轮廓的颜色、内轮廓的颜色、画第一个外轮廓、绘制轮廓线的宽度、轮廓线段的类型、按给定值移动所有点的坐标。

  40. }

  41. }

  42. printf("wai = %d, nei = %d", wai, nei);

  43. cvReleaseMemStorage(&pStorage);

  44. pStorage = NULL;

  45. }

  46. }

  47. int Otsu(IplImage* src)

  48. {

  49. int height=src->height;

  50. int width=src->width;

  51. //histogram

  52. float histogram[256] = {0};

  53. for(int i=0; i < height; i++)

  54. {

  55. unsigned char* p=(unsigned char*)src->imageData + src->widthStep * i;

  56. for(int j = 0; j < width; j++)

  57. {

  58. histogram[*p++]++;

  59. }

  60. }

  61. //normalize histogram

  62. int size = height * width;

  63. for(int i = 0; i < 256; i++)

  64. {

  65. histogram[i] = histogram[i] / size;

  66. }

  67. //average pixel value

  68. float avgValue=0;

  69. for(int i=0; i < 256; i++)

  70. {

  71. avgValue += i * histogram[i]; //整幅图像的平均灰度

  72. }

  73. int threshold;

  74. float maxVariance=0;

  75. float w = 0, u = 0;

  76. for(int i = 0; i < 256; i++)

  77. {

  78. w += histogram[i]; //假设当前灰度i为阈值, 0~i 灰度的像素(假设像素值在此范围的像素叫做前景像素) 所占整幅图像的比例

  79. u += i * histogram[i]; // 灰度i 之前的像素(0~i)的平均灰度值: 前景像素的平均灰度值

  80. float t = avgValue * w - u;

  81. float variance = t * t / (w * (1 - w) );

  82. if(variance > maxVariance)

  83. {

  84. maxVariance = variance;

  85. threshold = i;

  86. }

  87. }

  88. return threshold;

  89. }

  90. void DeleteIslet(IplImage* pic1,IplImage** pic0)

  91. {

  92. /************删除二值化图像中面积较小的连通域****************************/

  93. CvSeq* contour = NULL;

  94. double minarea =300.0;

  95. double tmparea = 0.0;

  96. CvMemStorage* storage = cvCreateMemStorage(0);

  97. IplImage* img_Clone=cvCloneImage(pic1);

  98. //访问二值图像每个点的值

  99. uchar *pp;

  100. IplImage* img_dst = cvCreateImage(cvGetSize(pic1),IPL_DEPTH_8U,1);

  101. //------------搜索二值图中的轮廓,并从轮廓树中删除面积小于某个阈值minarea的轮廓-------------//

  102. CvScalar color = cvScalar(255,0,0);//CV_RGB(128,0,0);

  103. CvContourScanner scanner = NULL;

  104. scanner = cvStartFindContours(pic1,storage,sizeof(CvContour),CV_RETR_CCOMP,CV_CHAIN_APPROX_NONE,cvPoint(0,0));

  105. //开始遍历轮廓树

  106. CvRect rect;

  107. while (contour==cvFindNextContour(scanner))

  108. {

  109. tmparea = fabs(cvContourArea(contour));

  110. rect = cvBoundingRect(contour,0);

  111. if (tmparea < minarea/*||tmparea>4900*/)

  112. {

  113. //当连通域的中心点为黑色时,而且面积较小则用白色进行填充

  114. pp=(uchar*)(img_Clone->imageData + img_Clone->widthStep*(rect.y+rect.height/2)+rect.x+rect.width/2);

  115. if (pp[0]==0)

  116. {

  117. for(int y = rect.y;y<rect.y+rect.height;y++)

  118. {

  119. for(int x =rect.x;x<rect.x+rect.width;x++)

  120. {

  121. pp=(uchar*)(img_Clone->imageData + img_Clone->widthStep*y+x);

  122. if (pp[0]==0)

  123. {

  124. pp[0]=255;

  125. }

  126. }

  127. }

  128. }

  129. }

  130. }

  131. *pic0=img_Clone;

  132. }

  133. int main( )

  134. {

  135. IplImage* oringin=cvLoadImage("test.bmp",1);

  136. IplImage* pic1=cvLoadImage("test.bmp",0);

  137. IplImage* pic2=NULL;

  138. DeleteIslet(pic1,&pic2); //删除小联通域

  139. IplImage* pic3=cvCloneImage(pic2); //取反再删除

  140. cvNot(pic2,pic3);

  141. IplImage* pic4=NULL;

  142. DeleteIslet(pic3,&pic4);

  143. /**************孔洞填充*********************/

  144. IplImage* pic5 = cvCreateImage(cvGetSize(pic4), 8, 1);

  145. int thresh = Otsu(pic4);

  146. cvThreshold(pic4,pic5, thresh, 255, CV_THRESH_BINARY);

  147. FillInternalContours(pic5, 200);

  148. IplImage* pic6 = cvCreateImage(cvGetSize(pic5), 8, 1);

  149. cvNot(pic5,pic6);

  150. //cvSaveImage("out2.bmp",pic6);

  151. cvNamedWindow("oringin");

  152. cvShowImage("oringin", oringin);

  153. cvNamedWindow("删除小联通域");

  154. cvShowImage("删除小联通域", pic2);

  155. cvNamedWindow("孔洞填充");

  156. cvShowImage("孔洞填充", pic6);

  157. cvWaitKey(0);

  158. cvDestroyWindow( "oringin" );

  159. cvDestroyWindow( "删除小联通域" );

  160. cvDestroyWindow( "孔洞填充" );

  161. cvReleaseImage(&oringin);

  162. cvReleaseImage(&pic2);

  163. cvReleaseImage(&pic6);

  164. return 0;

  165. }

图像孔洞填充与小连通域的删除相关推荐

  1. opencv-python图像孔洞填充

      在进行图像分割的过程中,由于算法的不稳定或者图像质量的问题,会造成图像孔洞出现,这个时候就需要对图像中的孔洞进行填充,具体函数如下 def fillHole(im_in):im_floodfill ...

  2. matlab灰度图孔洞填充,一种深度图像空洞的自动填充方法与流程

    本发明涉及深度图像空洞填充技术,从彩色图像的结构相似性出发,求解空洞像素点的多尺度结构相似性,将空洞像素分为平滑区域空洞像素和非平滑区域空洞像素,并针对不同的空洞像素,采用不同的填充算法.在平滑区域空 ...

  3. python skimage 填补图像孔洞

    最近处理图像的时候需要将图像中一些较小的黑色空洞填上,大致如下: 白色区域中的黑色点是需要填上的孔洞. cv2中有一个叫cv2.floodFill的函数,可以淹没孔洞.但这个方法有两个很大的缺点. 第 ...

  4. C++ Opencv imfill 孔洞填充函数的实现(学习笔记)

    C++ Opencv imfill 孔洞填充函数的实现 敬告 函数实现的中心思想 二值图 寻找连通域的关键 种子点的确定 连通域的寻找过程 条件设定 最后赋值 话不多说 直接上函数代码 主函数代码 代 ...

  5. OTSU阈值分割+孔洞填充+海陆分离

    1. OTSU阈值分割 参考1. https://zhuanlan.zhihu.com/p/124944108 OTSU算法(大津算法)的详细步骤: 假设初始有个阈值 T 0 T_0 T0​,并将图像 ...

  6. 数字图像处理(作业三)——孔洞填充+全局阈值+自适应阈值

    一.孔洞填充 基本思想: 基于形态学算法,膨胀后与上取反的原图 算法实现步骤: 1.首先找出所有孔洞的位置,只需知道洞中的一个点的坐标即可,下面直接以改点代替该洞 2.新建一张全零图,用0表示背景,1 ...

  7. 形态学重建之孔洞填充

    白菜苗 1.什么是膨胀(如果已经了解,请往下看) 2.什么是孔洞填充(如果已经了解,请往下看) 3.什么是形态学重建(如果已经了解,请往下看) 4.什么是测地膨胀(如果已经了解,请往下看) 5.什么是 ...

  8. OpenCV函数应用:基于二值图像的三种孔洞填充方法记录(附python,C++代码)

    系列文章目录 函数系列: OpenCV函数简记_第一章数字图像的基本概念(邻域,连通,色彩空间) OpenCV函数简记_第二章数字图像的基本操作(图像读写,图像像素获取,图像ROI获取,图像混合,图形 ...

  9. 形态学重建:孔洞填充的python实现

    主要参考:(美)拉斐尔·C.冈萨雷斯. 数字图像处理 第3版[M]. 阮秋琦,译. 北京:电子工业出版社, 2017: 633. 形态学重建 形态学重建涉及两幅图像和一个结构元: Marker 图像: ...

最新文章

  1. linux虚拟机文件挂载
  2. 图论讲解(3)——最小生成树(基础)
  3. log4j记录不同的日志_使用log4j将不同类型的日志信息记录到不同的文件中
  4. xshell常用的命令
  5. LeetCode 632. 最小区间(排序+滑动窗口)
  6. 运行opencv保存视频时出现错误的解决方法
  7. 二维数组七行七列C语言,C语言中级教程 再谈数组-7.ppt
  8. [asp.netMVC]通过configSource提高web.config配置灵活性
  9. Java中如何跳出多重for循环
  10. SpringMVC:学习笔记(1)——理解MVC及快速入门
  11. 开发时多选操作的实现
  12. SpringBoot2.0学习第四篇之拦截器过滤器配置
  13. c语言for循环计算100以内奇数的和
  14. 怎样培养数据分析的能力
  15. Typora修改空格样式(blockquote)
  16. 缩减50%调试成本  小匠物联推可远程的串口调试助手
  17. Virtual ChIP-seq: predicting transcription factor binding by learning from the transcriptome
  18. 苹果微信多开_史上最全微信双开 全平台【IOS/安卓/WIN】
  19. 大疆工程师:如何利用6年时间成为一名优秀的机器人工程师
  20. 记录IDEA导包不能导入的问题

热门文章

  1. Android中的帧布局
  2. java 线程包_Java 多线程——工具包
  3. spring 发送html邮件,Springboot2.0 发送HTML 格式的邮件。
  4. 使用隐式Intent打开系统浏览器的百度网页
  5. centerpoint 项目
  6. C++ this指针详解(精辟)
  7. bigquery sql 正则表达式
  8. c++   string类
  9. spark计算TF-IDF值(中文)
  10. 在单链表和双链表中删除倒数第K个节点