2012-03-05 9 views
17

Chcę posteryzować obraz za pomocą k-średnich i OpenCV w interfejsie C++ (przestrzeń nazw cv) i otrzymuję dziwne wyniki. Potrzebuję go, aby zmniejszyć hałas. To jest mój kod:OpenCV przy użyciu k-środków do posteryzacji obrazu

#include "cv.h" 
#include "highgui.h" 

using namespace cv; 

int main() { 
    Mat imageBGR, imageHSV, planeH, planeS, planeV; 

    imageBGR = imread("fruits.jpg"); 
    imshow("original", imageBGR); 

    cv::Mat labels, data; 
    cv::Mat centers(8, 1, CV_32FC1); 
    imageBGR.convertTo(data, CV_32F); 

    cv::kmeans(data, 8, labels, 
      cv::TermCriteria(CV_TERMCRIT_ITER, 10, 1.0), 
      3, cv::KMEANS_PP_CENTERS, &centers); 
    imshow("posterized hue", data); 
    data.convertTo(data, CV_32FC3); 

    waitKey(); 
    return 0; 
} 

Ale mam dziwne Wynik

Fruit

Pierwszy obraz: oryginalna

Drugi obraz: po k-średnich.

Każda rada?


Aktualizacja: odpowiednie rozwiązanie. może ktoś może mi pomóc w optymalizacji kodu?

#include "cv.h" 
#include "highgui.h" 

#include <iostream> 

using namespace cv; 
using namespace std; 

int main() { 
    Mat src; 

    src = imread("fruits.jpg"); 
    imshow("original", src); 

    blur(src, src, Size(15,15)); 
    imshow("blurred", src); 

    Mat p = Mat::zeros(src.cols*src.rows, 5, CV_32F); 
    Mat bestLabels, centers, clustered; 
    vector<Mat> bgr; 
    cv::split(src, bgr); 
    // i think there is a better way to split pixel bgr color 
    for(int i=0; i<src.cols*src.rows; i++) { 
     p.at<float>(i,0) = (i/src.cols)/src.rows; 
     p.at<float>(i,1) = (i%src.cols)/src.cols; 
     p.at<float>(i,2) = bgr[0].data[i]/255.0; 
     p.at<float>(i,3) = bgr[1].data[i]/255.0; 
     p.at<float>(i,4) = bgr[2].data[i]/255.0; 
    } 

    int K = 8; 
    cv::kmeans(p, K, bestLabels, 
      TermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 10, 1.0), 
      3, KMEANS_PP_CENTERS, centers); 

    int colors[K]; 
    for(int i=0; i<K; i++) { 
     colors[i] = 255/(i+1); 
    } 
    // i think there is a better way to do this mayebe some Mat::reshape? 
    clustered = Mat(src.rows, src.cols, CV_32F); 
    for(int i=0; i<src.cols*src.rows; i++) { 
     clustered.at<float>(i/src.cols, i%src.cols) = (float)(colors[bestLabels.at<int>(0,i)]); 
//  cout << bestLabels.at<int>(0,i) << " " << 
//    colors[bestLabels.at<int>(0,i)] << " " << 
//    clustered.at<float>(i/src.cols, i%src.cols) << " " << 
//    endl; 
    } 

    clustered.convertTo(clustered, CV_8U); 
    imshow("clustered", clustered); 

    waitKey(); 
    return 0; 
} 

Wynik:

Posterized Fruit

+0

może po prostu, że trzeba więcej iteracji i/lub mniejszy epsilon. Proponuję teraz spróbować usunąć "CV_TERMCRIT_EPS" i grać z liczbą iteracji w TermCriteria. Sprawdź, czy to pomaga. –

+0

Właśnie zbliżam się do wizji komputerowej, przetwarzania obrazu i uczenia maszynowego, ale dla mnie jest inny błąd w tym, co robię, a nie tylko parametr dostrajania .. – nkint

+0

Nie sugeruję, abyś zaczął dostrajanie parametru, jestem sugerując uproszczenie kodu w celu sprawdzenia, czy to, co próbujesz, działa w jego najbardziej podstawowej formie. Usunięcie epsilon i zwiększenie liczby iteracji usuwa niepotrzebne oszustwa. –

Odpowiedz

8

Nie jestem ekspertem w OpenCV więc dam ogólne porady, które odnosi się do Twojego pytania k-średnich trwa listę wektorów, które jest zasadniczo matryca:

