OpenCV例程:针对对比度差的图像,目标分割方法
1、目标区域和背景区域灰度值近似的图像,如下图所示:
在图中,
比较平滑的地方为路面--目标,
纹理较丰富的地方为树林--背景;这是遥感图像;
那么问题来了:
如何在这幅图像中,分割出路面目标区域?

2、在上图中,常见的阈值分割法肯定是不行的。
这里给出了一种纹理分割方法,
使用了三种特征来表征某一像素在其领域范围内的纹理性质;
根据路面目标区域和树林背景区域的纹理性质不同,来进行分析。
首先,给出了三个特征模板,如图所示:
①单位面积灰度变化总量:
针对这个公式,我构造了一个5*5的模板
即只需要用这个模板对原图进行滤波就可以求出单位面积灰度变化总量;
②Laws纹理测量模板中的高频点检测模板R5R5
③Laws纹理测量模板中的V形状检测模板E5S5

3、分别将上述三个模板与图像做卷积,
并将得到的结果组成一个矢量;在三维空间里面进行Kmean聚类;
程序如下:
Mat pLawsEnergy(raw_img.size(),CV_32FC3);//特征向量集合
int i,j,m,n;
float sumRR,sumES,sumVAR;
for (i=2;i<m_width-2;i++)//lie
{
for (j=2;j<m_height-2;j++)//hang
{
sumRR = 0;
sumES = 0;
sumVAR= 0;
for (m=-2;m<=2;m++)//核大小,卷积操作
{
for (n=-2;n<=2;n++)
{
unsigned char *raw_data=(unsigned char*)(raw_img.data+(j+n)*raw_img.step);
float p_data = raw_data[i+m];
sumES += E5S5[m+2][n+2]*p_data;
sumRR += R5R5[m+2][n+2]*p_data;
sumVAR+= VAR[m+2][n+2]*p_data;
}
}
float *pLawsEnergy_data = (float*)(pLawsEnergy.data+j*pLawsEnergy.step);
pLawsEnergy_data[3*i+0]=abs(sumVAR)/25;
pLawsEnergy_data[3*i+1]=abs(sumRR)/25;
pLawsEnergy_data[3*i+2]=abs(sumES)/25;
}
}


4、k-means聚类:
k-means算法是一个聚类算法,
聚类算法用于根据数据的特征发现数据项的相似性,并将相似的数据项放在同一个组中,相似性采用距离进行描述。
k-means聚类的流程:
首先,随机选取k个点,将每个点分配给它们,得到最初的k个分类;
然后,在每个分类中计算均值,将点重新分配,划归到最近的中心点;
重复上述步骤直到点的划归不再改变。

5、k-means聚类的流程:
针对本图像,分为路面目标和树林背景两类;
①设置初始聚类中心{100,100,100},{0,0,0};
②根据差的绝对值之和计算相似度,将图像像素划分为目标和背景两类;
③重新计算聚类中心;一般用求重心的方法;
④判断,聚类中心是否改变,如果没改变则收敛;否则,继续迭代。
⑤采用形态学操作进一步优化分割图像;
如图所示:
//进来的朋友,点赞,关注,投票,谢谢!

