k-means (метод k-средних) — метод кластеризации, стремящийся минимизировать суммарное квадратичное отклонение точек кластеров от центров этих кластеров.
Кластеризация — задача машинного обучения, состоящая в разбиении заданной выборки объектов (данных) на непересекающиеся подмножества/группы (кластеры) на основе близости их признаков/значений. Т.о., каждый кластер состоит из схожих объектов.
Кластеризация позволяет:
* лучше понять данные (выявив структурные группы),
* компактное хранение данных,
* выявление новых объектов.
В OpenCV, алгоритм k-means реализован в cxcore, т.к. он был реализован задолго до появления библиотеки ML.
K-means пытается найти кластеры в наборе данных.
Это реализуется функцией cvKMeans2().
Алгоритм работы k-means:
1. Принимает входные данные и желаемое число кластеров
2. Случайным образом выбирает центры кластеров
3. Ассоциирует каждую точку данных с ближайшим центром кластера
4. Перемещает центр кластера в центроид его точек данных.
5. Возвращается на шаг 3 пока не достигнута сходимость (центр кластера не двигается)
Проблемы алгоритма k-means:
1. не гарантирует определения лучшего из возможных расположений центров кластеров (достижение глобального минимума суммарного квадратичного отклонения). Однако, гарантирует сходимость к какому-либо решению, т.е. итерации не зациклятся в бесконечном цикле.
2. не определяет сколько кластеров стоит использовать (т.е. число кластеров надо знать заранее)
3. результат зависит от выбора исходных центров кластеров
4. K-means предполагает, что пространственная ковариационная составляющая, либо не имеет значения, либо уже была отнормирована.
CVAPI(int) cvKMeans2( const CvArr* samples, int cluster_count, CvArr* labels, CvTermCriteria termcrit, int attempts CV_DEFAULT(1), CvRNG* rng CV_DEFAULT(0), int flags CV_DEFAULT(0), CvArr* _centers CV_DEFAULT(0), double* compactness CV_DEFAULT(0) );
— разделение векторов на заданное число кластеров
samples — матрица примеров (float) — одна строчка — один вектор
cluster_count — число кластеров
labels — возвращаемый вектор (int), хранящий индекс кластера каждого вектора
termcrit — критерий завершения итераций
attempts — число попыток достижения лучшей компактности
rng — внешний ГСЧ
flags — флаг — 0 или:
#define CV_KMEANS_USE_INITIAL_LABELS 1 // для первой попытки будет использовать предуставновленное значение вектора labels (далее - будет использоваться ГСЧ)
_centers — возвращаемый массив центров кластеров
compactness — компактность — возвращаемый параметр, который высчитывается по формуле:
Summ( || samples(i) - centers(labels(i)) ||^2 )
— высчитывается после каждой попытки и лучшее (минимальное) значение выбирается и соответствующие labels возвращаются
// // Несколько модифицированный пример Example 13-1. // Использование K-means // // из книги: // Learning OpenCV: Computer Vision with the OpenCV Library // by Gary Bradski and Adrian Kaehler // Published by O'Reilly Media, October 3, 2008 #include "cxcore.h" #include "highgui.h" #define MAX_CLUSTERS 5 int main( int argc, char* argv[] ) { // изображение для показа точек IplImage* img = cvCreateImage( cvSize( 500, 500 ), 8, 3 ); // таблица цветов кластеров CvScalar color_tab[MAX_CLUSTERS]; color_tab[0] = CV_RGB(255,0,0); color_tab[1] = CV_RGB(0,255,0); color_tab[2] = CV_RGB(100,100,255); color_tab[3] = CV_RGB(255,0,255); color_tab[4] = CV_RGB(255,255,0); // инициализация состояния ГПСЧ CvRNG rng = cvRNG(0xffffffff); cvNamedWindow( "clusters", 1 ); for(;;){ int k, cluster_count = cvRandInt(&rng)%MAX_CLUSTERS + 1; int i, sample_count = cvRandInt(&rng)%1000 + 1; CvMat* points = cvCreateMat( sample_count, 1, CV_32FC2 ); CvMat* clusters = cvCreateMat( sample_count, 1, CV_32SC1 ); // генерация случайного гауссового распределения точек for( k = 0; k < cluster_count; k++ ){ CvPoint center; CvMat point_chunk; center.x = cvRandInt(&rng)%img->width; center.y = cvRandInt(&rng)%img->height; cvGetRows( points, &point_chunk, k*sample_count/cluster_count, k == cluster_count - 1 ? sample_count : (k+1)*sample_count/cluster_count ); cvRandArr( &rng, &point_chunk, CV_RAND_NORMAL, cvScalar(center.x,center.y,0,0), cvScalar(img->width/6, img->height/6,0,0) ); } // точки перемешиваются for( i = 0; i < sample_count/2; i++ ){ CvPoint2D32f* pt1 = (CvPoint2D32f*)points->data.fl + cvRandInt(&rng)%sample_count; CvPoint2D32f* pt2 = (CvPoint2D32f*)points->data.fl + cvRandInt(&rng)%sample_count; CvPoint2D32f temp; CV_SWAP( *pt1, *pt2, temp ); } // определение кластеров cvKMeans2( points, cluster_count, clusters, cvTermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 10, 1.0 )); cvZero( img ); // показываем точки for( i = 0; i < sample_count; i++ ){ CvPoint2D32f pt = ((CvPoint2D32f*)points->data.fl)[i]; // индекс кластера int cluster_idx = clusters->data.i[i]; cvCircle( img, cvPointFrom32f(pt), 2, color_tab[cluster_idx], CV_FILLED ); } cvReleaseMat( &points ); cvReleaseMat( &clusters ); // показываем cvShowImage( "clusters", img ); int key = cvWaitKey(330); if( key == 27 ){ // 'ESC' break; } } // освобождаем ресурсы cvReleaseImage(&img); // удаляем окна cvDestroyAllWindows(); return 0; }
CVAPI(CvMat*) cvGetRows( const CvArr* arr, CvMat* submat, int start_row, int end_row, int delta_row CV_DEFAULT(1)); CV_INLINE CvMat* cvGetRow( const CvArr* arr, CvMat* submat, int row ) { return cvGetRows( arr, submat, row, row + 1, 1 ); }
— возвращает массив строк
arr — исходный массив
submat — указатель возвращаемый заголовок массива
start_row — индекс (от 0) начальной строки
end_row — индекс последней строки (не включая)
delta_row — индекс шага (т.е. выбирается каждая delta_row-ая строка между [start_row:end_row) )
CVAPI(CvMat*) cvGetCols( const CvArr* arr, CvMat* submat, int start_col, int end_col ); CV_INLINE CvMat* cvGetCol( const CvArr* arr, CvMat* submat, int col ) { return cvGetCols( arr, submat, col, col + 1 ); }
— возвращает массив столбцов
arr — исходный массив
submat — указатель возвращаемый заголовок массива
start_col — индекс (от 0) начального столбца
end_col — индекс последнего столбца (не включая)
// // K-means для центров масс контуров изображения // #include <cv.h> #include <highgui.h> #define MAX_CLUSTERS 5 int main( int argc, char* argv[] ) { // для хранения изображения IplImage* image=0, *gray=0, *bin=0, *dst=0; // // загрузка изображения // char img_name[] = "Image0.jpg"; // имя картинки задаётся первым параметром char* image_filename = argc >= 2 ? argv[1] : img_name; // получаем картинку image = cvLoadImage(image_filename, 1); printf("[i] image: %s\n", image_filename); if(!image){ printf("[!] Error: cant load test image: %s\n", image_filename); return -1; } // показываем картинку cvNamedWindow("image"); cvShowImage("image", image); // создание изображений dst = cvCloneImage(image); gray = cvCreateImage( cvGetSize(image), IPL_DEPTH_8U, 1); bin = cvCreateImage( cvGetSize(image), IPL_DEPTH_8U, 1); cvConvertImage(image, gray, CV_BGR2GRAY); cvCanny(gray, bin, 50, 200, 3); cvNamedWindow("cvCanny"); cvShowImage("cvCanny", bin); // для хранения контуров CvMemStorage* storage = cvCreateMemStorage(0); CvSeq* contours=0, *seq=0; // находим контуры int contoursCont = cvFindContours( bin, storage, &contours, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0)); if(!contours){ printf("[!] Error: cant find contours!\n"); return -2; } printf("[i] contours: %d \n", contoursCont); // нарисуем контуры for(seq = contours; seq!=0; seq = seq->h_next){ cvDrawContours(image, seq, CV_RGB(255,216,0), CV_RGB(0,0,250), 0, 1, 4); // рисуем контур } cvNamedWindow("contours"); cvShowImage("contours", image); cvConvertImage(bin, dst, CV_GRAY2BGR); // // таблица цветов кластеров // CvScalar color_tab[MAX_CLUSTERS]; color_tab[0] = CV_RGB(255,0,0); color_tab[1] = CV_RGB(0,255,0); color_tab[2] = CV_RGB(100,100,255); color_tab[3] = CV_RGB(255,0,255); color_tab[4] = CV_RGB(255,255,0); cvNamedWindow("clusters"); { int cluster_count = MAX_CLUSTERS; int sample_count = contoursCont; int i, j, k; CvMat* points = cvCreateMat( sample_count, 1, CV_32FC2 ); CvMat* clusters = cvCreateMat( sample_count, 1, CV_32SC1 ); // определяем точки контуров for(k=0, seq = contours; seq!=0; seq = seq->h_next, k++){ CvPoint2D32f center = cvPoint2D32f(0, 0); #if 0 float radius=0; // определяем окружность заключающую в себе контур cvMinEnclosingCircle(seq, ¢er, &radius); #else // находим центр масс int count=0; for(i=0; itotal; ++i ) { CvPoint* p = CV_GET_SEQ_ELEM( CvPoint, seq, i ); center.x+=p->x; center.y+=p->y; count++; } center.x=center.x/count; center.y=center.y/count; #endif // отметим точку cvCircle( image, cvPointFrom32f(center), 2, CV_RGB(200,0,0), CV_FILLED ); // запихиваем в массив cvSet1D( points, k, cvScalar(center.x, center.y, 0) ); } cvShowImage("contours", image); // определение кластеров cvKMeans2( points, cluster_count, clusters, cvTermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 10, 1.0 )); // показываем точки for( i = 0; i < sample_count; i++ ){ CvPoint2D32f pt = ((CvPoint2D32f*)points->data.fl)[i]; // индекс кластера int cluster_idx = clusters->data.i[i]; cvCircle( dst, cvPointFrom32f(pt), 2, color_tab[cluster_idx], CV_FILLED ); } cvReleaseMat( &points ); cvReleaseMat( &clusters ); // показываем cvShowImage( "clusters", dst ); int key = cvWaitKey(0); //if( key == 27 ){ // ESC // break; //} } // // освобождаем ресурсы // cvReleaseImage(&image); cvReleaseImage(&gray); cvReleaseImage(&bin); cvReleaseImage(&dst); cvReleaseMemStorage(&storage); // удаляем окна cvDestroyAllWindows(); return 0; }
Ссылки
http://en.wikipedia.org/wiki/K-means_clustering
OpenCV documentation » core » Clustering
По теме
OpenCV шаг за шагом. Нахождение контуров и операции с ними
OpenCV — определение доминирующих цветов на изображении