图像内轮廓填充通常称为孔洞填充,主要用于目标提取。不带任何条件的内轮廓填充,在目标密度很大时,可能导致错误填充。一种典型情况,当多个目标粘连,并 且形成环状时,简单的内轮廓填充会将环状内部背景部分误认为目标空洞进行错误填充。这种错误对于目标分割和提取是非常致命的。
如果将内轮廓面积作为限制条件进行填充,就可以很好解决上述问题。通常内轮廓面积应该不大于目标的最大面积。
#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
#pragma comment(lib, "cv.lib")
#pragma comment(lib, "cxcore.lib")
#pragma comment(lib, "highgui.lib")
// 内轮廓填充
// 参数:
// 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);
cvFindContours(pBinary, pStorage, &pContour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
// 填充所有轮廓
cvDrawContours(pBinary, pContour, CV_RGB(255, 255, 255), CV_RGB(255, 255, 255), 2, CV_FILLED, 8, cvPoint(0, 0));
// 外轮廓循环
for (; pContour != NULL; pContour = pContour->h_next)
{
// 内轮廓循环
for (pConInner = pContour->v_next; pConInner != NULL; pConInner = pConInner->h_next)
{
// 内轮廓面积
dConArea = fabs(cvContourArea(pConInner, CV_WHOLE_SEQ));
if (dConArea <= dAreaThre)
{
cvDrawContours(pBinary, pConInner, CV_RGB(255, 255, 255), CV_RGB(255, 255, 255), 0, CV_FILLED, 8, cvPoint(0, 0));
}
}
}
cvReleaseMemStorage(&pStorage);
pStorage = NULL;
}
}
int main()
{
IplImage *img = cvLoadImage(".//test.png", 0);
IplImage *bin = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
cvCopy(img, bin);
FillInternalContours(bin, 200);
cvNamedWindow("img");
cvShowImage("img", img);
cvNamedWindow("result");
cvShowImage("result", bin);
cvWaitKey(-1);
cvReleaseImage(&img);
cvReleaseImage(&bin);
return 0;
}