opencv:如何使用kmeans()按角度聚类

Posted

技术标签:

【中文标题】opencv:如何使用kmeans()按角度聚类【英文标题】:opencv: how to clusterize by angle using kmeans() 【发布时间】:2016-07-05 16:57:21 【问题描述】:

问题是,如何通过角度对某些单元进行聚类?问题是,kmeans 在欧几里得空间距离的概念上运行,不知道角度的周期性。因此,要使其工作,需要将角度转换为欧几里得空间,但要满足以下条件:

    近角是欧几里得空间中的近值; 远角在欧几里得空间中很远。

这意味着,90 和 -90 是遥远的值,180 和 -180 是相同的,170 和 -170 是接近的(角度从左上到右:0 - +180,从左下到右:0 - -180)

我尝试使用各种 sin() 函数,但它们都存在第 1 点和第 2 点中提到的问题。大多数观点是 sin(x * 0.5f),但也有问题,即 180 和 -180 在欧几里得空间中是遥远的值。

【问题讨论】:

最好先发布问题,然后将答案发布为...答案。 他们肯定需要添加发帖功能。 一点也不。这是一个问答网站。如果问题(连同答案)被认为是有价值的,它可以成为社区 wiki 的一部分。现在,对于像你这样的情况,只需考虑我在之前评论中描述的方式是正确的。 Umka,正确的方法是发布一个问题,然后写下你自己的答案。然后您可以接受自己的答案,或者,如果其他答案更好,您可以接受另一个答案。那是你的选择。这样做,您还可以通过对您的回答进行投票来获得代表 顺便说一句,有点冗长...但是干得好;D 【参考方案1】:

我找到的解决方案是将角度转换为圆上的点并将它们输入 kmeans。通过这种方式,我们可以比较点之间的距离,并且效果很好。

值得一提的重要事情。终止标准中的 Kmeans @eps 以您提供给 kmeans 的样本单位表示。在我们的示例中,最大距离点的距离为 200 个单位(2 * 半径)。这意味着拥有 1.0f 完全没问题。如果您在调用kmeans() 之前对您的样品使用cv::normalize(samples, samples, 0.0f, 1.0f),请适当调整您的@eps。像eps=0.01f 这样的东西在这里玩得更好。

享受吧!希望这对某人有所帮助。

static cv::Point2f angleToPointOnCircle(float angle, float radius, cv::Point2f origin /* center */)

        float x = radius * cosf(angle * M_PI / 180.0f) + origin.x;
        float y = radius * sinf(angle * M_PI / 180.0f) + origin.y;
        return cv::Point2f(x, y);


static std::vector<std::pair<size_t, int> > biggestKmeansGroup(const std::vector<int> &labels, int count)

        std::vector<std::pair<size_t, int> > indices;
        std::map<int, size_t> l2cm;

        for (int i = 0; i < labels.size(); ++i)
                l2cm[labels[i]]++;

        std::vector<std::pair<size_t, int> > c2lm;
        for (std::map<int, size_t>::iterator it = l2cm.begin(); it != l2cm.end(); it++)
                c2lm.push_back(std::make_pair(it->second, it->first)); // count, group

        std::sort(c2lm.begin(), c2lm.end(), cmp_pair_first_reverse);
        for (int i = 0; i < c2lm.size() && count-- > 0; i++)
                indices.push_back(c2lm[i]);
        return indices;


static void sortByAngle(std::vector<boost::shared_ptr<Pair> > &group,
                        std::vector<boost::shared_ptr<Pair> > &result)

        std::vector<int> labels;
        cv::Mat samples;

        /* Radius is not so important here. */
        for (int i = 0; i < group.size(); i++)
                samples.push_back(angleToPointOnCircle(group[i]->angle, 100, cv::Point2f(0, 0)));

        /* 90 degrees per group. May be less if you need it. */
        static int PAIR_MAX_FINE_GROUPS = 4;

        int groupNr = std::max(std::min((int)group.size(), PAIR_MAX_FINE_GROUPS), 1);
        assert(group.size() >= groupNr);
        cv::kmeans(samples.reshape(1, (int)group.size()), groupNr, labels,
                   cvTermCriteria(CV_TERMCRIT_EPS/* | CV_TERMCRIT_ITER*/, 30, 1.0f),
                   100, cv::KMEANS_RANDOM_CENTERS);

        std::vector<std::pair<size_t, int> > biggest = biggestKmeansGroup(labels, groupNr);
        for (int g = 0; g < biggest.size(); g++) 
                for (int i = 0; i < group.size(); i++) 
                        if (labels[i] == biggest[g].second)
                                result.push_back(group[i]);
                
        

【讨论】:

以上是关于opencv:如何使用kmeans()按角度聚类的主要内容,如果未能解决你的问题,请参考以下文章

Opencv:'centers'参数如何在 cv::kmeans 中工作?

kmeans用于图像聚类

如何在opencv中访问特定的kmeans集群

Python,OpenCV中的K均值聚类——K-Means Cluster

如何使用 KMeans 进行距离聚类

如何使用 Sklearn Kmeans 对稀疏数据进行聚类