C++实现无监督K均值聚类算法进行图像分类
C++实现无监督K均值聚类算法进行图像分类
本篇博客介绍了如何使用C++实现无监督K均值聚类算法对图像进行分类。
代码实现
以下是C++实现K均值聚类算法的核心代码:
void CUnsupervised::k_means(int lWidth, int lHeight, int Bands, int inititerationtime, int clsnumber, double thresholdc, CString filename, CString fileoutname)
{
// 设置循环变量
int i, j, k, l;
int const MAX = 10000;
// classp用来存放类中心的值
double *classp = new double[Bands * clsnumber];
// 像元与中心的距离
float *Distance = new float[clsnumber];
BYTE *array = new BYTE[lHeight * lWidth];
// 设置指向文件的指针,对分类后的结果进行储存
CFile f, g;
// 对于无法打开文件的情况进行提醒
if (!f.Open(filename, CFile::modeRead))
{
AfxMessageBox('cant open the file!');
}
if (!g.Open(fileoutname, CFile::modeCreate | CFile::modeWrite))
{
AfxMessageBox('cant open the file!');
return;
}
// LPSTR为一个指向以NULL('\0')结尾的32位ANSI(扩展的ASCII编码)字符数组指针
BYTE **pData = new BYTE *[Bands];
for (i = 0; i < Bands; i++)
{
pData[i] = new BYTE[lHeight * lWidth];
}
BYTE **pDatak = new BYTE *[Bands];
for (i = 0; i < Bands; i++)
{
pDatak[i] = new BYTE[lHeight * lWidth];
}
// HeapAlloc(hHeap句柄,dwFlags可选参数,dwBytes字节数)
for (i = 0; i < Bands; i++)
{
f.Read(pData[i], lHeight * lWidth);
}
AfxMessageBox('初始类中心的选择');
// 初始类中心
int a = 0;
for (i = 0; i < Bands * clsnumber; i = i + Bands)
{
// a代表第a个样本
a = i / Bands;
for (j = 0; j < Bands; j++)
{
// pData[j]代表第j个波段的指向,聚类中心选取为对角线上的clsnumber个点,按照样本点进行存放(band*N)
classp[i + j] = pData[j][lWidth * (lHeight / clsnumber) * a + (lWidth / clsnumber) * a];
}
}
AfxMessageBox('分类开始');
float err = 0;
int *num = new int[clsnumber];
// 存储各类像元数
for (i = 0; i < clsnumber; i++)
num[i] = 0;
float *sum = new float[clsnumber * Bands];
for (i = 0; i < clsnumber * Bands; i++)
sum[i] = 0;
for (i = 0; i < Bands; i++)
{
for (j = 0; j < lWidth * lHeight; j++)
pDatak[i][j] = pData[i][j];
}
// 迭代开始
for (l = 0; l < inititerationtime; l++)
{
float err1 = 0;
float err2 = 0;
err1 = err2;
err2 = 0;
for (i = 0; i < clsnumber; i++)
num[i] = 0;
for (i = 0; i < clsnumber * Bands; i++)
sum[i] = 0;
// 影像的遍历,寻找最
for (int data = 0; data < lHeight * lWidth; data++)
{
// 变量的初始化
for (int k = 0; k < clsnumber; k++)
Distance[k] = 0;
// 比较每个点与初始聚类中心的距离,classp是聚类中心,pDatak是所有像素点
for (int i = 0; i < clsnumber; i++)
for (int j = 0; j < Bands; j++)
Distance[i] = Distance[i] + fabs(float(pDatak[j][data]) - classp[Bands * i + j]);
// 求取最小距离对应的类别
int Min_value = MAX;
int Min = MAX;
for (i = 0; i < clsnumber; i++)
{
if (Distance[i] < Min_value)
{
Min_value = Distance[i];
Min = i;
}
}
// 误差累积和求差
err2 = err2 + Min_value;
err = fabs(err1 - err2);
// 对应最相近的类像元数累积
num[Min]++;
// 对应类像元灰度累积
for (i = 0; i < Bands; i++)
sum[Min * Bands + i] = sum[Min * Bands + i] + pDatak[i][data];
// array即为分类的结果
array[data] = Min + 1;
}
// 计算新的聚类中心,求均值
for (i = 0; i < Bands * clsnumber; i = i + Bands)
{
for (j = 0; j < Bands; j++)
classp[i + j] = (double)sum[i + j] / num[i / Bands];
}
// 小于阈值,跳出循环
if (err <= (thresholdc / 100))
break;
}
// 进行图像的写入
g.Write(array, lHeight * lWidth);
f.SeekToBegin();
// delete[] iGrayValue;
delete[] array;
delete[] classp;
delete[] Distance;
delete[] sum;
delete[] num;
for (i = 0; i < Bands; i++)
delete[] pData[i];
delete[] pData;
for (i = 0; i < Bands; i++)
delete[] pDatak[i];
delete[] pDatak;
f.Close();
g.Close();
AfxMessageBox('数据处理完毕!');
}
代码解释
- 函数定义:
k_means函数接受图像宽度、高度、波段数、迭代次数、类别数、阈值、输入文件名和输出文件名作为参数。 - 内存分配: 代码首先为存储类中心、距离、像素数据等变量分配内存。
- 文件读取: 读取输入图像数据到
pData数组中。 - 初始化类中心: 选择图像对角线上的点作为初始类中心。
- 迭代计算: 进行迭代计算,直到满足停止条件(达到最大迭代次数或误差小于阈值)。
- 计算每个像素点到各个类中心的距离。
- 将像素点分配到距离最近的类。
- 更新类中心。
- 结果输出: 将分类结果写入输出文件。
- 内存释放: 释放所有动态分配的内存。
分类结果颜色问题
函数中,分类结果存储在array数组中,每个像素点的值代表其所属的类别。输出时,每个类别被赋予一个颜色,但这只是为了方便可视化,分类结果本身只是一个数字标识。
如果需要将分类结果可视化,可以使用其他软件进行渲染,例如:
- ENVI/IDL: 使用颜色表对分类结果进行渲染。
- Python: 使用
matplotlib库绘制分类结果。
希望这篇博客文章能够帮助您理解如何使用C++实现无监督K均值聚类算法进行图像分类。
原文地址: https://www.cveoy.top/t/topic/jnXn 著作权归作者所有。请勿转载和采集!