6、本文程序总结如下:
由于字数较多,故通过图像显示如下:
#include "stdafx.h"
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
const int R5R5[5][5]=
{
1, -4, 6, -4, 1,
-4, 16,-24, 16, -4,
6, -24, 36, -24, 6,
-4, 16,-24, 16, -4,
1, -4, 6, -4, 1
};
const int E5S5[5][5]=
{
-1, 0, 2, 0, -1,
-2, 0, 4, 0, -2,
0, 0, 0, 0, 0,
2, 0, -4, 0, 2,
1, 0, -2, 0, 1
};
const int VAR[5][5]=
{
-1, -1, -1, -1, -1,
-1, -1, -1, -1, -1,
-1, -1, 24, -1, -1,
-1, -1, -1, -1, -1,
-1, -1, -1, -1, -1,
};
Mat raw_img = imread("3A.bmp",0);
int m_height = raw_img.rows;
int m_width = raw_img.cols;
Mat pLawsEnergy(raw_img.size(),CV_32FC3);
int i,j,m,n;
float sumRR,sumES,sumVAR;
for (i=2;i<m_width-2;i++)
{
for (j=2;j<m_height-2;j++)
{
sumRR = 0;
sumES = 0;
sumVAR= 0;
for (m=-2;m<=2;m++)
{
for (n=-2;n<=2;n++)
{
unsigned char *raw_data=(unsigned char*)(raw_img.data+(j+n)*raw_img.step);
float p_data = raw_data[i+m];
sumES += E5S5[m+2][n+2]*p_data;
sumRR += R5R5[m+2][n+2]*p_data;
sumVAR+= VAR[m+2][n+2]*p_data;
}
}
float *pLawsEnergy_data = (float*)(pLawsEnergy.data+j*pLawsEnergy.step);
pLawsEnergy_data[3*i+0]=abs(sumVAR)/25;
pLawsEnergy_data[3*i+1]=abs(sumRR)/25;
pLawsEnergy_data[3*i+2]=abs(sumES)/25;
}
}
//namedWindow("dst",CV_WINDOW_NORMAL);
//imshow("dst",pLawsEnergy);
//聚类分割结果
Mat clusters_img(raw_img.size(),CV_8UC1);
float center1[3]={100,100,100};
float center2[3]={0,0,0};//初始聚类中心
float center1_pre[3]={0,0,0};//判别是否收敛
float center2_pre[3]={0,0,0};
unsigned int loop_times=0;//循环次数
unsigned int flag = 0;
int classnum[2]={0,0};//每种类别的个数
do
{
loop_times++;
classnum[0]=0;
classnum[1]=0;
float dist1,dist2;
for (i=2;i<m_width-2;i++)
{
for (j=2;j<m_height-2;j++)
{
dist1=0;
dist2=0;
for (m=0;m<3;m++)
{
float *pLawsEnergy_data_1=(float*)(pLawsEnergy.data+j*pLawsEnergy.step);
dist1 += abs(pLawsEnergy_data_1[3*i+m]-center1[m]);
dist2 += abs(pLawsEnergy_data_1[3*i+m]-center2[m]);
}
if (dist1 < dist2)
{
unsigned char *clusters_data_1=(unsigned char*)(clusters_img.data+j*clusters_img.step);
clusters_data_1[i]=0;
classnum[0]++;
}
else
{
unsigned char *clusters_data_2=(unsigned char*)(clusters_img.data+j*clusters_img.step);
clusters_data_2[i]=255;
classnum[1]++;
}
}
}
//重新计算聚类中心
for (i=0;i<3;i++)
{
center1[i]=0;
center2[i]=0;
}
for (i=2;i<m_width-2;i++)
{
for (j=2;j<m_height-2;j++)
{
unsigned char *clusters_data_3=(unsigned char*)(clusters_img.data+j*clusters_img.step);
if (clusters_data_3[i]==0)
{
for (m=0;m<3;m++)
{
float *pLawsEnergy_data_2=(float*)(pLawsEnergy.data+j*pLawsEnergy.step);
center1[m] +=pLawsEnergy_data_2[3*i+m]/classnum[0];
}
}
else
{
for (m=0;m<3;m++)
{
float *pLawsEnergy_data_3=(float*)(pLawsEnergy.data+j*pLawsEnergy.step);
center2[m] +=pLawsEnergy_data_3[3*i+m]/classnum[1];
}
}
}
}
flag=0;//判断是否收敛
for (m=0;m<3;m++)
{
if (center1[m]!=center1_pre[m])
{
flag=1;
center1_pre[m]=center1[m];
}
if (center2[m]!=center2_pre[m])
{
flag=1;
center2_pre[m]=center2[m];
}
}
} while (flag && loop_times<150);
imwrite("temp_binary.jpg",clusters_img);
Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));
dilate(clusters_img, clusters_img, element);
erode(clusters_img, clusters_img, element);
dilate(clusters_img, clusters_img, element);
imwrite("binary_img.jpg",clusters_img);
Mat dst_img;
raw_img.copyTo(dst_img,~clusters_img);
imwrite("segment.jpg",dst_img);
waitKey(0);
return 0;
}
