您的位置:首页 > 运维架构

【OpenCV学习笔记 016】图像分割-种子区域生长

2016-11-22 11:08 791 查看
    区域生长是一种古老的图像分割方法,最早的区域生长图像分割方法是由Levine等人提出的。该方法一般有两种方式,一种是先给定图像中要分割的目标物体内的一个小块或者说种子区域(seed point),再在种子区域基础上不断将其周围的像素点以一定的规则加入其中,达到最终将代表该物体的所有像素点结合成一个区域的目的;另一种是先将图像分割成很多的一致性较强,如区域内像素灰度值相同的小区域,再按一定的规则将小区域融合成大区域,达到分割图像的目的,典型的区域生长法如T. C. Pong等人提出的基于小面(facet)模型的区域生长法,区域生长法固有的缺点是往往会造成过度分割,即将图像分割成过多的区域
。 

    区域生长是一种串行区域分割的图像分割方法,其优点是基本思想相对简单,通常能将具有相同特征的联通区域分割出来,并能提供很好的边界信息和分割结果。在没有先验知识可以利用时,可以取得最佳的性能,可以用来分割比较复杂的图象,如自然景物。但是,区域生长法是一种迭代的方法,空间和时间开销都比较大,噪声和灰度不均一可能会导致空洞和过分割,并在对图像中的阴影效果处理上往往不是很好。

    区域生长的基本思想是将具有相似性质的像素集合起来构成区域。具体先对每个需要分割的区域找一个种子像素作为生长的起点,然后将种子像素周围邻域中与种子像素具有相同或相似性质的像素(根据某种事先确定的生长或相似准则来判定)合并到种子像素所在的区域中。将这些新像素当做新的种子像素继续进行上面的过程,直到再没有满足条件的像素可被包括进来,这样,一个区域就长成了。

    区域生长是指从某个像素出发,按照一定的准则,逐步加入邻近像素,当满足一定的条件时,区域生长终止。区域生长的好坏决定于1.初始点(种子点)的选取。2.生长准则。3.终止条件。区域生长是从某个或者某些像素点出发,最后得到整个区域,进而实现目标的提取。

    简单来说下三个法则,对出需要分割的图像:1、选取图像中的一点为种子点(种子点的选取需要具体情况具体分析)。2、在种子点处进行8邻域或4邻域扩展,判定准则是:如果考虑的像素与种子像素灰度值差的绝对值小于某个门限T,则将该像素包括进种子像素所在的区域。3、当不再有像素满足加入这个区域的准则时,区域生长停止。

区域生长实现的步骤如下:

1. 对图像顺序扫描!找到第1个还没有归属的像素, 设该像素为(x0, y0);

2. 以(x0, y0)为中心, 考虑(x0, y0)的8邻域像素(x, y),如果(x,, y)满足生长准则, 将(x, y)与(x0, y0)合并(在同一区域内), 同时将(x, y)压入堆栈;

3. 从堆栈中取出一个像素, 把它当作(x0, y0)返回到步骤2;

4. 当堆栈为空时!返回到步骤1;

5. 重复步骤1 - 4直到图像中的每个点都有归属时。生长结束。
源代码:

#include <iostream>
#include "cv.h"
#include "highgui.h"

using namespace std;
#define maxsize 5000

void Grow(IplImage* src, IplImage* src1, int t);

typedef struct
{
int stack[maxsize];
int top;
}mystack;

void initstack(mystack *s){

s->top = 0;
}

int pushstack(mystack *s, int e)//入栈
{
if (s->top >= maxsize)
return 0;
else
{
s->stack[s->top] = e;
s->top++;
return 1;
}

}

int popstack(mystack *s, int *e)//出栈
{
if (s->top == 0)
return 0;
else
{
s->top--;
*e = s->stack[s->top];
return 1;
}
}

int main(int argc, char**argv)
{
//src存放原图像,src1存放区域生长后的图像 lab-k-f-one 2lab-k
IplImage* src = cvLoadImage("picture.jpg", 0);
IplImage* src1 = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);

src1->origin = 0;
cvZero(src1);
//cout<<"q="<<q<<endl;
Grow(src, src1, 20);
//cout<<"q1="<<q<<endl;
cvNamedWindow("原图片");
cvNamedWindow("区域生长图片");

cvShowImage("原图片", src);
cvShowImage("区域生长图片", src1);

cvReleaseImage(&src);
cvReleaseImage(&src1);

cvWaitKey(0);
cvDestroyWindow("原图片");
cvDestroyWindow("区域生长图片");
}

void Grow(IplImage* src, IplImage* src1, int t1)
{
mystack stackx; //两个栈分别存放X Y坐标
mystack stacky;
initstack(&stackx); //初始化
initstack(&stacky);

static int nDx[] = { -1, 0, 1, 0 };
static int nDy[] = { 0, 1, 0, -1 };
int height = src->height;
int width = src->width;
int step = src->widthStep;//每行所占字节数
int seedx, seedy;
////cout<<height<<endl;
////cout<<width<<endl;
////cout<<step<<endl;
uchar* src1_data = (uchar*)src1->imageData;
uchar* src_data = (uchar*)src->imageData;

//取区域质心
int m00 = 0, m01 = 0, m10 = 0;
for (int i = 0; i < height; i++)
for (int j = 0; j < width; j++)
m00 += src_data[i*step + j];

for (int i = 0; i < height; i++)
for (int j = 0; j < width; j++)
{
m10 += i*src_data[i*step + j];
m01 += j*src_data[i*step + j];
}
seedx = m10 / m00;
seedy = m01 / m00;
pushstack(&stackx, seedx);
pushstack(&stacky, seedy);
int nCurrX;//当前点
int nCurrY;
int xx;
int yy;
while (stackx.top > 0 && stacky.top>0)
{
//popstack(&stackx,&stackx.top);
//popstack(&stackx,&stackx.top);
nCurrX = stackx.stack[stackx.top - 1]; //返回栈顶数据
nCurrY = stacky.stack[stacky.top - 1];
stackx.top--;
stacky.top--;
//seedd.pop(); //栈顶数据出栈
//cout<<point.x<<endl;
for (int k = 0; k < 4; k++)
{
// 4邻域象素的坐标
xx = nCurrX + nDx[k];
yy = nCurrY + nDy[k];

if ((xx < width) && (xx >= 0) && (yy >= 0) && (yy < height) && (src1_data[yy*step + xx] == 0) && abs(src_data[yy*step + xx] - src_data[nCurrY*step + nCurrX]) < t1)
{
// 堆栈的尾部指针后移一位
// 象素(xx,yy) 压入栈
pushstack(&stackx, xx);
pushstack(&stacky, yy);
// 把象素(xx,yy)设置成逻辑()
// 同时也表明该象素处理过
src1_data[yy*step + xx] = 255;

}
}

}
}实验效果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: