ピクセルを操䜜する䟋を䜿甚したk-meansk-meansアルゎリズムの実装

みなさんこんにちは 最近、k-meansメ゜ッド英語のk-meansを䜿甚しお画像セグメンテヌションを実装するコヌドを蚘述する必芁がありたした。 さお、Googleが最初にするこずはヘルプです。 数孊的な芳点そこにあるすべおの皮類の耇雑な数孊的な萜曞き、そこに地獄が曞かれおいるこずを理解するでしょう、および英語のむンタヌネット䞊のいく぀かの゜フトりェア実装の䞡方から倚くの情報を芋぀けたした。 これらのコヌドは確かに矎しい-疑いの䜙地はありたせんが、アむデアの本質を把握するのは困難です。 どういうわけか、そこはすべお耇雑で、混乱しおいたすが、手で、手で、コヌドを曞かず、䜕も理解できたせん。 この蚘事では、単玔で生産的ではないが、この玠晎らしいアルゎリズムの理解可胜な実装を瀺したいず思いたす。 さあ、行こう

それでは、認識の芳点からクラスタリングずは䜕ですか 䟋を挙げたしょう。おばあさんのコテヌゞの花の玠敵な写真があるずしたしょう。



問題は、この写真のほが同じ色で塗り぀ぶされおいる領域の数を刀断するこずです。 たあ、それはたったく難しくありたせん癜い花びら-1぀、黄色のセンタヌ-2぀私は生物孊者ではありたせん、私はそれらが䜕ず呌ばれるかわかりたせん、3぀の緑。 これらのセクションはクラスタヌず呌ばれたす。 クラスタヌは、共通の特城色、䜍眮などを持぀デヌタの組み合わせです。 このようなクラスタヌ-セクション内のデヌタの各コンポヌネントを決定しお配眮するプロセスは、クラスタリングず呌ばれたす。

倚くのクラスタリングアルゎリズムがありたすが、最も単玔なものはk-䞭皋床のもので、これに぀いおは埌で説明したす。 K-meansは、゜フトりェアメ゜ッドを䜿甚しお簡単に実装できるシンプルで効率的なアルゎリズムです。 クラスタに分散するデヌタはピクセルです。 ご存じのずおり、カラヌピクセルには3぀のコンポヌネント赀、緑、青がありたす。 これらのコンポヌネントの面付けにより、既存の色のパレットが䜜成されたす。



コンピュヌタのメモリでは、各色成分は0〜255の数字で特城付けられたす。぀たり、赀、緑、青の異なる倀を組み合わせお、画面䞊にカラヌパレットを取埗したす。

䟋ずしおピクセルを䜿甚しお、アルゎリズムを実装したす。 K-meansは反埩アルゎリズムです。぀たり、いく぀かの数孊的蚈算を䞀定回数繰り返した埌、正しい結果が埗られたす。

アルゎリズム


  1. デヌタを配信するために必芁なクラスタヌの数を事前に知る必芁がありたす。 これはこの方法の重倧な欠点ですが、この問題はアルゎリズムの改善された実装によっお解決されたすが、これは圌らが蚀うように、たったく異なる話です。
  2. クラスタヌの初期䞭心を遞択する必芁がありたす。 どうやっお はい、ランダムに。 なんで 各ピクセルをクラスタヌの䞭心にスナップできるようにしたす。 䞭心は王のようなもので、その呚りに圌の䞻題が集たっおいたす-ピクセル。 各ピクセルが誰に埓うかを決定するのは、䞭心からピクセルたでの「距離」です。
  3. 各䞭心から各ピクセルたでの距離を蚈算したす。 この距離は、空間内のポむント間のナヌクリッド距離ず芋なされ、この堎合、3぀の色成分間の距離ず芋なされたす。

    $$衚瀺$$ \ sqrt {R_ {2} -R_ {1}^ 2 +G_ {2} -G_ {1}^ 2 +B_ {2} -B_ {1}^ 2} 。$$衚瀺$$

    最初のピクセルから各䞭心たでの距離を蚈算し、このピクセルず䞭心の間の最小距離を決定したす。 䞭心である最小距離は、ピクセルの各コンポヌネント-キングずピクセル-被写䜓の間の算術平均ずしお座暙を再蚈算したす。 私たちのセンタヌは、蚈算に埓っお空間をシフトしたす。
  4. すべおの䞭心を再蚈算した埌、ピクセルをクラスタヌに分配し、各ピクセルから䞭心たでの距離を比范したす。 ピクセルはクラスタヌに配眮され、その䞭心は他の䞭心よりも近くにありたす。
  5. ピクセルが同じクラスタヌ内にある限り、すべおが最初から始たりたす。 倧量のデヌタでは䞭心が小さな半埄で移動し、クラスタヌの端に沿ったピクセルが䞀方たたは他方のクラスタヌにゞャンプするため、これは頻繁に発生したせん。 これを行うには、反埩の最倧数を決定したす。

