2012-04-20 4 views
5

Diese Frage ist spezifisch für opencv: Das in der opencv-Dokumentation angegebene kmeans-Beispiel hat eine 2-Kanal-Matrix - einen Kanal für jede Dimension des Merkmalsvektors. Aber einige der anderen Beispiele scheinen zu sagen, dass es eine Ein-Kanal-Matrix mit Merkmalen entlang der Spalten mit einer Zeile für jede Probe sein sollte. Welche davon ist richtig?Eingangsmatrix zu opencv kmeans Clustering

, wenn ich einen 5 dimensionalen Merkmalsvektor haben, sollten, was die Eingangsmatrix sein, die ich benutze: Dieses:

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

oder diese ein:

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

Antwort

28

Die richtige Antwort ist cv::Mat inputSamples(numSamples, numFeatures, CV_32F) . Die OpenCV Dokumentation über kmeanssays:

samples - Gleitkommazahl Matrix von Eingangsabtastwerten, eine Zeile pro Probe

So ist es nicht Gleitkommadivisionsschaltung Vektor von n-dimensionalen Schwimmern, wie in die andere Option. Welche Beispiele haben ein solches Verhalten nahegelegt?

Hier ist auch ein kleines Beispiel von mir, das zeigt, wie Kmeans verwendet werden können. Es Clustern die Pixel eines Bildes und zeigt das Ergebnis:

#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

Ich würde gerne wissen, was Sie in der Schleife vor der clusterCount-Variablendeklaration tun und auch, was Sie am Ende in der für nach dem Kmeans tun. Denkst du, dass es möglich ist, die Antwort mit diesen Informationen zu aktualisieren? Vielen Dank! –

+0

Die erste Schleife ordnet die Daten aus dem Bild von einer Matrix (Zeilen, Spalten, 3) in eine Matrix (Zeilen * Spalten, 3) um (eine Zeile pro Pixel). Die Schleife am Ende ersetzt jedes Pixel im Bild mit dem entsprechenden Cluster-Zentrum für die Visualisierung. – sietschie

+0

Ist es möglich, 'Mat :: reshape()' anstelle von geschachtelten for-Schleifen zu verwenden? – Jayesh

1

Als Alternative, um die Eingangsmatrix manuell zu Umformen, können Sie OpenCV reshape Funktion ähnliches Ergebnis mit weniger Code zu erreichen. Hier ist meine Arbeits Umsetzung von Farben zählen mit K-Means-Verfahren zu reduzieren (in 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 liest Bild in 2-dimensionale, 3-Kanalmatrix. Erster Aufruf an reshape - img.reshape(1, img.rows() * img.cols()); - im Wesentlichen entrollt 3 Kanäle in Spalten. In der resultierenden Matrix entspricht eine Reihe einem Pixel des Eingangsbildes und 3 Spalten entsprechen den RGB-Komponenten.

Nach K-Means-Algorithmus beendet seine Arbeit, und die Farbzuordnung angewendet wurde, rufen wir reshape wieder - imgMapped.reshape(3, img.rows()), aber jetzt Spalten zurück in die Kanäle, und die Verringerung der Zeilennummern auf die ursprüngliche Bildzeilennummer, so dass immer wieder die Roll Original-Matrix-Format, aber nur mit reduzierten Farben.

+0

I denke, du musst darauf achten, dass das Bild zuerst kontinuierlich ist, bevor du diesen Ansatz nimmst http://docs.opencv.org/2.4/modules/core/doc/basic_structures.html#mat-continuous – ejectamenta

+0

wenn du clone ie benutzt. Klon wie in img.clone() verwenden, reshape (1, img.rows() * img.cols()) dann wird das Bild kontinuierlich (und Ihr Originalbild wird unverändert) – ejectamenta