2017-12-18 227 views
0

我想找到圖片上的主要N種顏色。爲此我決定使用KMeans算法。我寫在C上的項目,就是我用cvKMeans2算法。但它給了我很奇怪的結果。然後我決定在OpenCV C++上嘗試kmeans算法。它給了我更準確的結果。那麼,我的錯在哪裏?有人可以向我解釋嗎?OpenCV中kmeans和cvKMeans2算法有什麼區別?

1.我用這張圖片進行測試。

Test image

2. C.

#include <cv.h> 
#include <highgui.h> 

#define CLUSTERS 3 


int main(int argc, char **argv) { 

    const char *filename = "test_12.jpg"; 
    IplImage *tmp = cvLoadImage(filename); 
    if (!tmp) { 
     return -1; 
    } 

    IplImage *src = cvCloneImage(tmp); 
    cvCvtColor(tmp, src, CV_BGR2RGB); 

    CvMat *samples = cvCreateMat(src->height * src->width, 3, CV_32F); 
    for (int i = 0; i < samples->height; i++) { 
     samples->data.fl[i * 3 + 0] = (uchar) src->imageData[i * 3 + 0]; 
     samples->data.fl[i * 3 + 1] = (uchar) src->imageData[i * 3 + 1]; 
     samples->data.fl[i * 3 + 2] = (uchar) src->imageData[i * 3 + 2]; 
    } 

    CvMat *labels = cvCreateMat(samples->height, 1, CV_32SC1); 
    CvMat *centers = cvCreateMat(CLUSTERS, 3, CV_32FC1); 

    int flags = 0; 
    int attempts = 5; 
    cvKMeans2(samples, CLUSTERS, labels, 
       cvTermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 10000, 0.005), 
       attempts, 0, flags, centers); 

    int rows = 40; 
    int cols = 300; 
    IplImage *des = cvCreateImage(cvSize(cols, rows), 8, 3); 

    int part = 4000; 
    int r = 0; 
    int u = 0; 
    for (int y = 0; y < 300; ++y) { 
     for (int x = 0; x < 40; ++x) { 
      if (u >= part) { 
       r++; 
       part = (r + 1) * part; 
      } 
      des->imageData[(300 * x + y) * 3 + 0] = static_cast<char>(centers->data.fl[r * 3 + 0]); 
      des->imageData[(300 * x + y) * 3 + 1] = static_cast<char>(centers->data.fl[r * 3 + 1]); 
      des->imageData[(300 * x + y) * 3 + 2] = static_cast<char>(centers->data.fl[r * 3 + 2]); 
      u++; 
     } 
    } 

    IplImage *dominant_colors = cvCloneImage(des); 
    cvCvtColor(des, dominant_colors, CV_BGR2RGB); 

    cvNamedWindow("dominant_colors", CV_WINDOW_AUTOSIZE); 
    cvShowImage("dominant_colors", dominant_colors); 
    cvWaitKey(0); 
    cvDestroyWindow("dominant_colors"); 

    cvReleaseImage(&src); 
    cvReleaseImage(&des); 
    cvReleaseMat(&labels); 
    cvReleaseMat(&samples); 
    return 0; 
} 

3.第C實施實施++。

#include <cv.h> 
#include <opencv/cv.hpp> 

#define CLUSTERS 3 

int main(int argc, char **argv) { 
    const cv::Mat &tmp = cv::imread("test_12.jpg"); 
    cv::Mat src; 
    cv::cvtColor(tmp, src, CV_BGR2RGB); 

    cv::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<cv::Vec3b>(y, x)[z]; 

    int attempts = 5; 
    cv::Mat labels; 
    cv::Mat centers; 

    kmeans(samples, CLUSTERS, labels, cv::TermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 1000, 0.005), 
      attempts, cv::KMEANS_PP_CENTERS, centers); 

    cv::Mat colors(cv::Size(CLUSTERS * 100, 30), tmp.type()); 
    int p = 100; 
    int cluster_id = 0; 
    for (int x = 0; x < CLUSTERS * 100; x++) { 
     for (int y = 0; y < 30; y++) { 
      if (x >= p) { 
       cluster_id++; 
       p = (cluster_id + 1) * 100; 
      } 
      colors.at<cv::Vec3b>(y, x)[0] = static_cast<uchar>(centers.at<float>(cluster_id, 0)); 
      colors.at<cv::Vec3b>(y, x)[1] = static_cast<uchar>(centers.at<float>(cluster_id, 1)); 
      colors.at<cv::Vec3b>(y, x)[2] = static_cast<uchar>(centers.at<float>(cluster_id, 2)); 
     } 
    } 

    cv::Mat dominant_colors; 
    cv::cvtColor(colors, dominant_colors, CV_RGB2BGR); 
    cv::imshow("dominant_colors", dominant_colors); 
    cv::waitKey(0); 

    return 0; 
} 

4. C.

的代碼結果

enter image description here

5.對C代碼結果++。

enter image description here

+1

有沒有區別,它的代碼相同的底層位。有什麼不同的是你調用它們的參數 - TermCriteria和flags。最重要的是,C和C++實現之間還有其他幾個細微差別。 –

+0

我認爲標準之間沒有顯着差異。所以我意識到我犯了錯誤。 IplImage有一個字段寬度步驟。它被填充到4的倍數。 –

回答

0

我發現我錯了。它與IplImage的widthStep字段相關。當我讀here寬度步驟由於性能原因被填充到4的倍數。如果widthStep等於30,將填補高達32

int h = src->height; 
int w = src->width; 
int c = 3; 
int delta = 0; 
for (int i = 0, y = 0; i < h; ++i) { 
    for (int j = 0; j < w; ++j) { 
     for (int k = 0; k < c; ++k, y++) { 
      samples->data.fl[i * w * c + c * j + k] = (uchar) src->imageData[delta + i * w * c + c * j + k]; 
     } 
    } 
    delta += src->widthStep - src->width * src->nChannels; 
} 

隨着指針

for (int x = 0, i = 0; x < src->height; ++x) { 
    auto *ptr = (uchar *) (src->imageData + x * src->widthStep); 
    for (int y = 0; y < src->width; ++y, i++) { 
     for (int j = 0; j < 3; ++j) { 
      samples->data.fl[i * 3 + j] = ptr[3 * y + j]; 
     } 
    } 
} 
相關問題