基于K-Means的图像分割
发布时间
阅读量:
阅读量
简述
作为一种典型的无监督聚类方法,在数据挖掘领域具有重要应用价值。该算法的核心思想在于选取K个初始点作为聚类中心,并通过不断优化的方式将所有数据对象按照与各中心点之间的距离最小原则分组形成K个集群。随后重新计算每个集群的核心位置,并持续执行这一过程直至数据集群不再发生显著变化或达到预设的最大迭代次数。同时确保不同集群之间的间距最大化的同时使同一集群内部的数据点尽可能地紧密聚集。
在每次迭代过程中,在初始化阶段我们随机生成初始质心;从第二个阶段起则基于上一轮划分结果计算各集群均值的位置作为新的质心位置;这就是为什么它被称为K-means聚类的原因。
K均值算法流程图

模拟效果图

Learning Opencv的源码
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "opencv2/opencv.hpp"
#include <iostream>
#include <opencv/cv.h>
#include <opencv/cvaux.h>
#include <opencv/cxcore.h>
#include <opencv/highgui.h>
int main( int argc, char** argv )
{
//类别个数的上界
#define MAX_CLUSTERS 5
CvScalar color_tab[MAX_CLUSTERS];
IplImage* img = cvCreateImage( cvSize( 500, 500 ), 8, 3 );
CvRNG rng = cvRNG(0xffffffff);
color_tab[0] = CV_RGB(255,0,0);
color_tab[1] = CV_RGB(0,255,0);
color_tab[2] = CV_RGB(100,100,255);
color_tab[3] = CV_RGB(255,0,255);
color_tab[4] = CV_RGB(255,255,0);
cvNamedWindow( "clusters", 1 );
for(;;)
{
//产生随机数
int k, cluster_count = cvRandInt(&rng)%MAX_CLUSTERS + 1;
int i, sample_count = cvRandInt(&rng)%1000 + 1;
CvMat* points = cvCreateMat( sample_count, 1, CV_32FC2 );
CvMat* clusters = cvCreateMat( sample_count, 1, CV_32SC1 );
/* generate random sample from multivariate
Gaussian distribution */
for( k = 0; k < cluster_count; k++ )
{
CvPoint center;
CvMat point_chunk;
center.x = cvRandInt(&rng)%img->width;
center.y = cvRandInt(&rng)%img->height;
/* Selects row span of the input array: arr(start_row:delta_row:end_row,:)
(end_row is not included into the span). */
//CVAPI(CvMat*) cvGetRows( const CvArr* arr, CvMat* submat,
// int start_row, int end_row,
// int delta_row CV_DEFAULT(1));
//得到某一行的值
cvGetRows( points, &point_chunk,
k*sample_count/cluster_count,
k == cluster_count - 1 ? sample_count :
(k+1)*sample_count/cluster_count );
cvRandArr( &rng, &point_chunk, CV_RAND_NORMAL,
cvScalar(center.x,center.y,0,0),
cvScalar(img->width/6, img->height/6,0,0) );
}
/* shuffle samples */
for( i = 0; i < sample_count/2; i++ )
{
CvPoint2D32f* pt1 = (CvPoint2D32f*)points->data.fl +
cvRandInt(&rng)%sample_count;
CvPoint2D32f* pt2 = (CvPoint2D32f*)points->data.fl +
cvRandInt(&rng)%sample_count;
CvPoint2D32f temp;
CV_SWAP( *pt1, *pt2, temp );
}
cvKMeans2( points, cluster_count, clusters,
cvTermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER,
10, 1.0 ));
cvZero( img );
for( i = 0; i < sample_count; i++ )
{
CvPoint2D32f pt = ((CvPoint2D32f*)points->data.fl)[i];
int cluster_idx = clusters->data.i[i];
cvCircle( img, cvPointFrom32f(pt), 2,
color_tab[cluster_idx], CV_FILLED );
}
cvReleaseMat( &points );
cvReleaseMat( &clusters );
cvShowImage( "clusters", img );
int key = cvWaitKey(0);
if( key == 27 ) // 'ESC'
break;
}
}
代码解读
对图像进行颜色聚类
typedef struct colorCluster
{
Scalar color;
double max_distance = 0;
}colorCluster;
std::vector<colorCluster> colorClusterByKMeans(cv::Mat src, int nCuster)
{
Mat samples = Mat(src.size().width*src.size().height, 1, CV_32FC3);
int k = 0;
for (int i = 0; i < src.rows; i++) {
for (int j = 0; j < src.cols; j++) {
//std::cout << src.at<cv::Vec3b>(i, j)[0] << std::endl;
samples.at<cv::Vec3f>(k, 0)[0] = src.at<cv::Vec3b>(i, j)[0];
samples.at<cv::Vec3f>(k, 0)[1] = src.at<cv::Vec3b>(i, j)[1];
samples.at<cv::Vec3f>(k, 0)[2] = src.at<cv::Vec3b>(i, j)[2];
++k;
}
}
Mat labels;
Mat centers;
kmeans(samples, nCuster, labels, TermCriteria(CV_TERMCRIT_ITER, 10, 1.0), 3, KMEANS_RANDOM_CENTERS, centers);
std::vector<colorCluster> results;
results.resize(nCuster);
for (int i = 0; i < centers.rows; i++)
{
for (int j = 0; j < centers.cols; j++)
{
results[i].color[j] = centers.at<float>(i, j);
}
}
k = 0;
Mat img(src.size(), CV_8UC3);
std::vector<int> counter;
counter.resize(nCuster);
for (int i = 0; i < src.rows; i++) {
for (int j = 0; j < src.cols; j++) {
int clusterIdx = labels.at<int>(k++, 0);
counter[clusterIdx] ++;
//circle(img, {j,i}, 2, colorTab[clusterIdx], CV_FILLED, CV_AA);
circle(img, { j,i }, 2, results[clusterIdx].color, CV_FILLED, CV_AA);
cv::Vec3b data = src.at<cv::Vec3b>(i, j);
cv::Scalar color = results[clusterIdx].color;
double dist = (data[0] - color[0]) * (data[0] - color[0])
+ (data[1] - color[1]) * (data[1] - color[1])
+ (data[2] - color[2]) * (data[2] - color[2]);
if (dist > results[clusterIdx].max_distance)
{
results[clusterIdx].max_distance = dist;
}
}
}
for(int i = nCuster - 1; i >= 0; i--)
{
std::cout << counter[i] << std::endl;
if (counter[i] < 10000)
{
results.erase(results.begin() + i);
}
}
cv::imshow("result", img);
return results;
}
代码解读
该函数返回颜色的聚类中心和聚类范围。
在此基础上其实相当已经实现了图像分割技术的基本应用,并且还有一种不同的图像分割方式值得探索。
假设图像的背景由两种不同的颜色组成,在这种情况下可以通过选择一个非目标区域特征来进行聚类分析,并提取图像中各类别区域的颜色特征以及这些区域所占的比例范围,在此基础上建立统一的颜色分类标准以实现目标物体与环境的有效分离。
void threshByBgColorCluster(cv::Mat src, cv::Mat &dst, std::vector<colorCluster> cluster)
{
dst = cv::Mat::zeros(src.size(), CV_8UC1);
for (int i = 0; i < src.rows; i++) {
for (int j = 0; j < src.cols; j++)
{
bool flag = false;
for(int k=0; k<cluster.size(); k++)
{
cv::Vec3b data = src.at<cv::Vec3b>(i, j);
cv::Scalar color = cluster[k].color;
double dist = (data[0] - color[0]) * (data[0] - color[0])
+ (data[1] - color[1]) * (data[1] - color[1])
+ (data[2] - color[2]) * (data[2] - color[2]);
if(dist < cluster[k].max_distance)
{
flag = true;
break;
}
}
if(flag == false)
{
dst.at<uchar>(i, j) = 255;
}
}
}
cv::imshow("dst", dst);
cv::waitKey(0);
}
代码解读
全部评论 (0)
还没有任何评论哟~
