其实主要是imfill(matrix, 'holes'); 的openCV/C++ 实现。
Overview:
imfill是matlab的一个函数,在http://www.mathworks.cn/cn/help/images/ref/imfill.html 中有详细的讲解。这个函数有好几种不同的签名。在这里我的侧重点是imfill(m, 'holes'),以及如何用openCV来实现imfill一样的功能。本文有三部分组成。
1. 使用Matlab 的imfill 进行填充图像
在Matlab中简单的几行代码就能实现:
clc;
clear;
BW = im2bw( imread( 'imfilltest.tif'));
imshow(BW);
holes = imfill(BW, 'holes');
BW(~holes) = 1;
figure,imshow(holes);
左图为填充前的图像,右图是填充后的图像:
2. 用opencv来实现imfill(bw, 'holes')
opencv 在这里不像matlab那么好用了,matlab调用下。
#include
#include
#include
using namespace std;
using namespace cv;
void my_imfillholes(Mat &src)
{
// detect external contours
//
vector > contours;
vector hierarchy;
findContours(src, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
//
// fill external contours
//
if( !contours.empty() && !hierarchy.empty() )
{
for ( int idx= 0;idx < contours.size();idx++)
{
drawContours(src,contours,idx,Scalar::all( 255),CV_FILLED, 8);
}
}
}
void test_my_imfillholes()
{
Mat m = imread(filltestName,IMREAD_GRAYSCALE);
//threshold, (i,j)>100 -->255
Mat th_m;
threshold(m, th_m, 100, 255, THRESH_BINARY);
my_imfillholes(th_m);
namedWindow(WinName, CV_WINDOW_AUTOSIZE);
imshow(WinName, th_m);
waitKey( 0);
}
void main()
{
test_my_imfillholes();
system( "pause");
}
3. imfill 和opencv实现的imfill 对矩阵进行操作的对比
我仍有点不放心,觉得尽管2幅图看起来差不多,但是是不是完全一样呢,然后我觉得用个矩阵试一下。
m = [1, 1, 1, 0, 0, 0, 0, 0;
1, 0, 1, 0, 1, 1, 0, 0;
1, 0, 1, 0, 1, 1, 0, 0;
1, 1, 1, 0, 1, 0, 1, 0;
1, 0, 1, 0, 1, 0, 1, 0;
1, 1, 1, 0, 1, 0, 1, 0;
1, 0, 1, 0, 0, 1, 1, 0;
1, 1, 1, 0, 0, 0, 0, 0];
without_holes = imfill(m, 'holes')
得到结果:
without_holes =
1 1 1 0 0 0 0 0
1 1 1 0 1 1 0 0
1 1 1 0 1 1 0 0
1 1 1 0 1 1 1 0
1 1 1 0 1 1 1 0
1 1 1 0 1 1 1 0
1 1 1 0 0 1 1 0
1 1 1 0 0 0 0
然后用第2部分所说的opencv的方法也试一下,结果发现是这样的:
without_holes =
0 0 0 0 0 0 0 0
0 1 1 0 1 1 0 0
0 1 1 0 1 1 0 0
0 1 1 0 1 1 1 0
0 1 1 0 1 1 1 0
0 1 1 0 1 1 1 0
0 1 1 0 0 1 1 0
0 0 0 0 0 0 0
是不一样的。这个问题折腾了我一个晚上,终于,我在
中的 findContours找到了这样的一个note:
Note:
Source image is modified by this function. Also, the function does not take into account 1-pixel border
它的意思是,findCountours 是不会包含1-pixel的边界的。所以这就是为啥opencv计算的结果中边界的1都消失的原因。 我想,既然边界不被包含,那么我给它上下左右加一个1-pixel的框,这样边界点就变成了内部点,就能成功的用findCountours了。于是乎:我写了下面的code:
#include
#include
#include
using namespace std;
using namespace cv;
void my_imfillholes_v2()
{
//step 1: make a border
Mat m( 8, 8, CV_8UC1, data);
Mat m_with_border;
copyMakeBorder(m, m_with_border, 1, 1, 1, 1, BORDER_CONSTANT, Scalar());
cout<<m_with_border<<endl;
//setp 2: find the contour fill holes
vector > contours;
vector hierarchy;
findContours(m_with_border, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
//
// fill external contours
//
if( !contours.empty() && !hierarchy.empty() )
{
for ( int idx= 0;idx < contours.size();idx++)
{
drawContours(m_with_border,contours,idx,Scalar::all( 1),CV_FILLED, 8);
}
}
//cout<<m_with_border<<endl;
//step 3: remove the border
m_with_border = m_with_border.rowRange(Range( 1, m_with_border.rows- 1));
//cout<<m_with_border<<endl;
m_with_border = m_with_border.colRange(Range( 1, m_with_border.cols- 1));
cout<<m_with_border<<endl;
}
void main()
{
my_imfillholes_v2();
system( "pause");
}
先加一个全0的1-pixel的框,然后在findCountours填充,最后把框给去了。这样结果就完全和matlab中的imfill一致了。
result =
1 1 1 0 0 0 0
1 1 1 0 1 1 0
1 1 1 0 1 1 0
1 1 1 0 1 1 1
1 1 1 0 1 1 1
1 1 1 0 1 1 1
1 1 1 0 0 1 1
1 1 1 0 0 0 0