用VLFeat庫進行k-means聚類


摘要

本文將介紹如何使用VLFeat開源庫來進行K-means聚類,關於K-means的介紹可以參考這里

什么是VLFeat

VLFeat官方主頁的話來說,VLFeat 是一個實現了視覺領域諸多算法的開源庫,其包括SIFT, MSERk-means, hierarchical k-means, agglomerative information bottleneck, quick shift 等等。底層代碼用C語言實現,並提供了MATLAB接口。支持Windows, Mac OS X, 和 Linux。最新版本為 VLFeat 0.9.14

和OpenCV相比,VLFeat是一個輕量級的庫,主要實現了在特征提取和聚類方面的高效算法, 可以用在圖像檢索和物體識別領域中。

Integer K-means (IKM) 介紹

接下來將介紹如何用VLFeat庫來進行k-means聚類。

VLFeat 提供了k-means 聚類和分層k-means聚類的輕量級的實現。需要注意的是,IKmeans聚類數據的類型是unsigned char型。雖然這看上去有局限性,但對於圖像的特征聚類,算法有很高的准確性,因為在高維空間中(例如SIFT特征,128維),UCHAR型已經足夠。C語言的Integer K-means的接口文檔請看這里

Integer K-means (IKM) 實現了整型數據的 K-means 聚類 (或者叫矢量量化)。在圖像檢索、識別領域,經常會用到Bag-of-words(BOW)模型,該模型對訓練集圖像提取特征並進行聚類,得到固定數量的代表性特征集(dictionary), 將測試集中提取出來的特征進行量化,用之前得到代表性特征集中的特征(word)來表示,這樣每幅圖像就可以表示成bag-of-words。該功能可以用VLFeat庫輕松實現。

如何使用接口?

用VLFeat進行K-means 聚類,需要包含 ikmeans.h 頭文件,其聲明了如下接口:

數據結構

struct   VlIKMFilt  

量化器,k-means聚類的核心數據結構。聚類相關的任何函數都與此數據結構有關。

枚舉類型

enum  VlIKMAlgorithms { VL_IKM_LLOYD, VL_IKM_ELKAN }

VLFeat 實現了兩種聚類算法,在進行訓練時作為參數傳入給vl_ikm_train 函數。

函數

VlIKMFilt  * vl_ikm_new (int method)

創建量化器,傳入參數為VL_IKM_LLOYD或者 VL_IKM_ELKAN

 

void  vl_ikm_delete (VlIKMFilt *f)

刪除量化器

 

void vl_ikm_init (VlIKMFilt *f, vl_ikm_acc const *centers, int M, int K)

指定聚類的center 對量化器初始化,M為數據的維數,K為聚類數

 

void vl_ikm_init_rand (VlIKMFilt *f, int M, int K)

隨機生成center,並進行初始化,M為數據的維數,K為聚類數

 

void vl_ikm_init_rand_data (VlIKMFilt *f, vl_uint8 const *data, int M, int N, int K)

在數據中隨機指定center,對量化器進行初始化,M為維數,N為數據數,K為聚類數

 

int vl_ikm_train (VlIKMFilt *f, vl_uint8 const *data, int N)

對輸入數據進行訓練, data為數據, N為數據數目。

 

void vl_ikm_push (VlIKMFilt *f, vl_uint *asgn, vl_uint8 const *data, int N)

將新數據量化到聚類中心,得到每個數據的標記。 asgn為數據的標記數組, data為輸入數據, N為數據數目。

 

vl_uint vl_ikm_push_one (vl_ikm_acc const *centers, vl_uint8 const *data, int M, int K)

對一個數據進行量化

 

還有一些存取函數下文將省略,可以查詢文檔查看詳情。

 

IKM使用步驟

step 1. 創建量化器

        用 vl_ikm_new() 函數創建一個IKM 量化器(聚類器)。

step 2. 初始化 IKM量化器

       用 vl_ikm_init() 或者其它接口(本文用vl_ikm_init_rand函數)。

step 3. 用 vl_ikm_train() 訓練量化器。

step 4. 用vl_ikm_push() 函數或者 vl_ikm_push_one() 對新的特征進行量化(如只需要聚類,可以在這一步重復使用step 3. 的訓練數據)。

 

開始聚類吧

准備工作就緒,開始聚類吧!

