在做椭圆检测时,首先需要对灰度图像进行边缘检测,得到离散的边缘点。本文旨在介绍一种连通域标记算法,对边缘点进行合并,并去除较短的边,得到包含椭圆弧段的轮廓集合。
函数如下:
void Labeling(Mat1b& image, vector<vector<Point>>& segments, int iMinLength)
{
// 定义栈区
#define RG_STACK_SIZE 2048
int stack2[RG_STACK_SIZE];// 保存边缘点序号
#define RG_PUSH2(a) (stack2[sp2]=(a),sp2++)
#define RG_POP2(a) (sp2--, (a) = stack2[sp2])
Point stack3[RG_STACK_SIZE];// 保存边缘点坐标
#define RG_PUSH3(a) (stack3[sp3] = (a), sp3++)
#define RG_POP3(a) (sp3--, (a) = stack3[sp3])
int i, w, h, iDim;
int x, y;
int x2, y2;
int sp2;// 栈指针
int sp3;
Mat_<uchar> src = image.clone();
w = src.cols;
h = src.rows;
iDim = w*h;
Point point;
for (y = 0; y < h; ++y){
for (x = 0; x < w; ++x){
if ((src(y, x)) != 0){
sp2 = 0;
i = x + y*w;
RG_PUSH2(i);
sp3 = 0;
while (sp2 > 0){
RG_POP2(i);
x2 = i%w;
y2 = i / w;
point.x = x2;
point.y = y2;
if (src(y2, x2)){
RG_PUSH3(point);
src(y2, x2) = 0;
}
// 检测八连通区域
if (x2 > 0 && (src(y2, x2 - 1) != 0))
RG_PUSH2(i - 1);// 保存左点
if (y2 > 0 && (src(y2 - 1, x2) != 0))
RG_PUSH2(i - w);// 上点
if (y2 < h - 1 && (src(y2 + 1, x2) != 0))
RG_PUSH2(i + w);// 下点
if (x2 < w - 1 && (src(y2, x2 + 1) != 0))
RG_PUSH2(i + 1);// 右点
if (x2>0 && y2>0 && (src(y2 - 1, x2 - 1) != 0))
RG_PUSH2(i - w - 1);// 左上
if (x2 > 0 && y2 < h - 1 && (src(y2 + 1, x2 - 1) != 0))
RG_PUSH2(i + w - 1);// 左下
if (x2 < w - 1 && y2>0 && (src(y2 - 1, x2 + 1) != 0))
RG_PUSH2(i - w + 1);// 右上
if (x2 < w - 1 && y2 < h - 1 && (src(y2 + 1, x2 + 1) != 0))
RG_PUSH2(i + w + 1);// 右下
// 去除短边
if (sp3 >= iMinLength){
vector<Point> component;
component.reserve(sp3);
for (i = 0; i < sp3; ++i)
{
component.push_back(stack3[i]);// 轮廓压栈
}
segments.push_back(component);// 轮廓组压栈
}
}
}
}
};
本方法的特别之处在于利用栈来处理,具有更快的处理速度,但占用内存空间更大。