2012-04-20 11 views
5

To pytanie jest specyficzne dla opencv: Przykład kmeans podany w dokumentacji opencv ma 2-kanałową macierz - jeden kanał dla każdego wymiaru wektora cechy. Ale niektóre z innych przykładów mówią, że powinna to być macierz jednokanałowa z funkcjami wzdłuż kolumn z jednym wierszem dla każdej próbki. Które z nich ma rację?Macierz wejściowa do klastra opencv, które grupuje

jeśli mam 5 funkcji wymiarowy wektor, co powinno być macierz wejściowy używać: to jedno:

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

czy to jedno:

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

Odpowiedz

28

Prawidłowa odpowiedź to cv::Mat inputSamples(numSamples, numFeatures, CV_32F) . OpenCV dokumentację kmeanssays:

próbki - zmiennoprzecinkowych macierz próbek wejściowych w jednym rzędzie na próbkę

więc nie jest zmiennoprzecinkowych wektor n-wymiarowej pływaków jak w druga opcja. Jakie przykłady sugerowały takie zachowanie?

Oto także mały przykład, który pokazuje, w jaki sposób można wykorzystać kmeans. klastrów to piksele obrazu i wyświetla wynik:

#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

Chciałbym wiedzieć, co robisz w pętli przed deklaracją zmiennych clusterCount, a także co robisz na końcu w for po kmeans. Czy uważasz, że możliwe jest zaktualizowanie odpowiedzi za pomocą tych informacji? Dzięki! –

+0

Pierwsza pętla zmienia kolejność danych z obrazu z macierzy (wiersze, cols, 3) na macierz (wiersze * cols, 3) (jeden wiersz na piksel). Pętla na końcu zastępuje każdy piksel na obrazie odpowiednim centrum klastra do wizualizacji. – sietschie

+0

Czy jest możliwe użycie 'Mat :: reshape()' zamiast zagnieżdżonych dla pętli? – Jayesh

1

Jako alternatywa dla ręcznego przekształcenia macierzy wejściowej, można użyć OpenCV reshape funkcji, aby osiągnąć podobny wynik z mniej kodu. Oto moja realizacja pracy redukcji kolorów liczyć metodą k-średnich (w Javie):

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 odczytuje obraz w 2 wymiarowej macierzy 3 kanałów. Pierwsze połączenie z reshape - img.reshape(1, img.rows() * img.cols()); - zasadniczo rozwija 3 kanały w kolumny. W wynikowej macierzy jeden wiersz odpowiada jednemu pikselowi obrazu wejściowego, a 3 kolumny odpowiadają elementom RGB.

Po K-means algorytm zakończył swoją pracę, a odwzorowanie kolorów zostało zastosowane, nazywamy reshape ponownie - imgMapped.reshape(3, img.rows()), ale teraz toczenia kolumn z powrotem do kanałów, a zmniejszenie liczby wierszy z oryginalnym numerem obraz wiersza, a tym samym uzyskanie z powrotem oryginalny format matrycy, ale tylko ze zredukowanymi kolorami.

+0

I myślę, że musisz zadbać o to, aby obraz był ciągły najpierw zanim podejmiesz takie podejście http://docs.opencv.org/2.4/modules/core/doc/basic_structures.html#mat-iscontinuous – ejectamenta

+0

jeśli używasz klonu, czyli. używając klonu jak w img.clone(). reshape (1, img.rows() * img.cols()), a następnie obraz będzie ciągły (a oryginalny obraz pozostanie niezmieniony) – ejectamenta