実装


このプロゞェクトをC ++で実装したす。 最初のファむルは「k_means.h」です。その䞭で、䞻なデヌタ型、定数、および䜜業甚のメむンクラス「K_means」を定矩したした。
各ピクセルを特城付けるには、3぀のピクセルコンポヌネントで構成される構造を䜜成したす。より正確な蚈算のためにdoubleタむプを遞択し、プログラムの定数を決定したした。

const int KK = 10; //  const int max_iterations = 100; //   typedef struct { double r; double g; double b; } rgb; 

K_meansクラス自䜓

 class K_means { private: std::vector<rgb> pixcel; int q_klaster; int k_pixcel; std::vector<rgb> centr; void identify_centers(); inline double compute(rgb k1, rgb k2) { return sqrt(pow((k1.r - k2.r),2) + pow((k1.g - k2.g), 2) + pow((k1.b - k2.b), 2)); } inline double compute_s(double a, double b) { return (a + b) / 2; }; public: K_means() : q_klaster(0), k_pixcel(0) {}; K_means(int n, rgb *mas, int n_klaster); K_means(int n_klaster, std::istream & os); void clustering(std::ostream & os); void print()const; ~K_means(); friend std::ostream & operator<<(std::ostream & os, const K_means & k); }; 

クラスのコンポヌネントを調べおみたしょう。

vectorpixcel-ピクセルのベクトル。
q_klaster-クラスタヌの数。
k_pixcel-ピクセル数。
vectorcentr-クラスタリング䞭心のベクトル。その䞭の芁玠の数はq_klasterによっお決定されたす。
identity_centers-入力ピクセル間で初期䞭心をランダムに遞択する方法。
computeおよびcompute_sは、それぞれピクセル間の距離を蚈算し、䞭心を再蚈算するための組み蟌みメ゜ッドです。
3぀のコンストラクタヌ最初はデフォルトで、2番目は配列からピクセルを初期化するため、3番目はテキストファむルからピクセルを初期化するためです私の実装では、ファむルが最初に誀っおデヌタで満たされ、次にこのファむルからピクセルが読み蟌たれたす。ちょうど私の堎合に必芁な;
クラスタリングstd :: ostreamos-クラスタリング方法;
メ゜ッドを䜜成し、出力ステヌトメントをオヌバヌロヌドしお結果を公開したす。

メ゜ッドの実装

 void K_means::identify_centers() { srand((unsigned)time(NULL)); rgb temp; rgb *mas = new rgb[q_klaster]; for (int i = 0; i < q_klaster; i++) { temp = pixcel[0 + rand() % k_pixcel]; for (int j = i; j < q_klaster; j++) { if (temp.r != mas[j].r && temp.g != mas[j].g && temp.b != mas[j].b) { mas[j] = temp; } else { i--; break; } } } for (int i = 0; i < q_klaster; i++) { centr.push_back(mas[i]); } delete []mas; } 

これは、初期クラスタリング䞭心を遞択しお䞭心ベクトルに远加する方法です。 これらの堎合、センタヌを繰り返しお亀換するためにチェックが実行されたす。

 K_means::K_means(int n, rgb * mas, int n_klaster) { for (int i = 0; i < n; i++) { pixcel.push_back(*(mas + i)); } q_klaster = n_klaster; k_pixcel = n; identify_centers(); } 

配列からピクセルを初期化するためのコンストラクタヌ実装。

 K_means::K_means(int n_klaster, std::istream & os) : q_klaster(n_klaster) { rgb temp; while (os >> temp.r && os >> temp.g && os >> temp.b) { pixcel.push_back(temp); } k_pixcel = pixcel.size(); identify_centers(); } 

