根据不同领域的不同需要,往往只对原始图像的某一部分进行处理,这些部分一般来说具有特定的灰度 纹理等特征,图像分割就是根据图像在各个区域的不同特性,而对齐进行边界或区域的分割,并从中提取所关心的目标。
图像分割算法可划分为4类:并行边界分割算法,串行边界分割算法,并行区域分割算法,串行区域分割算法。
并行边界分割算法:
图像边缘:图像局部区域亮度变化最显著的部分,该区域的灰度剖面一般可看作是一个阶跃。
梯度算子:
1.罗布特交叉算子:
2个2*2的模版:
1 0 0 1
0 -1
-1 0
代码:
/*************************************************************************
* 函数名称:
* SplitRoberts()
* 参数:
* BYTE* bmp,LONG width,LONG height ------图像参数
* 返回值:
* BOOL - 成功返回TRUE,否则返回FALSE。
* 说明:该函数对图象进行罗伯特交叉算子的边缘检测。采用2*2算子,所以只能对具有
*陡峭的低噪声图像才有很好的效果
************************************************************************/
voidSplitRoberts(BYTE *bmp,LONG width,LONG height);
voidMyProcess::SplitRoberts(BYTE *bmp,LONG width,LONG height)
{
LONG i,j;
BYTE *temp_bmp =new BYTE [(width+6)*(height+2)];
//填充temp_bmp
BmpFilter(temp_bmp,bmp,width,height);
//2*2模版
for(i=1;i<height+1;i++)
for(j=3;j<width+3;j++)
{
int a = temp_bmp[i*(width+6)+j];
int b = temp_bmp[(i+1)*(width+6)+j+1];
int c = temp_bmp[i*(width+6)+j+1];
int d = temp_bmp[(i+1)*(width+6)+j];
/*a = sqrt(a);
b = sqrt(b);
c = sqrt(c);
d = sqrt(d);*/
bmp[(i-1)*width+j-3]= sqrt((a-b)*(a-b)+(c-d)*(c-d));
}
delete[]temp_bmp;
}
薄瑞维特边缘算子(运算同上):
-1 0
1
1 1 1
-1 0 1
0 0 0
-1 0 1
1 1 1
if(type==0)//罗伯特交叉算子
{
int a = temp_bmp[i*(width+6)+j];
int b = temp_bmp[(i+1)*(width+6)+j+1];
int c = temp_bmp[i*(width+6)+j+1];
int d = temp_bmp[(i+1)*(width+6)+j];
bmp[(i-1)*width+j-3]= sqrt((a-b)*(a-b)+(c-d)*(c-d));
}
elseif(type==1)//薄瑞维特边缘算子
{
int a = temp_bmp[i*(width+6)+j]*-1;
a+= temp_bmp[i*(width+6)+j+2];
a+= temp_bmp[(i+1)*(width+6)+j]*-1;
a+= temp_bmp[(i+1)*(width+6)+j+2];
if(i<height)
{
a+= temp_bmp[(i+2)*(width+6)+j]*-1;
a+= temp_bmp[(i+2)*(width+6)+j+2];
}
int b = temp_bmp[i*(width+6)+j];
b+= temp_bmp[i*(width+6)+j+1];
b+= temp_bmp[i*(width+6)+j+2];
if(i<height)
{
b+= temp_bmp[(i+2)*(width+6)+j]*-1;
b+= temp_bmp[(i+2)*(width+6)+j+1]*-1;
b+= temp_bmp[(i+2)*(width+6)+j+2]*-1;
}
bmp[(i-1)*width+j-3]= sqrt((a)*(a)+(b)*(b));
}
}
索贝尔边缘算子
-1 0
1
1 2 1
-2 0 2
0 0 0
-1 0 1
1 2 1
elseif(type==2)//索贝尔边缘算子
{
int a = temp_bmp[i*(width+6)+j]*-1;
a+= temp_bmp[i*(width+6)+j+2];
a+= temp_bmp[(i+1)*(width+6)+j]*-2;
a+= temp_bmp[(i+1)*(width+6)+j+2]*2;
if(i<height)
{
a+= temp_bmp[(i+2)*(width+6)+j]*-1;
a+= temp_bmp[(i+2)*(width+6)+j+2];
}
int b = temp_bmp[i*(width+6)+j];
b+= temp_bmp[i*(width+6)+j+1]*2;
b+= temp_bmp[i*(width+6)+j+2];
if(i<height)
{
b+= temp_bmp[(i+2)*(width+6)+j]*-1;
b+= temp_bmp[(i+2)*(width+6)+j+1]*-2;
b+= temp_bmp[(i+2)*(width+6)+j+2]*-1;
}
bmp[(i-1)*width+j-3]= sqrt((a)*(a)+(b)*(b));
}
拉普拉斯算子:
在模版滤波中实现,速度优于方向导数滤波。适合无噪声图像,是无方向性算子
Kirsch方向算子:
采用一组模版分别计算不同方向上的差值,取其最大值作为最终输出的边缘强度
{5,5,5,
-3,0,-3,
-3,-3,-3,
1,3},
{-3,5,5,
-3,0,5,
-3,-3,-3,
1,3},
{-3,-3,5,
-3,0,5,
-3,-3,5,
1,3},
{-3,-3,-3,
-3,0,5,
-3,5,5,
1,3}
/*************************************************************************
* 函数名称:SplitKirsch()
* 参数:BYTE* bmp,LONG width,LONG height ------图像参数
* 返回值:无
* 说明:该函数进行Kirsch方向算子边缘检测。
************************************************************************/
voidSplitKirsch(BYTE *bmp,LONG width,LONG height);
LONG i,j;
//申请一个临时空间,由1包围bmp图像,方便进行模版处理
BYTE *temp_bmp =new BYTE[(width+6)*(height+2)];
BmpFilter(temp_bmp,bmp,width,height);
//模版滤波
for(i=1;i<height+1;i++)
for(j=3;j<width+3;j++)
{
LONG g,max=-99999;
for(g=0;g<4;g++)
{
LONG k,m,temp=0;
for(k=-1;k<kernel_kirsch[g].Dimention-1;k++)
for(m=-1;m<kernel_kirsch[g].Dimention-1;m++)
{
int a = temp_bmp[(i+k)*(width+6)+j+m*3];
int b = kernel_kirsch[g].Element[k+1][m+1];
temp+=a*b;
}
//temp/=kernel.Divisor;
//
//此处注意,不应将值给待处理对象,而应传给未处理的原图像
// temp_bmp[i*(width+6)+j] = (BYTE)temp;
if(temp>max) max = temp;
}
bmp[(i-1)*width+j-3]= max;
}
delete[] temp_bmp;
轮廓提取:
方法:如果当前8领域的值中间值的差相等(或在一定范围内),则认为是非轮廓,将值设为白色。
/*************************************************************************
* 函数名称:SplitContour()
* 参数:BYTE* bmp,LONG width,LONG height ------图像参数
* 返回值:无
* 说明:轮廓提取
************************************************************************/
voidSplitContour(BYTE *bmp,LONG width,LONG height);
voidMyProcess::SplitContour(BYTE *bmp,LONG width,LONG height)
{
LONG i,j;
BYTE *temp_bmp =new BYTE[width*height];
memcpy(temp_bmp,bmp,width*height);
for(i=0;i<height;i++)
for(j=0;j<width/3;j++)
{
int I=0,temp_r,temp_g,temp_b;
temp_r = temp_bmp[i*width+j*3];
temp_g = temp_bmp[i*width+j*3+1];
temp_b = temp_bmp[i*width+j*3+2];
for(int k=i-1;k<i+2;k++)
for(int l=j-1;l<j+2;l++)
{
if(k>=0&& l>=0&& l<width && k<height)
{
if(abs(temp_bmp[k*width+l*3]-temp_r)<10&&
abs(temp_bmp[k*width+l*3+1]-temp_g)<10&&
abs(temp_bmp[k*width+l*3+2]-temp_b)<10)
{
I++;
}
}
}
if(I==9)
{
bmp[i*width+j*3]=255;
bmp[i*width+j*3+1]=255;
bmp[i*width+j*3+2]=255;
}
}
delete[] temp_bmp;
}
Hough变换直线检测
检测直线,直线可写成p=xcos(angle)+ysin(angle);
p为原点到直线距离,angle为斜率角
再找出最大的num[angle][p],即可确定一条直线,因为同一直线上的点,当angle一样时,p只有等于原直线斜率时才相等
/*************************************************************************
* 函数名称:HoufuLine()
* 参数:BYTE* bmp,LONG width,LONG height ------图像参数
* 返回值:无
* 说明:Houfu直线检测
************************************************************************/
voidHoufuLine(BYTE *bmp,LONG width,LONG height);
voidMyProcess::HoufuLine(BYTE *bmp,LONG width,LONG height)
{
LONG i,j,angle,p;
BYTE *temp_bmp =new BYTE[width*height];
//图像二值化
for(i=0;i<height;i++)
for(j=0;j<width;j++)
{
if(bmp[i*width+j]>128) temp_bmp[i*width+j]=255;
else temp_bmp[i*width+j]=0;
}
//存储累计变量的数组
//最大距离
int maxDis = sqrt(width*width+height*height);
int*num =newint[360*maxDis];
//初始化数组
memset(num,0,(360*maxDis)*sizeof(int));
//检测直线,p=xcos(angle)+ysin(angle);
//再找出最大的num[angle][p],即可确定一条直线
int max=0,maxa,maxp;
for(i=0;i<height;i++)
for(j=0;j<width;j++)
{
if(temp_bmp[i*width+j]==0)
for(angle=0;angle<360;angle++)
{
p=abs(i*sin(angle/360.0*6.28)+j*cos(angle/360.0*6.28));
num[p*360+angle]++;
if(max < num[p*360+angle])
{
max=num[p*360+angle];
maxa=angle;
maxp=p;
}
}
}
//找出直线的策略---数目大于一个值即认为是直线
int line = width/10;
// for(i=0;i<360;i++)
// for(j=0;j<width+height;j++)
{
// if(num[i*maxDis+j]>line)
{
/*double k=tan(i/360.0*6.28);
double b=j/sin(i/360.0*6.28);
for(int m=0;m<width;m++)
{
int y = k*m+b;
bmp[y*width+m]=0;
}*/
int k,m;
for(k=0;k<height;k++)
for(m=0;m<width;m++)
{
// if(temp_bmp[k*width+m]==0)
{
if(maxp==abs(m*cos(maxa/360.0*6.28)+k*sin(maxa/360.0*6.28)))
{
bmp[k*width+m]=0;
}
}
}
}
}
}
并行区域分割算法:
迭代求阈值
/*************************************************************************
* 函数名称:Threshold()
* 参数:BYTE* bmp,LONG width,LONG height ------图像参数
* 返回值:int 阈值,返回迭代的阈值
* 说明:并行区域分割,迭代求阈值,先取一阈值,分割图像,分别积分并平均,反复迭代,直到阈值不变
积分即离散值加权和
************************************************************************/
intThreshold(BYTE *bmp,LONG width,LONG height);
intMyProcess::Threshold(BYTE *bmp,LONG width,LONG height)
{
//得到灰度分布直方图数值
int*grayInt =newint[256];
memset(grayInt,0,256*sizeof(int));
this->GetGrayIntensity(bmp,grayInt,width,height);
//定义阈值
long T1=127,T2=0;
long i,j;
//迭代阈值选取
long temp0=0,temp1=0,temp2=0,temp3=0;
while(1)
{
//定义临时变量
for(i=0;i<T1+1;i++)
{
temp0+=grayInt[i]*i;
temp1+=grayInt[i];
}
for(;i<256;i++)
{
temp2+=grayInt[i]*i;
temp3+=grayInt[i];
}
T2 =(temp0/temp1+temp2/temp3)/2;
//迭代是否结束
if(T1==T2)break;
else T1=T2;
}
//对bmp进行转换
for(i=0;i<height;i++)
for(j=0;j<width;j++)
{
if(bmp[i*width+j]<T1) bmp[i*width+j]=0;
else bmp[i*width+j]=255;
}
delete[] grayInt;
return T1;
}
串行区域分割
区域生长:选取多个种子,将图像像素与其比较,将像素点设为最接近的点
/*************************************************************************
* 函数名称:RgnGrow()
* 参数:BYTE* bmp,LONG width,LONG height ------图像参数
BYTE c1,BYTE c2 种子1和种子二的值
* 返回值:无
* 说明:串行区域分割,区域生长 对灰度图进行处理
************************************************************************/
voidRgnGrow(BYTE *bmp,LONG width,LONG height,BYTE c1,BYTE c2);
voidMyProcess::RgnGrow(BYTE *bmp,LONG width,LONG height,BYTE c1,BYTE c2)
{
LONG i,j;
for(i=0;i<height;i++)
for(j=0;j<width;j++)
{
BYTE temp=bmp[i*width+j];
if(abs(temp-c1)<abs(temp-c2))
{
bmp[i*width+j]=c1;
}
else
{
bmp[i*width+j]=c2;
}
}
}