Edited at

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

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