ファむルずコン゜ヌルの䞡方からデヌタを入力できるように、このコンストラクタヌに入力オブゞェクトを枡したす。

 void K_means::clustering(std::ostream & os) { os << "\n\n :" << std::endl; /*             :        ,    -  ,    ,   ,        .*/ std::vector<int> check_1(k_pixcel, -1); std::vector<int> check_2(k_pixcel, -2); int iter = 0; /* .*/ while(true) { os << "\n\n----------------  №" << iter << " ----------------\n\n"; { for (int j = 0; j < k_pixcel; j++) { double *mas = new double[q_klaster]; /*  :          .      ,   .*/ for (int i = 0; i < q_klaster; i++) { *(mas + i) = compute(pixcel[j], centr[i]); os << "   " << j << "   #" << i << ": " << *(mas + i) << std::endl; } /*     m_k      .*/ double min_dist = *mas; int m_k = 0; for (int i = 0; i < q_klaster; i++) { if (min_dist > *(mas + i)) { min_dist = *(mas + i); m_k = i; } } os << "    #" << m_k << std::endl; os << "  #" << m_k << ": "; centr[m_k].r = compute_s(pixcel[j].r, centr[m_k].r); centr[m_k].g = compute_s(pixcel[j].g, centr[m_k].g); centr[m_k].b = compute_s(pixcel[j].b, centr[m_k].b); os << centr[m_k].r << " " << centr[m_k].g << " " << centr[m_k].b << std::endl; delete[] mas; } /*   .*/ int *mass = new int[k_pixcel]; os << "\n  : "<< std::endl; for (int k = 0; k < k_pixcel; k++) { double *mas = new double[q_klaster]; /*    .*/ for (int i = 0; i < q_klaster; i++) { *(mas + i) = compute(pixcel[k], centr[i]); os << "   №" << k << "   #" << i << ": " << *(mas + i) << std::endl; } /*  .*/ double min_dist = *mas; int m_k = 0; for (int i = 0; i < q_klaster; i++) { if (min_dist > *(mas + i)) { min_dist = *(mas + i); m_k = i; } } mass[k] = m_k; os << " №" << k << "     #" << m_k << std::endl; } /*            .*/ os << "\n    : \n"; for (int i = 0; i < k_pixcel; i++) { os << mass[i] << " "; check_1[i] = *(mass + i); } os << std::endl << std::endl; os << " : " << std::endl; int itr = KK + 1; for (int i = 0; i < q_klaster; i++) { os << " #" << i << std::endl; for (int j = 0; j < k_pixcel; j++) { if (mass[j] == i) { os << pixcel[j].r << " " << pixcel[j].g << " " << pixcel[j].b << std::endl; mass[j] = ++itr; } } } delete[] mass; /*    .*/ os << " : \n" ; for (int i = 0; i < q_klaster; i++) { os << centr[i].r << " " << centr[i].g << " " << centr[i].b << " - #" << i << std::endl; } } /*         –  .*/ iter++; if (check_1 == check_2 || iter >= max_iterations) { break; } check_2 = check_1; } os << "\n\n ." << std::endl; } 
クラスタリングの䞻な方法。

 std::ostream & operator<<(std::ostream & os, const K_means & k) { os << " : " << std::endl; for (int i = 0; i < k.k_pixcel; i++) { os << k.pixcel[i].r << " " << k.pixcel[i].g << " " << k.pixcel[i].b << " - №" << i << std::endl; } os << std::endl << "   : " << std::endl; for (int i = 0; i < k.q_klaster; i++) { os << k.centr[i].r << " " << k.centr[i].g << " " << k.centr[i].b << " - #" << i << std::endl; } os << "\n : " << k.q_klaster << std::endl; os << " : " << k.k_pixcel << std::endl; return os; } 

初期デヌタの出力。

出力䟋


出力䟋
開始ピクセル
255140 50-No. 0
100 70 1-No. 1
150 20200-2番
251141 51-No.3
104 69 3-No. 4
153 22210-No. 5
252138 54-第6
101 74 4-No. 7

ランダムな初期クラスタリングセンタヌ
150 20200-0
104 69 3-1
100 70 1-2

クラスタヌの数3
ピクセル数8

クラスタヌ開始

反埩番号0

ピクセル0から䞭心0たでの距離218.918
ピクセル0から䞭心1たでの距離173.352
ピクセル0から䞭心2たでの距離176.992
最小䞭心距離1
センタヌの再蚈算1179.5 104.5 26.5
ピクセル1から䞭心0たでの距離211.189
ピクセル1から䞭心1たでの距離90.3369
ピクセル1から䞭心2たでの距離0
最小䞭心距離2
センタヌの再蚈算2100 70 1
ピクセル2から䞭心0たでの距離0
ピクセル2から䞭心1たでの距離195.225
ピクセル2から䞭心2たでの距離211.189
最小䞭心距離0
センタヌを数える0150 20 200
ピクセル3から䞭心0たでの距離216.894
ピクセル3から䞭心1たでの距離83.933
ピクセル3から䞭心2たでの距離174.19
最小䞭心距離1
センタヌを数える1215.25 122.75 38.75
ピクセル4から䞭心0たでの距離208.149
ピクセル4から䞭心1たでの距離128.622
ピクセル4から䞭心2たでの距離4.58258
最小䞭心距離2
センタヌを数える2102 69.5 2
ピクセル5から䞭心0たでの距離10.6301
ピクセル5から䞭心1たでの距離208.212
ピクセル5から䞭心2たでの距離219.366
最小䞭心距離0
センタヌを数える0151.5 21 205
ピクセル6から䞭心0たでの距離215.848
ピクセル6から䞭心1たでの距離42.6109
ピクセル6から䞭心2たでの距離172.905
最小䞭心距離1
センタヌの再蚈算1233.625 130.375 46.375
ピクセル7から䞭心0たでの距離213.916
ピクセル7から䞭心1たでの距離150.21
ピクセル7から䞭心2たでの距離5.02494
最小䞭心距離2
センタヌの再蚈算2101.5 71.75 3

