2

バイナリ イメージ (黒と白のピクセル) があり、白のピクセルを互いの距離に応じてグループ (オブジェクト) にクラスター化し、各クラスターの重心を取得したいと考えています。

これは、私が取り組む必要がある画像の例です。

画像

(紫色のフレーム)

クラスタリング アプローチが探している結果を提供するかどうかを確認したいと思います。つまり、価値があることを知る前に、自分でアルゴリズムを実装することを避けようとしています。
OpenCV には、必要なことを行う方法がありますか?

4

2 に答える 2

2

これはかなり古いことは知っていますが、この回答が役立つかもしれません。


要素セットを等価クラスに分割するpartitionを使用できます。

等価クラスを、指定されたユークリッド距離内のすべてのポイントとして定義できます。これは、ラムダ関数 (C++11) またはファンクター (コード内の両方の例を参照) のいずれかです。

この画像から始めて (紫色の境界線を手動で削除しました):

ここに画像の説明を入力

私が得た20のユークリッド距離を使用して:

ここに画像の説明を入力

ユークリッド距離内にあるすべての白いピクセルが同じクラスター (同じ色) に割り当てられていることがわかります。円は、各クラスターの重心を表します。

コード:

#include <opencv2\opencv.hpp>
#include <vector>
#include <algorithm>

using namespace std;
using namespace cv;

struct EuclideanDistanceFunctor
{
    int _dist2;
    EuclideanDistanceFunctor(int dist) : _dist2(dist*dist) {}

    bool operator()(const Point& lhs, const Point& rhs) const
    {
        return ((lhs.x - rhs.x)*(lhs.x - rhs.x) + (lhs.y - rhs.y)*(lhs.y - rhs.y)) < _dist2;
    }
};

int main()
{
    // Load the image (grayscale)
    Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);

    // Get all non black points
    vector<Point> pts;
    findNonZero(img, pts);

    // Define the distance between clusters
    int euclidean_distance = 20;

    // Apply partition 
    // All pixels within the the given distance will belong to the same cluster

    vector<int> labels;

    // With functor
    //int n_labels = partition(pts, labels, EuclideanDistanceFunctor(euclidean_distance));

    // With lambda function
    int th2 = euclidean_distance * euclidean_distance;
    int n_labels = partition(pts, labels, [th2](const Point& lhs, const Point& rhs) {
        return ((lhs.x - rhs.x)*(lhs.x - rhs.x) + (lhs.y - rhs.y)*(lhs.y - rhs.y)) < th2;
    });


    // Store all points in same cluster, and compute centroids
    vector<vector<Point>> clusters(n_labels);
    vector<Point> centroids(n_labels, Point(0,0));

    for (int i = 0; i < pts.size(); ++i)
    {
        clusters[labels[i]].push_back(pts[i]);
        centroids[labels[i]] += pts[i];
    }
    for (int i = 0; i < n_labels; ++i)
    {
        centroids[i].x /= clusters[i].size();
        centroids[i].y /= clusters[i].size();
    }

    // Draw results

    // Build a vector of random color, one for each class (label)
    vector<Vec3b> colors;
    for (int i = 0; i < n_labels; ++i)
    {
        colors.push_back(Vec3b(rand() & 255, rand() & 255, rand() & 255));
    }

    // Draw the points
    Mat3b res(img.rows, img.cols, Vec3b(0, 0, 0));
    for (int i = 0; i < pts.size(); ++i)
    {
        res(pts[i]) = colors[labels[i]];
    }

    // Draw centroids
    for (int i = 0; i < n_labels; ++i)
    {
        circle(res, centroids[i], 3, Scalar(colors[i][0], colors[i][1], colors[i][2]), CV_FILLED);
        circle(res, centroids[i], 6, Scalar(255 - colors[i][0], 255 - colors[i][1], 255 - colors[i][2]));
    }


    imshow("Clusters", res);
    waitKey();

    return 0;
}
于 2015-11-21T12:42:13.420 に答える
0

画像内のクラスター数がわかっている場合は、OpenCV に実装されているK-Means アルゴリズムを使用できます。

それに加えて、このタスクに対する他の簡単な解決策は知りません。このクラスタリングを自分で実装したい場合は、このペーパーが役立ちます。これは、固定数のクラスターを持たない k-means の適応に関するものです。

于 2013-10-01T13:06:21.297 に答える