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('数据处理完毕!');
}

代码解释

  1. 函数定义: k_means函数接受图像宽度、高度、波段数、迭代次数、类别数、阈值、输入文件名和输出文件名作为参数。
  2. 内存分配: 代码首先为存储类中心、距离、像素数据等变量分配内存。
  3. 文件读取: 读取输入图像数据到pData数组中。
  4. 初始化类中心: 选择图像对角线上的点作为初始类中心。
  5. 迭代计算: 进行迭代计算,直到满足停止条件(达到最大迭代次数或误差小于阈值)。
    • 计算每个像素点到各个类中心的距离。
    • 将像素点分配到距离最近的类。
    • 更新类中心。
  6. 结果输出: 将分类结果写入输出文件。
  7. 内存释放: 释放所有动态分配的内存。

分类结果颜色问题

函数中,分类结果存储在array数组中,每个像素点的值代表其所属的类别。输出时,每个类别被赋予一个颜色,但这只是为了方便可视化,分类结果本身只是一个数字标识。

如果需要将分类结果可视化,可以使用其他软件进行渲染,例如:

  • ENVI/IDL: 使用颜色表对分类结果进行渲染。
  • Python: 使用matplotlib库绘制分类结果。

希望这篇博客文章能够帮助您理解如何使用C++实现无监督K均值聚类算法进行图像分类。

C++实现无监督K均值聚类算法进行图像分类

原文地址: https://www.cveoy.top/t/topic/jnXn 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录