[英]opencv: how to clusterize by angle using kmeans()
問題是,如何按角度對某些單位進行聚類? 問題在於,kmeans 基於歐幾里得空間距離的概念運行,並且不知道角度的周期性。 因此,要使其工作,需要將角度轉換為歐幾里得空間,但要保持以下正確:
這意味着,90 和 -90 是遠值,180 和 -180 是相同的,170 和 -170 是接近的(角度從左上到右:0 - +180,從左下到右:0 - -180)
我嘗試使用各種sin()
函數,但它們都有第 1 點和第 2 點中提到的問題。大多數觀點是sin(x * 0.5f)
但也有問題,即 180 和 -180 是歐幾里得空間中的遠值。
我找到的解決方案是將角度轉換為圓上的點並將它們輸入 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]);
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.