我們將隨機產生值為[0,255)的2維數據點來進行k-means聚類,這樣可以很直觀方便地在圖像中畫出來看到聚類結果。

在這里用OpenCV的函數來顯示二維數據點以及分類結果。

首先包含必須的頭文件 ikmeans.h。

extern "C" {
#include "ikmeans.h"
}

用OpenCV建立矩陣來顯示隨機生成的數據以及聚類后的結果。

int row = 255;
int col = 255;
Mat show = Mat::zeros(row, col, CV_8UC3);
Mat show2 = show.clone();

創建隨機訓練數據,用200組2維數據進行訓練。並在圖像中繪制數據點。

int data_num = 200;
int data_dim = 2;
vl_uint8 *data = new vl_uint8[data_num * data_dim];

for( int i=0; i<data_num; ++i)
{
    vl_uint8 x = data[i*data_dim] = rand()% col;
    vl_uint8 y = data[i*data_dim+1] = rand()% row;
    circle(show,Point(x,y),2,Scalar(255,255,255));
}

如下圖所示,生成了200個點的數據。

show

                                                                                 圖1.隨機生成的數據點

接下來的幾行代碼將創建量化器,訓練,並得到量化結果,在這里,用訓練數據作為新數據傳給量化器,這樣就可以得到訓練數據的聚類結果,聚類數目為K=3。

VlIKMFilt *kmeans = vl_ikm_new(VL_IKM_ELKAN);
vl_uint K = 3;
vl_ikm_init_rand(kmeans, data_dim, K);
vl_ikm_train(kmeans, data, data_num);
vl_uint * label = new vl_uint[data_num];
vl_ikm_push(kmeans, label, data, data_num);

在上面的代碼中,label數組存放的就是量化結果,量化的序號為0,1,2...,K-1。

最后,畫圖,顯示結果。

    for( int i=0;i<data_num; ++i)
    {
        vl_uint8 x = data[i*data_dim];
        vl_uint8 y = data[i*data_dim+1];
        switch(label[i])
        {
        case 0:
                circle(show2,Point(x,y),2,Scalar(255,0,0));
                break;
        case 1:
                circle(show2,Point(x,y),2,Scalar(0,255,0));
                break;
        case 2:
                circle(show2,Point(x,y),2,Scalar(0,0,255));
                break;
        }
    }

結果如下圖所示:

show2

                                                                                    圖2.聚類(量化)結果

最后別忘了刪除聚類器,以及清空數組。

vl_ikm_delete(kmeans);
delete []label;
label = NULL;
delete []data;
data = NULL;

整個程序的源代碼如下:

#include "stdafx.h"

extern "C" {

#include "ikmeans.h"
}

#include "global_header.h"


int main()
{
    /*initialize data point*/
    int row = 255;
    int col = 255;
    Mat show = Mat::zeros(row, col, CV_8UC3);
     Mat show2 = show.clone();

    int data_num = 200;
    int data_dim = 2;
    vl_uint8 *data = new vl_uint8[data_num * data_dim];

    for( int i=0; i<data_num; ++i)
    {
        vl_uint8 x = data[i*data_dim] = rand()% col;
        vl_uint8 y = data[i*data_dim+1] = rand()% row;
        circle(show,Point(x,y),2,Scalar(255,255,255));
    }

    VlIKMFilt *kmeans = vl_ikm_new(VL_IKM_ELKAN);
    vl_uint K = 3;
    vl_ikm_init_rand(kmeans, data_dim, K);
    vl_ikm_train(kmeans, data, data_num);

    vl_uint * label = new vl_uint[data_num];

    vl_ikm_push(kmeans, label, data, data_num);


    for( int i=0;i<data_num; ++i)
    {
        vl_uint8 x = data[i*data_dim];
        vl_uint8 y = data[i*data_dim+1];
        switch(label[i])
        {
        case 0:
                circle(show2,Point(x,y),2,Scalar(255,0,0));
                break;
        case 1:
                circle(show2,Point(x,y),2,Scalar(0,255,0));
                break;
        case 2:
                circle(show2,Point(x,y),2,Scalar(0,0,255));
                break;
        }
    }


    imwrite("show.jpg",show);
    imwrite("show2.jpg",show2);

    vl_ikm_delete(kmeans);
    
    delete []label;
    label = NULL;

    delete []data;
    data = NULL;


    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM