zoukankan      html  css  js  c++  java
  • kmeans聚类分析、随机数生成、源代码的修改(保证随机点在500*500矩阵内)(OpenCV案例源码kmeans.cpp解读)

    官方源代码中有一点瑕疵,高斯分布产生的随机点points的坐标可能出现负数或大于500的数。如横坐标均值是0,方差是25,那么横坐标随机值中会出现负数。

    修改了两处:随机数生成种子是时间、随机点points坐标保证在500*500以内。

    【知识点1】聚类函数

    double kmeans( InputArray data, int K, InputOutputArray bestLabels,TermCriteria criteria, int attempts,int flags, OutputArray centers = noArray() );
    data——原始数据集,行为样本列为特征。
    K——聚类的类别数。
    bestLabels——每个样本所属的类别标签,从0开始,样本数行1列的矩阵。
    criteria——迭代终止条件,TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 1.0),10次迭代、期望准确率1.0
    attempts——运行kmeans的次数,取结果最好的那次聚类为最终的聚类。
    flags——聚类中心初始化
        KMEANS_RANDOM_CENTERS 随机初始化
        KMEANS_USE_INITIAL_LABELS 第一次初始化使用用户设定的,之后使用随机的(random or semi-random centers)。
        KMEANS_PP_CENTERS 算法kmeans++的center
    centers——最终最优的聚类的中心。

    返回值——点的紧凑程度,越小越紧凑。

    【知识点2】随机数生成

    RNG rng(getTickCount());  //随时间每次产生的随机数都不同(起始种子不同),如果RNG rng(12345); 每次运行,随机数都一样。
    rng.uniform(2, 6); //指定范围内产生随机数,左闭右开。最多5类。
    rng.fill(pointChunk, RNG::NORMAL, Scalar(mean.x, mean.y), Scalar(25, 25)); //高斯分布RNG::NORMAL,等效下句代码
    randn(pointChunk, Scalar(mean.x, mean.y), Scalar(25, 25)); //高斯分布的随机数,被填充的矩阵、均值、方差
    randShuffle(points); //打乱points矩阵中元素(坐标)顺序

    其他参考https://blog.csdn.net/qq_33485434/article/details/78980587

    【难理解点讲解】pointChunk等分points,每一份的数据均值随机[25,475)、方差25

    Mat pointChunk = points.rowRange(k*sampleCount / clusterCount, k == clusterCount - 1 ? sampleCount : (k + 1)*sampleCount / clusterCount);

    案例中这句难理解,Mat a=b;是浅拷贝,a、b指向同一内存,其一改变,都改变。所以操作pointChunk,就是处理points。

    样本被等分成“sampleCount / clusterCount”份,当然有可能不完美等分,前n-1份一定是个数相同的,最后一份个数会少于其他。

    所以前n-1份,points.rowRange(k*sampleCount / clusterCount, (k + 1)*sampleCount / clusterCount); ,即第k份的头和第k份的尾。

    最后一份,points.rowRange(k*sampleCount / clusterCount, sampleCount);,即最后一份的头和所有样本的尾。

    #include<opencv2opencv.hpp>
    #include<iostream>
    using namespace cv;
    using namespace std;
    
    int main()
    {
        //5种颜色,因为最多分5类
        Scalar colorTab[] =
        {
            Scalar(0, 0, 255),
            Scalar(0, 255, 0),
            Scalar(255, 100, 100),
            Scalar(255, 0, 255),
            Scalar(0, 255, 255)
        };
    
        Mat img(500, 500, CV_8UC3);//3通道
        RNG rng(getTickCount()); //随时间每次产生的随机数都不同(起始种子不同),如果RNG rng(12345); 每次运行,随机数都一样。
    
        for (;;)
        {
            int k, clusterCount = rng.uniform(2, 6);//指定范围内产生随机数,左闭右开。最多5类。
            int i, sampleCount = rng.uniform(1, 1001);
            Mat points(sampleCount, 1, CV_32FC2), labels;//样本点,一列,2通道
            clusterCount = MIN(clusterCount, sampleCount);//分类数与样本数的最小值
            std::vector<Point2f> centers;//中心坐标容器
    
            //生成随机点,pointChunk是points被等分的局部矩阵,每一份均值随机[25,475),方差25
            for (k = 0; k < clusterCount; k++)
            {
                //浅拷贝,pointChunk与points同一内存
                Mat pointChunk = points.rowRange(k*sampleCount / clusterCount, k == clusterCount - 1 ? sampleCount : (k + 1)*sampleCount / clusterCount);
                Point mean;//均值
                mean.x = rng.uniform(0+25, img.cols-25);//由于randn中方差是25,所以均值随机范围增减了25,防止pointChunk点坐标出现负数或大于500的数
                mean.y = rng.uniform(0+25, img.rows-25);
                randn(pointChunk, Scalar(mean.x, mean.y), Scalar(25, 25));//高斯分布的随机数,被填充的矩阵、均值、方差
                //rng.fill(pointChunk, RNG::NORMAL, Scalar(mean.x, mean.y), Scalar(25, 25));//等效上句代码
            }
            randShuffle(points);//打乱points内容
            //聚类分析
            double compactness = kmeans(points, clusterCount, labels,TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 1.0),3, KMEANS_PP_CENTERS, centers);
            //黑色图
            img = Scalar::all(0);//img三通道图,值全是0,黑色
            //画样本点
            for (i = 0; i < sampleCount; i++)
            {
                int clusterIdx = labels.at<int>(i);//具体样本所属类别的标识
                Point ipt = points.at<Point2f>(i);//样本点
                circle(img, ipt, 2, colorTab[clusterIdx],FILLED);//画圆,图、圆心、半径、颜色、实心填充
            }
            //画圆
            for (i = 0; i < (int)centers.size(); ++i)
            {
                Point2f c = centers[i];
                circle(img, c, 40, colorTab[i], 2, LINE_AA);//画圆,图、圆心、半径、颜色、线条粗细与细腻程度(16连通)
            }
            cout << "Compactness: " << compactness << endl;//输出返回值,紧凑度,越小越紧凑
    
            imshow("clusters", img);
    
            char key = (char)waitKey();
            if (key == 27 || key == 'q' || key == 'Q') // 'ESC'
                break;
        }
    
        return 0;
    }
  • 相关阅读:
    Vuex ~ 初识
    Vue 2.0 生命周期-钩子函数理解
    vue利用watch侦听对象具体的属性 ~ 巧用计算属性computed做中间层
    Elements in iteration expect to have 'v-bind:key' directives.' 提示错误如何解决?
    微信小程序-如何自定义导航栏(navigationStyle)?
    微信小程序~触摸相关事件(拖拽操作、手势识别、多点触控)
    [Java] Collections
    [Java] Map / HashMap
    [Data Structure] 红黑树( Red-Black Tree )
    [Data Structure] 二叉搜索树(Binary Search Tree)
  • 原文地址:https://www.cnblogs.com/xixixing/p/12455316.html
Copyright © 2011-2022 走看看