[x0, y0, r0, g0, b0] 
[x1, y1, r1, g1, b1] 
[x2, y2, r2, g2, b2] 
. 
. 
. 

Dajesz mu obraz, który nie zadziała. Najpierw musisz przekonwertować obraz do tego formatu matrycy k-średnich. Dla każdego piksela obrazu źródłowego masz jeden wiersz w wynikowej macierzy. Należy również pamiętać, że należy skalować wartości tak, aby wszystkie miały podobne wartości. Jeśli tego nie zrobisz, współrzędne x i y będą zwykle miały znacznie wyższą "grawitację" niż kolor, który prowadzi do niezadowalających wyników. C++ pseudokod:

int pixel_index = 0; 
for (int y = 0; y < image height; y++) { 
    for (int x = 0; x < image width; x++) { 
    matrix[pixel_index][0] = (float)x/image width; 
    matrix[pixel_index][1] = (float)y/image height; 
    matrix[pixel_index][2] = (float)pixel(x, y).r/255.0f; 
    matrix[pixel_index][3] = (float)pixel(x, y).g/255.0f; 
    matrix[pixel_index][4] = (float)pixel(x, y).b/255.0f; 
    } 
} 
// Pass the matrix to kmeans... 

W rezultacie otrzymasz etykiety każdego piksela, który odpowiada klastra został przypisany. Następnie należy określić kolor klastra - może to różnić się od przyjęcia wartości koloru środkowego piksela do obliczenia średniego/medianowego koloru klastra. Po określeniu koloru, po prostu chodzić obraz i ustawić ich kolory pikseli klastra:

for (int y = 0; y < image height; y++) { 
    for (int x = 0; x < image width; x++) { 
    int index = y * image width + x; // This corresponds to pixel_index above 
    int cluster_index = labels[index]; // 0 to 7 in your case 
    Color color = colors[cluster_index]; // Colors is an array of 8 colors of the clusters 
    image.setpixel(x, y, color) 
    } 
} 

Jeśli wolisz używać HSV zamiast RGB, wystarczy użyć wartości HSV zamiast jedynek RGB.

Jest możliwe, że OpenCV ma funkcje, które wykonują dokładnie taką samą konwersję, jak opisałem powyżej, ale nie mogłem ich szybko znaleźć za pomocą Google.

+0

Przepraszam, ale gdzie mogę znaleźć informacje na temat tego formatu wejściowego kmeans? – nkint

+0

W dokumentacji OpenCV (http: // opencv. willowgarage.com/documentation/cpp/clustering_and_search_in_multi-dimensional_spaces.html): 'samples - macierz zmiennoprzecinkowa próbek wejściowych, jeden wiersz na próbkę' gdzie próbka oznacza punkt wielowymiarowy. W przypadku obrazu kolorowego punkt 5 wymiarów (x, y, r, g, b) Jest to normalny sposób na tworzenie kmeans, OpenCV po prostu wyraża je za pomocą własnych struktur danych. Dla ogólnego wprowadzenia dla kme polecam filmy instruktażowe Machine o milionach na http : //www.ml-class.org. –

+0

Jestem już subskrybowany, następny kurs musi się rozpocząć! :) – nkint

8

Jeśli nie trzeba współrzędne x, y w swoim k-średnich, można zorganizować dane znacznie szybciej, co następuje przy użyciu polecenia przekształcenia:

int origRows = img.rows; 
    notes << "original image is: " << img.rows << "x" << img.cols << endl; 
    Mat colVec = img.reshape(1, img.rows*img.cols); // change to a Nx3 column vector 
    cout << "colVec is of size: " << colVec.rows << "x" << colVec.cols << endl; 
    Mat colVecD, bestLabels, centers, clustered; 
    int attempts = 5; 
    int clusts = 8; 
    double eps = 0.001; 
    colVec.convertTo(colVecD, CV_32FC3, 1.0/255.0); // convert to floating point 
    double compactness = kmeans(colVecD, clusts, bestLabels, 
     TermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, attempts, eps), 
     attempts, KMEANS_PP_CENTERS, centers); 
    Mat labelsImg = bestLabels.reshape(1, origRows); // single channel image of labels 
    cout << "Compactness = " << compactness << endl; 
+0

Dobrze! niezły sposób, szukałem łatwego sposobu na zrobienie tego! dzięki! – nkint

+0

@zzzz Współrzędne wektorów cech powinny pomóc w "spójności przestrzennej", prawda? Oznacza to, że wolałaby grupować piksele o podobnych kolorach ORAZ są blisko siebie. –

Powiązane problemy