LoginSignup
4
2

More than 3 years have passed since last update.

OpenCVでのK-means 法による2次元座標クラスタリング

Last updated at Posted at 2019-06-23

C++ OpenCVのkmeansメソッドを使用し、K-means法の使用例でよくある
2次元座標上でのクラスタリングのサンプルを作成したので、情報を残しておきます

以下は、500×500の2次元座標上にある任意の8点に対しk-meansにより3グループに分けるサンプルです

実行環境
OpenCV 2.X

doKmeans.cpp

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <list>

using namespace std;
using namespace cv;

//2次元のクラスタリング
void main()
{
    //プロット用画像を生成
    Mat imgMark = Mat(500, 500, CV_8UC3);

    //クラスタリング表現の色(3パターン)
    Scalar colorTab[] =
    {
        Scalar(255,100,100),
        Scalar(255,0,255),
        Scalar(0,255,255)
    };

    //クラスタリング対象ポイントを設定
    int pointCount = 8; //8ポイント分確保
    Mat points(pointCount, 1, CV_32FC2);

    //クラスタリング対象2次元座標を設定
    points.at<Point2f>(0) = Point2f(100, 200);
    points.at<Point2f>(1) = Point2f(200, 300);
    points.at<Point2f>(2) = Point2f(350, 250);
    points.at<Point2f>(3) = Point2f(450, 150);
    points.at<Point2f>(4) = Point2f(200, 100);
    points.at<Point2f>(5) = Point2f(200, 200);
    points.at<Point2f>(6) = Point2f(420, 350);
    points.at<Point2f>(7) = Point2f(450, 450);

    //クラスタリング数を定義
    int clusterCount = 3;
    vector<Point*> centerPointList(clusterCount);

    //ラベルごとのポイントリストのインスタンスを確保
    vector< vector<Point*>*> labelPointList;
    for (int i = 0; i < clusterCount; i++)
    {
        labelPointList.push_back(new vector<Point*>());
    }

    //K-means実施
    Mat centers;    //各グループの中心座標を受け取る変数
    Mat labels;     //各グルーピングごとのポイントを受け取る変数
    kmeans(points
        , clusterCount
        , labels
        , TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 1.0)
        , 3
        , KMEANS_PP_CENTERS
        , centers);

    //各ラベルのポイントを取得(各Labelがrows)
    for (int i = 0; i < points.rows; i++)
    {
        //分類のインデックスを取得
        int labelIndex = labels.at<int>(i);
        //分類元の座標を取得
        Point ipt = points.at<Point2f>(i);

        vector<Point*>* pointList = labelPointList[labelIndex];

        pointList->push_back(new Point(ipt.x, ipt.y));

        //位置に分類を色別に表示
        circle(imgMark, ipt, 10, colorTab[labelIndex], 2, 4);
    }

    //各ラベルの中心を取得(各Labelがrows)
    double sse = 0;
    for (int labelIndex = 0; labelIndex < centers.rows; labelIndex++) {

        //中心座標を取得
        int centersX = centers.at<float>(labelIndex, 0);
        int centersY = centers.at<float>(labelIndex, 1);

        //位置に分類を色別に表示
        Point* centerPoint = new Point(centersX, centersY);
        circle(imgMark, *centerPoint, 20, colorTab[labelIndex], 8, 8);
    }

    //クラスタリング結果を表示
    namedWindow("Kmeans", WINDOW_NORMAL);
    imshow("Kmeans", imgMark);
    waitKey(0);
    destroyAllWindows();
}

上記を実行すると以下のように各グループごとに色わけされた結果が表示されます
小さい円はグルーピング対象の座標の位置を示し、大きい円は各グループの中心位置を示しています

image.png

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2