2012-03-05 6 views
17

Voglio posterizzare un'immagine con k-means e OpenCV nell'interfaccia C++ (spazio dei nomi cv) e ottengo risultati strani. Ne ho bisogno per ridurre un po 'di rumore. Questo è il mio codice:OpenCV che utilizza k-means per posterizzare un'immagine

#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; 
} 

ma ottengo un risultato strano

Fruit

Prima immagine: originale

seconda immagine: dopo k-means.

Qualche consiglio?


Aggiornamento: la soluzione giusta. forse qualcuno può aiutarmi a ottimizzare il codice?

#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; 
} 

Risultato:

Posterized Fruit

+0

Potrebbe essere solo che avete bisogno di più iterazioni e/o un epsilon più piccolo. Ti suggerisco di provare a rimuovere "CV_TERMCRIT_EPS" per ora e giocare con il numero di iterazioni nel tuo TermCriteria. Vedi se questo aiuta. –

+0

Mi sto avvicinando alla visione del computer, all'elaborazione delle immagini e all'apprendimento automatico, ma per me c'è un altro errore in quello che sto facendo e non solo una regolazione dei parametri. – nkint

+0

Non sto suggerendo di iniziare la sintonizzazione dei parametri, sono suggerendo di semplificare il codice per verificare che ciò che stai cercando funzioni nella sua forma più semplice. La rimozione di epsilon e l'aumento del numero di iterazioni rimuove gli inganni non necessari. –

risposta

8

non sono un esperto in OpenCV quindi darò un consiglio generale che si riferisce alla tua domanda K-means prende l'elenco dei vettori che è essenzialmente una matrice:

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

Si sta dando un'immagine che non sta andando a lavorare. Devi prima convertire l'immagine in questo formato di matrice k-means. Per ogni pixel dell'immagine sorgente hai una riga nella matrice risultante. Si noti inoltre che è necessario ridimensionare i valori in modo che abbiano tutti valori simili. Se non lo fai, le coordinate xey di solito hanno una "gravità" molto più alta del colore che porta a risultati insoddisfacenti. C++ pseudocodice:

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... 

Di conseguenza, si ottiene etichette di ogni singolo pixel, che corrisponde al cluster è stato assegnato a. È quindi necessario determinare il colore dei cluster, che può variare dal valore del colore del pixel centrale al calcolo del colore medio/mediano del cluster. Dopo aver determinato il colore, solo a piedi l'immagine e impostare i pixel per i loro colori a grappolo:

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) 
    } 
} 

Se si preferisce utilizzare HSV invece di RGB, basta usare valori HSV al posto di quelle RGB.

È possibile che OpenCV abbia funzioni che eseguono esattamente la conversione descritta sopra ma non sono stato in grado di trovarle rapidamente utilizzando Google.

+0

scusa ma dove posso trovare informazioni su questo formato di input specifico per kmenti? – nkint

+0

Nella documentazione di OpenCV (http: // opencv. willowgarage.com/documentation/cpp/clustering_and_search_in_multi-dimensional_spaces.html): 'samples - Matrice a virgola mobile di campioni di input, una riga per campione' dove campione indica un punto multidimensionale. Nel caso di un'immagine a colori, il punto ha 5 dimensioni (x, y, r, g, b) .Questo è praticamente il modo standard per fare kmi, OpenCV lo esprime semplicemente usando le proprie strutture di dati.Per un'introduzione generale ai kmea raccomando i video di apprendimento automatico di kmeai a http : //www.ml-class.org. –

+0

sono già iscritto, il prossimo corso deve ancora cominciare! :) – nkint

8

Se non c'è bisogno di coordinate x, y nei vostri k-means, è possibile organizzare i dati molto più veloce come segue utilizzando il comando Reshape:

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

buono! bel modo, stavo cercando un modo semplice per farlo! Grazie! – nkint

+0

@zzzz Le coordinate nei vettori feature dovrebbero aiutare con "coerenza spaziale", giusto? Cioè, preferirebbe raggruppare i pixel che hanno colori simili E sono vicini l'uno all'altro. –

Problemi correlati