ピクセルを分類したしょう
ピクセルNo. 0から䞭心0たでの距離221.129
ピクセルNo. 0から䞭心たでの距離123.7207
ピクセルNo. 0から䞭心たでの距離2174.44
センタヌ1に最も近いピクセル0
ピクセル1から䞭心たでの距離0216.031
ピクセル1から䞭心たでの距離1153.492
ピクセルNo. 1から䞭心たでの距離23.05164
センタヌ2に最も近いピクセル1
ピクセルNo. 2から䞭心たでの距離05.31507
ピクセル2から䞭心たでの距離1206.825
ピクセル2から䞭心たでの距離2209.378
センタヌ0に最も近いピクセル2
ピクセル番号3から䞭心0たでの距離219.126
ピクセル3から䞭心たでの距離120.8847
ピクセルNo. 3から䞭心たでの距離2171.609
センタヌ1に最も近いピクセル3
ピクセルNo. 4から䞭心たでの距離0212.989
ピクセル4から䞭心たでの距離1149.836
ピクセル4から䞭心たでの距離23.71652
センタヌ2に最も近いピクセル4
ピクセルNo. 5から䞭心たでの距離05.31507
ピクセル5から䞭心たでの距離1212.176
ピクセル5から䞭心たでの距離2219.035
センタヌ0に最も近いピクセル5
ピクセル番号6から䞭心0たでの距離215.848
ピクセルNo. 6から䞭心たでの距離121.3054
ピクセル6から䞭心たでの距離2172.164
センタヌ1に最も近いピクセル6
ピクセルNo. 7から䞭心たでの距離0213.916
ピクセル番号7から䞭心たでの距離1150.21
ピクセルNo. 7から䞭心たでの距離22.51247
センタヌ2に最も近いピクセル7

䞀臎するピクセルず䞭心の配列
1 2 0 1 2 0 1 2

クラスタリング結果
クラスタヌ0
150 20 200
153 22 210
クラスタヌ1
255 140 50
251 141 51
252138 54
クラスタヌ2
100 70 1
104 69 3
101 74 4
新しいセンタヌ
151.5 21205-0
233.625 130.375 46.375-1
101.5 71.75 3-2

反埩番号1

ピクセル0から䞭心0たでの距離221.129
ピクセル0から䞭心1たでの距離23.7207
ピクセル0から䞭心2たでの距離174.44
最小䞭心距離1
センタヌを数える1244.313 135.188 48.1875
ピクセル1から䞭心0たでの距離216.031
ピクセル1から䞭心1たでの距離165.234
ピクセル1から䞭心2たでの距離3.05164
最小䞭心距離2
センタヌの再蚈算2100.75 70.875 2
ピクセル2から䞭心0たでの距離5.31507
ピクセル2から䞭心1たでの距離212.627
ピクセル2から䞭心2たでの距離210.28
最小䞭心距離0
䞭心の再蚈算0150.75 20.5 202.5
ピクセル3から䞭心0たでの距離217.997
ピクセル3から䞭心1たでの距離9.29613
ピクセル3から䞭心2たでの距離172.898
最小䞭心距離1
センタヌを数える1247.656 138.094 49.5938
ピクセル4から䞭心0たでの距離210.566
ピクセル4から䞭心1たでの距離166.078
ピクセル4から䞭心2たでの距離3.88306
最小䞭心距離2
センタヌを数える2102.375 69.9375 2.5
ピクセル5から䞭心0たでの距離7.97261
ピクセル5から䞭心1たでの距離219.471
ピクセル5から䞭心2たでの距離218.9
最小䞭心距離0
センタヌのカりント0151.875 21.25 206.25
ピクセル6から䞭心0たでの距離216.415
ピクセル6から䞭心1たでの距離6.18805
ピクセル6から䞭心2たでの距離172.257
最小䞭心距離1
センタヌの再蚈算1249.828 138.047 51.7969
ピクセル7から䞭心0たでの距離215.118
ピクセル7から䞭心1たでの距離168.927
ピクセル7から䞭心2たでの距離4.54363
最小䞭心距離2
センタヌの再蚈算2101.688 71.9688 3.25

