2012-03-05 65 views
17

我想用k-means和OpenCV在C++界面(cv命名空間)中張貼圖像,我得到奇怪的結果。我需要它來減少一些噪音。這是我的代碼:OpenCV使用k-means來張貼圖像

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

,但我得到一個奇怪的結果

Fruit

第一張圖片:原

2圖像:後K-均值。

有什麼建議嗎?


更新:正確的解決方案。也許有人可以幫助我優化代碼?

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

結果:

Posterized Fruit

+0

這可能僅僅是因爲你需要更多的迭代和/或更小的epsilon。我建議你現在嘗試移除「CV_TERMCRIT_EPS」,並使用TermCriteria中的迭代次數。看看是否有幫助。 – 2012-03-06 09:22:14

+0

我只是接近計算機視覺,圖像處理和機器學習,但對我來說,我在做什麼,而不僅僅是一個參數微調,另一個錯誤。 – nkint 2012-03-06 12:19:54

+0

我不是說你開始參數調整,我是建議您簡化代碼以測試您嘗試的最基本形式。刪除epsilon並增加迭代次數可消除不必要的欺騙。 – 2012-03-06 12:31:00

回答

8

我在OpenCV中沒有專家,所以我會給涉及到您的問題K-裝置採用向量的列表,它本質上是一個矩陣一般建議:

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

您正在給它一個不起作用的圖像。您首先必須將圖像轉換爲這種k-means矩陣格式。對於源圖像的每個像素,在結果矩陣中都有一行。另請注意,您應該縮放這些值,以使它們都具有相似的值。如果你不這樣做,x和y座標通常比導致不滿意結果的顏色具有高得多的「重力」。 C++僞代碼:

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

結果,你會得到相當於已分配到集羣的每個單獨像素的標籤。然後您需要確定羣集的顏色 - 這可以從採用中心像素顏色值到計算羣集的平均/中值顏色而變化。您確定顏色後,只是走的形象,並設置像素的顏色集羣:

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

如果你喜歡使用HSV而不是RGB,只是使用的不是RGB那些HSV值。

OpenCV可能具有執行上述轉換的功能,但我無法使用Google快速找到它們。

+0

對不起,但我在哪裏可以找到關於這種kmeans特定輸入格式的信息? – nkint 2012-03-07 22:46:35

+0

在OpenCV文檔(http:// opencv。 「樣本 - 輸入樣本的浮點矩陣,每個樣本一行」,其中樣本表示多維點,對於彩色圖像,點具有5個維度(x,y,r,g,b)。這是做kmeans的標準方法,OpenCV只是用它自己的數據結構來表示它。對於一般kmeans的介紹,我推薦關於kmeans的機器學習視頻://www.ml-class.org。 – 2012-03-07 23:08:06

+0

我已經訂閱了下一個課程已經開始了! :) – nkint 2012-03-07 23:37:17

8

如果您不需要x,y座標在K-手段,可以多安排數據更快使用重塑命令如下:

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

不錯!好方法,我正在尋找一個簡單的方法來做到這一點!謝謝! – nkint 2012-06-28 15:51:59

+0

@zzzz特徵向量中的座標應該有助於「空間一致性」,對嗎?也就是說,它傾向於將具有相似顏色和彼此靠近的像素分組。 – 2016-11-12 14:00:49