本文主要是介绍图像孔洞填充与小连通域的删除,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
图像孔洞填充与小连通域的删除
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计算整个或部分轮廓的面积。在计算部分轮廓的情况时,由轮廓弧线和连接两端点的弦
围成的区域总面积被计算
-
#include <stdio.h>
-
#include <cv.h>
-
#include <cxcore.h>
-
#include <highgui.h>
-
// 内轮廓填充
-
// 参数:
-
// 1. pBinary: 输入二值图像,单通道,位深IPL_DEPTH_8U。
-
// 2. dAreaThre: 面积阈值,当内轮廓面积小于等于dAreaThre时,进行填充。
-
void FillInternalContours(IplImage *pBinary, double dAreaThre)
-
{
-
double dConArea;
-
CvSeq *pContour = NULL; //创建一序列
-
CvSeq *pConInner = NULL;
-
CvMemStorage *pStorage = NULL; //用来创建一个内存存储器,来统一管理各种动态对象的内存,比如说序列,这个函数返回一个新创建的内存存储器指针。
-
// 执行条件
-
if (pBinary)
-
{
-
// 查找所有轮廓
-
pStorage = cvCreateMemStorage(0); //创建一个内存存储器,为0时内存块默认大小为64k,申请一块内存来存储找到的轮廓序列
-
cvFindContours(pBinary, pStorage, &pContour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE); //从二值图像中检索轮廓,并返回检测到的轮廓的个数
-
// 源二值图、容器、第一个外轮廓指针、head序列大小、轮廓分两层、边缘只保留终点
-
cvDrawContours(pBinary, pContour, CV_RGB(255, 255, 255), CV_RGB(255, 255, 255), 2, CV_FILLED, 8, cvPoint(0, 0)); //在图像上绘制外部和内部轮廓
-
// 源二值图、第一个轮廓的指针、外轮廓的颜色、内轮廓的颜色、画两层轮廓、绘制轮廓线的宽度、轮廓线段的类型、按给定值移动所有点的坐标。
-
// 外轮廓循环
-
int wai = 0;
-
int nei = 0;
-
for (; pContour != NULL; pContour = pContour->h_next) //对检测到的轮廓进行逐一处理,h_next为下一个轮廓
-
{
-
wai++;
-
// 内轮廓循环
-
for (pConInner = pContour->v_next; pConInner != NULL; pConInner = pConInner->h_next)
-
{
-
nei++;
-
dConArea = fabs(cvContourArea(pConInner, CV_WHOLE_SEQ));// 内轮廓面积
-
printf("%f\n", dConArea);
-
if (dConArea <= dAreaThre) //只处理满足条件的轮廓,不满足条件的轮廓扔掉
-
{
-
cvDrawContours(pBinary, pConInner, CV_RGB(255, 255, 255), CV_RGB(255, 255, 255), 0, CV_FILLED, 8, cvPoint(0, 0));
-
} // 源二值图、第一个轮廓的指针、外轮廓的颜色、内轮廓的颜色、画第一个外轮廓、绘制轮廓线的宽度、轮廓线段的类型、按给定值移动所有点的坐标。
-
}
-
}
-
printf("wai = %d, nei = %d", wai, nei);
-
cvReleaseMemStorage(&pStorage);
-
pStorage = NULL;
-
}
-
}
-
int Otsu(IplImage* src)
-
{
-
int height=src->height;
-
int width=src->width;
-
//histogram
-
float histogram[256] = {0};
-
for(int i=0; i < height; i++)
-
{
-
unsigned char* p=(unsigned char*)src->imageData + src->widthStep * i;
-
for(int j = 0; j < width; j++)
-
{
-
histogram[*p++]++;
-
}
-
}
-
//normalize histogram
-
int size = height * width;
-
for(int i = 0; i < 256; i++)
-
{
-
histogram[i] = histogram[i] / size;
-
}
-
//average pixel value
-
float avgValue=0;
-
for(int i=0; i < 256; i++)
-
{
-
avgValue += i * histogram[i]; //整幅图像的平均灰度
-
}
-
int threshold;
-
float maxVariance=0;
-
float w = 0, u = 0;
-
for(int i = 0; i < 256; i++)
-
{
-
w += histogram[i]; //假设当前灰度i为阈值, 0~i 灰度的像素(假设像素值在此范围的像素叫做前景像素) 所占整幅图像的比例
-
u += i * histogram[i]; // 灰度i 之前的像素(0~i)的平均灰度值: 前景像素的平均灰度值
-
float t = avgValue * w - u;
-
float variance = t * t / (w * (1 - w) );
-
if(variance > maxVariance)
-
{
-
maxVariance = variance;
-
threshold = i;
-
}
-
}
-
return threshold;
-
}
-
void DeleteIslet(IplImage* pic1,IplImage** pic0)
-
{
-
/************删除二值化图像中面积较小的连通域****************************/
-
CvSeq* contour = NULL;
-
double minarea =300.0;
-
double tmparea = 0.0;
-
CvMemStorage* storage = cvCreateMemStorage(0);
-
IplImage* img_Clone=cvCloneImage(pic1);
-
//访问二值图像每个点的值
-
uchar *pp;
-
IplImage* img_dst = cvCreateImage(cvGetSize(pic1),IPL_DEPTH_8U,1);
-
//------------搜索二值图中的轮廓,并从轮廓树中删除面积小于某个阈值minarea的轮廓-------------//
-
CvScalar color = cvScalar(255,0,0);//CV_RGB(128,0,0);
-
CvContourScanner scanner = NULL;
-
scanner = cvStartFindContours(pic1,storage,sizeof(CvContour),CV_RETR_CCOMP,CV_CHAIN_APPROX_NONE,cvPoint(0,0));
-
//开始遍历轮廓树
-
CvRect rect;
-
while (contour==cvFindNextContour(scanner))
-
{
-
tmparea = fabs(cvContourArea(contour));
-
rect = cvBoundingRect(contour,0);
-
if (tmparea < minarea/*||tmparea>4900*/)
-
{
-
//当连通域的中心点为黑色时,而且面积较小则用白色进行填充
-
pp=(uchar*)(img_Clone->imageData + img_Clone->widthStep*(rect.y+rect.height/2)+rect.x+rect.width/2);
-
if (pp[0]==0)
-
{
-
for(int y = rect.y;y<rect.y+rect.height;y++)
-
{
-
for(int x =rect.x;x<rect.x+rect.width;x++)
-
{
-
pp=(uchar*)(img_Clone->imageData + img_Clone->widthStep*y+x);
-
if (pp[0]==0)
-
{
-
pp[0]=255;
-
}
-
}
-
}
-
}
-
}
-
}
-
*pic0=img_Clone;
-
}
-
int main( )
-
{
-
IplImage* oringin=cvLoadImage("test.bmp",1);
-
IplImage* pic1=cvLoadImage("test.bmp",0);
-
IplImage* pic2=NULL;
-
DeleteIslet(pic1,&pic2); //删除小联通域
-
IplImage* pic3=cvCloneImage(pic2); //取反再删除
-
cvNot(pic2,pic3);
-
IplImage* pic4=NULL;
-
DeleteIslet(pic3,&pic4);
-
/**************孔洞填充*********************/
-
IplImage* pic5 = cvCreateImage(cvGetSize(pic4), 8, 1);
-
int thresh = Otsu(pic4);
-
cvThreshold(pic4,pic5, thresh, 255, CV_THRESH_BINARY);
-
FillInternalContours(pic5, 200);
-
IplImage* pic6 = cvCreateImage(cvGetSize(pic5), 8, 1);
-
cvNot(pic5,pic6);
-
//cvSaveImage("out2.bmp",pic6);
-
cvNamedWindow("oringin");
-
cvShowImage("oringin", oringin);
-
cvNamedWindow("删除小联通域");
-
cvShowImage("删除小联通域", pic2);
-
cvNamedWindow("孔洞填充");
-
cvShowImage("孔洞填充", pic6);
-
cvWaitKey(0);
-
cvDestroyWindow( "oringin" );
-
cvDestroyWindow( "删除小联通域" );
-
cvDestroyWindow( "孔洞填充" );
-
cvReleaseImage(&oringin);
-
cvReleaseImage(&pic2);
-
cvReleaseImage(&pic6);
-
return 0;
-
}
这篇关于图像孔洞填充与小连通域的删除的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!