2012-04-20 148 views
5

這個問題特定於opencv: opencv文檔中給出的kmeans示例具有2通道矩陣 - 每個維度的特徵向量都有一個通道。但是,其他的一些例子似乎表明,它應該是一個單通道矩陣,沿着列具有一個特徵,每個樣本一行。哪些是正確的?opencv kmeans聚類的輸入矩陣

,如果我有一個5維特徵向量,應該是什麼,我使用的輸入矩陣: 這一個:

cv::Mat inputSamples(numSamples, 1, CV32FC(numFeatures)) 

或者這一個:

cv::Mat inputSamples(numSamples, numFeatures, CV_32F) 

回答

28

正確的答案是cv::Mat inputSamples(numSamples, numFeatures, CV_32F) 。 在OpenCV文檔約kmeanssays

樣本 - 輸入樣本的浮點矩陣,每採樣1行

所以它不是如在n維浮一個浮點矢量另一種選擇。哪些例子表明了這種行爲?

這裏也是一個小例子,顯示瞭如何使用kmeans。它簇的圖像的像素,並顯示結果:

#include "opencv2/imgproc/imgproc.hpp" 
#include "opencv2/highgui/highgui.hpp" 

using namespace cv; 

int main(int argc, char** argv) 
{ 
    Mat src = imread(argv[1], 1); 
    Mat samples(src.rows * src.cols, 3, CV_32F); 
    for(int y = 0; y < src.rows; y++) 
    for(int x = 0; x < src.cols; x++) 
     for(int z = 0; z < 3; z++) 
     samples.at<float>(y + x*src.rows, z) = src.at<Vec3b>(y,x)[z]; 


    int clusterCount = 15; 
    Mat labels; 
    int attempts = 5; 
    Mat centers; 
    kmeans(samples, clusterCount, labels, TermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS, 10000, 0.0001), attempts, KMEANS_PP_CENTERS, centers); 


    Mat new_image(src.size(), src.type()); 
    for(int y = 0; y < src.rows; y++) 
    for(int x = 0; x < src.cols; x++) 
    { 
     int cluster_idx = labels.at<int>(y + x*src.rows,0); 
     new_image.at<Vec3b>(y,x)[0] = centers.at<float>(cluster_idx, 0); 
     new_image.at<Vec3b>(y,x)[1] = centers.at<float>(cluster_idx, 1); 
     new_image.at<Vec3b>(y,x)[2] = centers.at<float>(cluster_idx, 2); 
    } 
    imshow("clustered image", new_image); 
    waitKey(0); 
} 
+0

我想知道你在循環之前在clusterCount變量聲明中所做的事情,以及你在kmeans之後的for循環結尾所做的事情。你認爲用這些信息更新答案是可能的嗎?謝謝! – 2013-02-11 19:17:27

+0

第一個循環將來自圖像的數據從(行,列,3)矩陣重新排列到(rows * cols,3)矩陣(每個像素一行)。最後的循環將圖像中的每個像素替換爲相應的聚類中心以進行可視化。 – sietschie 2013-02-12 09:25:07

+0

是否可以使用'Mat :: reshape()'而不是嵌套for循環? – Jayesh 2016-10-26 13:41:16

1

作爲替代手動重塑輸入矩陣,可以使用的OpenCV reshape函數來實現類似的結果用更少的代碼。下面是降低顏色與K-means法(在Java中)計數的我的工作實現:

private final static int MAX_ITER = 10; 
private final static int CLUSTERS = 16; 

public static Mat colorMapKMeans(Mat img, int K, int maxIterations) { 

    Mat m = img.reshape(1, img.rows() * img.cols()); 
    m.convertTo(m, CvType.CV_32F); 

    Mat bestLabels = new Mat(m.rows(), 1, CvType.CV_8U); 
    Mat centroids = new Mat(K, 1, CvType.CV_32F); 
    Core.kmeans(m, K, bestLabels, 
       new TermCriteria(TermCriteria.COUNT | TermCriteria.EPS, maxIterations, 1E-5), 
       1, Core.KMEANS_RANDOM_CENTERS, centroids); 
    List<Integer> idx = new ArrayList<>(m.rows()); 
    Converters.Mat_to_vector_int(bestLabels, idx); 

    Mat imgMapped = new Mat(m.size(), m.type()); 
    for(int i = 0; i < idx.size(); i++) { 
     Mat row = imgMapped.row(i); 
     centroids.row(idx.get(i)).copyTo(row); 
    } 

    return imgMapped.reshape(3, img.rows()); 
} 

public static void main(String[] args) { 
    System.loadLibrary(Core.NATIVE_LIBRARY_NAME); 
    Highgui.imwrite("result.png", 
     colorMapKMeans(Highgui.imread(args[0], Highgui.CV_LOAD_IMAGE_COLOR), 
      CLUSTERS, MAX_ITER)); 
} 

OpenCV的讀取圖像到二維,3信道矩陣。首先撥打reshape - img.reshape(1, img.rows() * img.cols()); - 實質上將3個通道展開爲列。在結果矩陣中,一行對應於輸入圖像的一個像素,而3列對應於RGB分量。

後K-means算法完成其工作,並且顏色映射得到了應用,我們再次呼籲reshape - imgMapped.reshape(3, img.rows()),但現在滾動體列回通道,減少行數爲原始圖像的行數,從而找回了原始矩陣格式,但只有減少的顏色。

+0

I認爲在採取這種方法之前,你需要注意圖像是連續的。如果你使用克隆ie,你需要注意http://docs.opencv.org/2.4/modules/core/doc/basic_structures.html#mat-iscontinuous – ejectamenta 2017-01-24 09:34:16

+0

。使用克隆,如img.clone()。reshape(1,img。rows()* img.cols()),那麼圖像將是連續的(並且原始圖像將保持不變) – ejectamenta 2017-01-24 10:07:49