您的位置:首页 > 其它

k-means提取图像主色(手写)

2017-03-18 09:53 218 查看
一、k-means算法
  k-means算法是一种简单有效的聚类算法。根据制定的类别数k,将所给数据集,以一定的距离(用户自定义距离,如本文采用是RGB图像每点的欧式距离)相近程度进行类别划分,可以理解为一种求质心的聚类方式。
  算法基本流程如下:
1、随机产生k个聚类中心(也可用户指定)。计算每个样本到各个聚类中心的距离。将样本归类到相应最接近的类别中。
2、计算每个样本的算数平均数,求出每一类中离平均数最近的点记为该类新的中心。
3、判断所有类别中中心点有没有发生变化,如果中心点改变,则再次以该中心点对所有数据聚类,直到所有的中心点都不改变为止,算法结束。

二、c++实现
#define CLUST_NUM 5
struct Node
{
CvPoint point;
CvScalar color;
};

struct Clust
{
Node center;
vector<Node> buff;
};

void DCD(IplImage* src)
{
IplImage* src = NULL;
src = cvLoadImage("test.jpg");

if(!src)
{
return;
}

int width = src->width;
int height = src->height;

// 产生随机种子
CvPoint point[CLUST_NUM];
CvRNG rng(cvGetTickCount());
for (int i = 0; i< CLUST_NUM; i++)
{
point[i].x = cvRandInt(&rng) % width;
point[i].y = cvRandInt(&rng) % height;
}

//分类簇
Clust v[CLUST_NUM];

//初始化
for (int i = 0; i < CLUST_NUM; i++)
{
v[i].center.point = point[i];
v[i].center.color = cvGet2D(src, v[i].center.point.y, v[i].center.point.x);

cout << point[i].x << ":" << point[i].y << endl;

Node node;
node.point = v[i].center.point;
node.color = v[i].center.color;
v[i].buff.push_back(node);
}

do
{
for (int rows = 0; rows < height; rows++)
{
uchar* ptr = (uchar*)src->imageData + src->widthStep*rows;
for (int cols = 0; cols < width; cols++)
{
int b, g, r;
b = ptr[cols*src->nChannels + 0];
g = ptr[cols*src->nChannels + 1];
r = ptr[cols*src->nChannels + 2];

//计算每个像素到达每个簇的中心距离
double dis[CLUST_NUM] = { 0.0 };
for (int i = 0; i < CLUST_NUM; i++)
{
dis[i] = (v[i].center.color.val[0] - b)*(v[i].center.color.val[0] - b) +
(v[i].center.color.val[1] - g)*(v[i].center.color.val[1] - g) +
(v[i].center.color.val[2] - r)*(v[i].center.color.val[2] - r);
dis[i] = sqrt(dis[i]);
}

//获取该像素点离的最近的簇的编号
int minDisClustId = 0;
int i = 0;
for (i = 1; i < CLUST_NUM; i++)
{
if (dis[
97b8
i] < dis[minDisClustId])
{
minDisClustId = i;
}

}

Node node;
node.color.val[0] = b;
node.color.val[1] = g;
node.color.val[2] = r;

node.point.x = cols;
node.point.y = rows;

v[minDisClustId].buff.push_back(node);
}
}

for (int i = 0; i < CLUST_NUM; i++)
{
double avgR = 0.0;
double avgG = 0.0;
double avgB = 0.0;

//计算每一簇的平均RGB
vector<Node>::iterator it = v[i].buff.begin();
while (it != v[i].buff.end())
{
avgB += it->color.val[0];
avgG += it->color.val[1];
avgR += it->color.val[2];
it++;
}

avgB /= v[i].buff.size();
avgG /= v[i].buff.size();
avgR /= v[i].buff.size();

//获得距离平均RGB最近的点
it = v[i].buff.begin();

double curDis = 0.0;
double minDis = 0.0;
int curId = 0;
int minId = 0;

while (it != v[i].buff.end())
{
if (it == v[i].buff.begin())
{
minDis = (it->color.val[0] - avgB)*(it->color.val[0] - avgB) +
(it->color.val[1] - avgG)*(it->color.val[1] - avgG) +
(it->color.val[2] - avgR)*(it->color.val[2] - avgR);

minDis = sqrt(minDis);
minId = 0;
it++;
}
else
{
curDis = (it->color.val[0] - avgB)*(it->color.val[0] - avgB) +
(it->color.val[1] - avgG)*(it->color.val[1] - avgG) +
(it->color.val[2] - avgR)*(it->color.val[2] - avgR);
curDis = sqrt(curDis);

if (curDis < minDis)
{
minDis = curDis;
minId = it - v[i].buff.begin();
}
it++;
}
}
//获得新的每一簇的中心点
v[i].center.point = v[i].buff.at(minId).point;
v[i].center.color = v[i].buff.at(minId).color;
}

//检查终止条件 是否所有的中心点都没有改变
int flg = 0;
for (int i = 0; i < CLUST_NUM; i++)
{
for (int j = 0; j < CLUST_NUM; j++)
{
if (point[i].x == v[j].center.point.x &&
point[i].y == v[j].center.point.y)
{
flg++;
break;
}
}
}

if (flg == CLUST_NUM)
{
break;
}

//清空每一簇的点 保留中心点
for (int i = 0; i < CLUST_NUM; i++)
{
cout << v[i].center.point.x << ":" << v[i].center.point.y << "size:" << v[i].buff.size() << endl;
v[i].buff.clear();

//中心点进入对应的簇
Node node;
node.point = v[i].center.point;
node.color = v[i].center.color;
v[i].buff.push_back(node);

point[i] = v[i].center.point;
}
cout << "*" << endl;
} while (1);

IplImage* dst = NULL;
IplImage* lab = NULL;

dst = cvCreateImage(cvGetSize(src), src->depth, src->nChannels);
lab = cvCreateImage(cvGetSize(src), src->depth, src->nChannels);

if(!dst ||!lab)
{
reurn;
}

for (int i = 0; i < CLUST_NUM; i++)
{
CvScalar color;
color = v[i].center.color;
cvSetImageROI(dst, cvRect(i*src->width / CLUST_NUM, 0, src->width / CLUST_NUM, src->height));
cvSet(dst, color);
cvResetImageROI(dst);

vector<Node>::iterator it = v[i].buff.begin();
while (it != v[i].buff.end())
{
cvSet2D(lab, it->point.y, it->point.x, v[i].center.color);
it++;
}

}
cvShowImage("src", src);
cvShowImage("dst", dst);
cvShowImage("lab", lab);

cvReleaseImage(&src);
cvReleaseImage(&dst);
cvReleaseImage(&lab);
cvWaitKey(0);
return;
}


三、程序结果





src: 原图像 lab: 划分区域 dst: 提取的颜色
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: