图像分割--主要是提取潜在目标区域,可以利用边缘算子,或者轮廓,或者根据某种特征找到最佳阈值。
这里主要考虑的是:阈值分割
- Kmeans
- 模糊数学
(1)介绍波峰的确定
首先将图像转为灰度图像,然后求灰度直方图,对于波峰不明显的情况,采用直方图增强技术。
波峰就是导数为0,且满足F(x)>F(x−1)且F(x)>F(x+1)。
或者波峰处的一阶导数为0,二阶导数为负。 二阶导数求波峰
在图像中采用差分得到一节导数。
(2)Kmeans
将物理或抽象对象的集合分成由类似的对象组成的多个类的过程被称为聚类。由聚类所生成的簇是一组数据对象的集合,这些对象与同一个簇中的对象彼此相似,与其他簇中的对象相异。聚类分析又称群分析,它是研究(样品或指标)分类问题的一种统计分析方法。聚类分析计算方法主要有如下几种:划分方法,层次方法,基于密度的方法,基于网格的方法,基于模型的方法。
K-均值聚类算法是著名的划分聚类分割方法。划分方法的基本思想是:给定一个有N个元组或者纪录的数据集,分裂法将构造K个分组,每一个分组就代表一个聚类,K<N。而且这K个分组满足下列条件:(1) 每一个分组至少包含一个数据纪录;(2)每一个数据纪录属于且仅属于一个分组;对于给定的K,算法首先给出一个初始的分组方法,以后通过反复迭代的方法改变分组,使得每一次改进之后的分组方案都较前一次好,而所谓好的标准就是:同一分组中的记录越近越好,而不同分组中的纪录越远越好。
K-均值聚类算法的工作原理:
K-means算法的工作原理:算法首先随机从数据集中选取 K个点作为初始聚类中心,然后计算各个样本到聚类中心的距离,把样本归到离它最近的那个聚类中心所在的类。计算新形成的每一个聚类的数据对象的平均值来得到新的聚类中心,如果相邻两次的聚类中心没有任何变化,说明样本调整结束,聚类准则函数已经收敛。本算法的一个特点是在每次迭代中都要考察每个样本的分类是否正确。若不正确,就要调整,在全部样本调整完后,再修改聚类中心,进入下一次迭代。这个过程将不断重复直到满足某个终止条件,终止条件可以是以下任何一个:
(1)没有对象被重新分配给不同的聚类。
(2)聚类中心再发生变化。
(3)误差平方和局部最小。
K-means聚类算法的一般步骤:
处理流程:
(1)从 n个数据对象任意选择 k 个对象作为初始聚类中心;
(2)循环(3)到(4)直到每个聚类不再发生变化为止;
(3)根据每个聚类对象的均值(中心对象),计算每个对象与这些中心对象的距离;并根据最小距离重新对相应对象进行划分;
(4)重新计算每个(有变化)聚类的均值(中心对象),直到聚类中心不再变化。这种划分使得下式最小
K-均值聚类法的缺点:
(1)在 K-means 算法中 K 是事先给定的,这个 K 值的选定是非常难以估计的。
(2)在 K-means 算法中,首先需要根据初始聚类中心来确定一个初始划分,然后对初始划分进行优化。
(3) K-means算法需要不断地进行样本分类调整不断地计算调整后的新的聚类中心因此当数据量非常大时算法的时间开销是非常大的。
(4)K-means算法对一些离散点和初始k值敏感,不同的距离初始值对同样的数据样本可能得到不同的结果。
传统的Kmeans用于图像分割中的应用
OpenCV中K均值函数为:
void cvKMeans2(const CvArr* samples,int cluster_count,
CvArr* labels,CvTermCriteria tercrit);
Samples:输入样本的浮点矩阵,每个样本一行。
cluster_count:所给定的聚类数目
labels:输出整数向量:每个样本对应的类别标识
termcrit:指定聚类的最大迭代次数和/或精度(两次迭代引起的聚类中心的移动距离)
其执行 k-means 算法搜索 cluster_count 个类别的中心并对样本进行分类,输出 labels(i) 为样本i的类别标识。
其中CvTermCriteria为OpenCV中的迭代算法的终止准则,其结构如下:
#define CV_TERMCRIT_ITER 1
#define CV_TERMCRIT_NUMBER CV_TERMCRIT_ITER
#define CV_TERMCRIT_EPS 2
typedef struct CvTermCriteria
{
int type;
int max_iter;
doubleepsilon;
}CvTermCriteria;
max_iter:最大迭代次数。
epsilon:结果的精确性 。
利用OpenCV对彩色图像进行颜色聚类的基本步骤及核心代码如下:
1、读取彩色图像。
2、利用k均值聚类对像素的灰度值进行划分,提取特征点。
(1)获取彩色图像RGB三通道的值并将像素点三通道的值按顺序排入样本矩阵。
for(i=0;i<m_imagesrc->width;i++)
{
for (j=0;j<m_imagesrc->height;j++)
{
CvScalar s;
//获取图像各个像素点的三通道值(RGB)
s.val[0]=(float)cvGet2D(m_imagesrc,j,i).val[0];
s.val[1]=(float)cvGet2D(m_imagesrc,j,i).val[1];
s.val[2]=(float)cvGet2D(m_imagesrc,j,i).val[2];
cvSet2D(samples,k++,0,s); }
}
这里用到cvGet2D函数获取三通道值并利用cvSet2D函数将三通道的值排入样本。函数如下:
CvScalar cvGet2D(const CvArr* arr, int idx0, int idx1 );
Arr:输入数组。
idx0:元素下标第一个以0为基准的成员。
idx1:元素下标第二个以0为基准的成员。
void cvSet2D(CvArr* arr, int idx0, int idx1, CvScalar value );
arr:输入数组。
idx0:元素下标的第一个成员,以0为基点。
idx1:元素下标的第二个成员,以0为基点。
(2)对样本矩阵的值进行迭代聚类,迭代100次,终止误差1.0。
cvKMeans2(samples,nCuster,clusters,cvTermCriteria(CV_TERMCRIT_ITER,100,1.0));
3、绘制聚类后的图像。
将聚类后的不同类别取不同的像素值,并对聚类后的图像的每个像素点赋值。
float step=255/(nCuster-1);
for (i=0;i<m_imagesrc->width;i++)
{
for(j=0;j<m_imagesrc->height;j++)
{
val=(int)clusters->data.i[k++];
CvScalar s;
s.val[0]=255-val*step;,
cvSet2D(bin,j,i,s);
}
}
4、绘制灰度直方图。
(1)设置直方图尺寸和灰度范围,这里将显示范围设置为-5到260,防止灰度值为0 和255的直方图不显示。
int hist_size = 256;
int hist_height = 256;
float range[] = {-5,260};
float* ranges[]={range};
(2)创建直方图,统计图像在[0 255]像素的均匀分布
CvHistogram* gray_hist
=cvCreateHist(1,&hist_size,CV_HIST_ARRAY,ranges,1);
cvCalcHist(&bin,gray_hist,0,0);
cvNormalizeHist(gray_hist,1.0);
int scale= 2;
//横坐标为灰度级,纵坐标为像素个数*scale
IplImage* hist_image =
cvCreateImage(cvSize(hist_size*scale,hist_height),8,3);
cvZero(hist_image);
float max_value = 0;
cvGetMinMaxHistValue(gray_hist, 0,&max_value,0,0);
其中CvHistogram为多维直方图类型,其定义如下:
typedef structCvHistogram
{
int type;
CvArr* bins;
float thresh[CV_MAX_DIM][2];
float** thresh2;
CvMatND mat;
}CvHistogram;
bins : 用于存放直方图每个灰度级数目的数组指针,数组在cvCreateHist 的时候创建其维数由cvCreateHist确定。
而cvCreateHist创建一个指定尺寸的直方图,并且返回创建的直方图的指针。其定义如下:
CvHistogram* cvCreateHist( int dims, int* sizes, int type,
float** ranges=NULL,int uniform=1 );
dims:直方图维数的数目。
sizes:直方图维数尺寸的数组。
type:直方图的表示格式: CV_HIST_ARRAY 意味着直方图数据表示为多维密集数组CvMatND; CV_HIST_TREE 意味着直方图数据表示为多维稀疏数组CvSparseMat.
Ranges:图中方块范围的数组. 它的内容取决于参数 uniform 的值。这个范围的用处是确定何时计算直方图或决定反向映射(backprojected),每个方块对应于输入图像的哪个/哪组值。
Uniform:归一化标识。
而CalcHist用来计算图像image的直方图,其定义如下:
void cvCalcHist(IplImage** image, CvHistogram* hist,
int accumulate=0, const CvArr*mask=NULL );
image:输入图像 (也可以使用CvMat** )。
Hist:直方图指针。
Accumulate:累计标识。如果设置,则直方图在开始时不被清零。这个特征保证可以为多个图像计算一个单独的直方图,或者在线更新直方图。
Mask:操作mask,确定输入图像的哪个象素被计数。
(3)分别将每个直方块的值绘制到图中
for( i=0;i<hist_size;i++)
{
float bin_val =cvQueryHistValue_1D(gray_hist,i);
int intensity =cvRound(bin_val*hist_height/max_value);
cvRectangle(hist_image,cvPoint(i*scale,hist_height-1), cvPoint((i+1)*scale-1,hist_height-intensity),CV_RGB(255,255,255));
}
cvQueryHistValue_1D用来查询直方块的值,其定义如下:
#definecvQueryHistValue_1D( hist, idx0) \
cvGetReal2D( (hist)->bins, (idx0) )
hist:直方图。
idx0, idx1, idx2,idx3:直方块的下标索引。
idx:下标数组。
函数cvRound,用一种舍入方法将输入浮点数转换成整数, 返回和参数最接近的整数值。
cvRectangle函数用以绘制简单、指定粗细或者带填充的矩形,其定义如下:
void cvRectangle( CvArr* img,CvPoint pt1, CvPoint pt2, CvScalar color, int thickness=1, int line_type=8, intshift=0 );
img:目标图像。
pt1:矩形的一个顶点。
pt2:矩形对角线上的另一个顶点。
Color:线条颜色 (RGB) 或亮度(灰度图像 )(grayscale image)。
Thickness:组成矩形的线条的粗细程度。取负值时(如 CV_FILLED)函数绘制填充了色彩的矩形。
line_type:线条的类型。
Shift:坐标点的小数点位数。
5、实现结果如图:
图4-1 输入聚类类别数
图4-2 分为3类聚类后的图像和直方图
(3)将直方图的波峰应用到Kmeans中
将波峰的个数作为K,将波峰的灰度值作为初始值。