ピクセルを分類したしょう
ピクセルNo. 0から䞭心0たでの距離221.699
ピクセルNo. 0から䞭心たでの距離15.81307
ピクセルNo. 0から䞭心たでの距離2174.122
センタヌ1に最も近いピクセル0
ピクセルNo. 1から䞭心0たでの距離217.244
ピクセル1から䞭心たでの距離1172.218
ピクセル1から䞭心2たでの距離3.43309
センタヌ2に最も近いピクセル1
ピクセル2から䞭心たでの距離06.64384
ピクセルNo. 2から䞭心たでの距離1214.161
ピクセル2から䞭心たでの距離2209.154
センタヌ0に最も近いピクセル2
ピクセルNo. 3から䞭心0たでの距離219.701
ピクセルNo. 3から䞭心たでの距離13.27555
ピクセル3から䞭心たでの距離2171.288
センタヌ1に最も近いピクセル3
ピクセルNo. 4から䞭心たでの距離0214.202
ピクセル4から䞭心たでの距離1168.566
ピクセル4から䞭心たでの距離23.77142
センタヌ2に最も近いピクセル4
ピクセル5から䞭心たでの距離03.9863
ピクセル5から䞭心たでの距離1218.794
ピクセル5から䞭心たでの距離2218.805
センタヌ0に最も近いピクセル5
ピクセル番号6から䞭心たでの距離0216.415
ピクセルNo. 6から䞭心たでの距離13.09403
ピクセル6から䞭心たでの距離2171.842
センタヌ1に最も近いピクセル6
ピクセル番号7から䞭心0たでの距離215.118
ピクセルNo. 7から䞭心たでの距離1168.927
ピクセルNo. 7から䞭心たでの距離22.27181
センタヌ2に最も近いピクセル7

䞀臎するピクセルず䞭心の配列
1 2 0 1 2 0 1 2

クラスタリング結果
クラスタヌ0
150 20 200
153 22 210
クラスタヌ1
255 140 50
251 141 51
252138 54
クラスタヌ2
100 70 1
104 69 3
101 74 4
新しいセンタヌ
151.875 21.25 206.25-0
249.828 138.047 51.7969-1
101.688 71.9688 3.25-2

クラスタリングの終わり。

この䟋は前もっお蚈画されおおり、ピクセルはデモ甚に遞択されおいたす。 プログラムがデヌタを3぀のクラスタヌにグルヌプ化するには、2回の反埩で十分です。 最埌の2回の繰り返しの䞭心を芋るず、それらが実際に所定の䜍眮に残っおいるこずがわかりたす。

さらに興味深いのは、ランダムに生成されたピクセルの堎合です。 10個のクラスタヌに分割する必芁がある50個のポむントを生成したので、5回繰り返したした。 3぀のクラスタヌに分割する必芁のある50個のポむントを生成したので、最倧100回の反埩が蚱可されたした。 クラスタヌが倚いほど、プログラムが最も類䌌したピクセルを芋぀けおそれらをより小さなグルヌプにたずめるのが簡単になり、逆も同様です-クラスタヌが少なく、ポむントが倚い堎合、アルゎリズムは倚くの堎合、最倧反埩数を超えたずきにのみ終了したすあるクラスタヌから別のクラスタヌぞ。 ただし、バルクはクラスタヌ内で完党に決定されたす。

それでは、クラスタリングの結果を確認したしょう。 10個のクラスタヌあたり50ポむントの䟋からいく぀かのクラスタヌの結果を取埗し、これらのデヌタの結果をIllustratorに送り蟌みたした。



各クラスタヌでは色の濃淡が優勢であるこずがわかりたす。ここでは、ピクセルがランダムに遞択されたこずを理解する必芁がありたす。実際の生掻でのそのような画像の類䌌物は、すべおの色が誀っお吹き付けられた䜕らかの皮類の画像であり、同様の色の領域を遞択するこずは困難です

このような写真があるずしたしょう。 島を1぀のクラスタヌずしお定矩するこずもできたすが、増加するず、さたざたな緑の陰圱で構成されるこずがわかりたす。



これはクラスタヌ8ですが、小さいバヌゞョンでは結果は同様です



プログラムのフルバヌゞョンは、私のGitHubで衚瀺できたす。

Source: https://habr.com/ru/post/J427761/


All Articles