C++ 实现Kmeans对图片颜色的聚类

基于opencv

#include<iostream>
#include<stdio.h>  
#include<string>  
#include<math.h>   
#include<vector>  
#include<ctime>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;

typedef struct
{
	int x;
	int y;
	float b;
	float g;
	float r;
}Tuple;

bool Isequal(vector<Tuple> a, vector<Tuple> b)
{
	int s = a.size();
	for (int i = 0; i < s; i++)
	{
		if (a[i].x == b[i].x&&a[i].y == b[i].y&&a[i].r == b[i].r&&a[i].g == b[i].g&&a[i].b == b[i].b)
			continue;
		else
			return false;
	}
	return true;
}

int main()
{
	srand(time(0));

	Mat src = imread("E:/ni.jpg"); //图片地址
	imshow("input", src);
	int width = src.cols;
	int height = src.rows;
	int dims = src.channels();
	Tuple t;
	//Kmeans//
	vector<Tuple> sum_points;
	vector<Tuple> points;
	int K = 6;
	int temp_K = K;
	//图中所点存入sum_points
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			Vec3b rgb = src.at<Vec3b>(i, j);
			t.x = j;
			t.y = i;
			t.b = (float)(rgb[0]);
			t.g = (float)(rgb[1]);
			t.r = (float)(rgb[2]);
			sum_points.push_back(t);
		}
	}
	随机K个点//
	for (; temp_K > 0; temp_K--)
	{
		int w = rand() % (width);
		int h = rand() % (height);
		Vec3b rgb = src.at<Vec3b>(w, h);
		Tuple temppoint;
		temppoint.x = w;
		temppoint.y = h;
		temppoint.b = (float)(rgb[0]);
		temppoint.g = (float)(rgb[1]);
		temppoint.r = (float)(rgb[2]);
		points.push_back(temppoint);
	}
	// 记录label值
	vector<int> label(sum_points.size(), 0);
	// 每次更新迭代的质心点坐标
	vector<Tuple> new_points(points.size());
	// 记录迭代多少次
	int index = 0;
	do
	{
		if (index != 0)
		{
			for (int i = 0; i < points.size(); i++)
			{
				points[i].x = new_points[i].x;
				points[i].y = new_points[i].y;
				points[i].b = new_points[i].b;
				points[i].r = new_points[i].r;
				points[i].g = new_points[i].g;
			}

		}
		
		// 欧式距离判断并更新质心点位置
		// 1、欧式距离计算分类
		for (int j = 0; j < sum_points.size(); j++)
		{
			float min_distance = float(INT_MAX);
			for (int i = 0; i < points.size(); i++)
			{
				float distance = sqrt((float)pow((sum_points[j].b - points[i].b), 2) + pow((sum_points[j].g - points[i].g), 2) + pow((sum_points[j].r - points[i].r), 2));
				if (distance < min_distance)
				//if (distance < 1)
				{
					min_distance = distance;
					label[j] = i;
				}
			}
		}

		// 2、更新质心的坐标点(通过label值)
		// 定义k类分别的点值变量
		vector<int> k(points.size(), 0);
		vector<float> b(points.size(), 0.0);
		vector<float> g(points.size(), 0.0);
		vector<float> r(points.size(), 0.0);
		for (int i = 0; i < label.size(); i++)
		{
			for (int j = 0; j < points.size(); j++)
			{
				// 找到对应的类别进行后续的质心坐标运算
				if (j == label[i])
				{
					k[j]++;
					b[j] += sum_points[i].b;
					g[j] += sum_points[i].g;
					r[j] += sum_points[i].r;
					break;
				}
			}
		}

		// 更新坐标
		for (int i = 0; i < points.size(); i++)
		{
			b[i] /= k[i];
			g[i] /= k[i];
			r[i] /= k[i];
		}

		// 质心坐标值填补
		for (int i = 0; i < points.size(); i++)
		{
			Tuple temp;
			temp.x = 0;
			temp.y = 0;
			temp.b = b[i];
			temp.g = g[i];
			temp.r = r[i];
			new_points[i] = temp;
		}
		index++;
	} while (!Isequal(points,new_points));
	

	Mat result = Mat::zeros(src.size(), CV_8UC3);

	for (int i = 0; i < sum_points.size(); i++)
	{
		sum_points[i].b = points[label[i]].b;
		sum_points[i].g = points[label[i]].g;
		sum_points[i].r = points[label[i]].r;
	}
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			result.at<Vec3b>(i, j)[0] = sum_points[j+i*width].b;
			result.at<Vec3b>(i, j)[1] = sum_points[j + i * width].g;
			result.at<Vec3b>(i, j)[2] = sum_points[j + i * width].r;
		}
	}


	imshow("kmeans-demo", result);
	waitKey(0);

	return 0;
}

 

菜鸡代码,欢迎讨论。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值