2013-03-25 56 views
10

我正在用C++實現一個科威拉過濾器,使用OpenCV來幫助打開和顯示圖像。這個想法很直接,但不知何故,我從它得到了一個奇怪的結果。這裏的COSE:來自Kuwahara過濾器的奇怪結果

#include "opencv2/opencv.hpp" 
#include <iostream> 
#include <iomanip> 
#include <cmath> 

using namespace std; 
using namespace cv; 

//This class is essentially a struct of 4 Kuwahara regions surrounding a pixel, along with each one's mean, sum and variance. 
class Regions{ 
    int* Area[4]; 
    int Size[4]; 
    unsigned long long Sum[4]; 
    double Var[4]; 
    int kernel; 
public: 
    Regions(int _kernel) : kernel(_kernel) { 
     for (int i = 0; i<4; i++) { 
      Area[i] = new int[kernel*kernel]; 
      Size[i] = 0; 
      Sum[i] = 0; 
      Var[i] = 0.0; 
     } 
    } 

    //Update data, increase the size of the area, update the sum 
    void sendData(int area, int data){ 
     Area[area][Size[area]] = data; 
     Sum[area] += data; 
     Size[area]++; 
    } 
    //Calculate the variance of each area 
    double var(int area) { 
     int __mean = Sum[area]/Size[area]; 
     double temp = 0; 
     for (int i = 0; i<Size[area]; i++) { 
      temp+= (Area[area][i] - __mean) * (Area[area][i] - __mean); 
     } 
     if (Size[area]==1) return 1.7e38; //If there is only one pixel inside the region then return the maximum of double 
              //So that with this big number, the region will never be considered in the below minVar() 
     return sqrt(temp/(Size[area]-1)); 
    } 
    //Call the above function to calc the variances of all 4 areas 
    void calcVar() { 
     for (int i = 0; i<4; i++) { 
      Var[i] = var(i); 
     } 
    } 
    //Find out which regions has the least variance 
    int minVar() { 
     calcVar(); 
     int i = 0; 
     double __var = Var[0]; 
     if (__var > Var[1]) {__var = Var[1]; i = 1;} 
     if (__var > Var[2]) {__var = Var[2]; i = 2;} 
     if (__var > Var[3]) {__var = Var[3]; i = 3;} 
     return i; 
    } 

    //Return the mean of that regions 
    uchar result(){ 
     int i = minVar(); 
     return saturate_cast<uchar> ((double) (Sum[i] *1.0/Size[i])); 
    } 
}; 

class Kuwahara{ 
private: 
    int wid, hei, pad, kernel; 
    Mat image; 
public: 
    Regions getRegions(int x, int y){ 
     Regions regions(kernel); 

     uchar *data = image.data; 

     //Update data for each region, pixels that are outside the image's boundary will be ignored. 

     //Area 1 (upper left) 
     for (int j = (y-pad >=0)? y-pad : 0; j>= 0 && j<=y && j<hei; j++) 
      for (int i = ((x-pad >=0) ? x-pad : 0); i>= 0 && i<=x && i<wid; i++) { 
       regions.sendData(1,data[(j*wid)+i]); 
      } 
     //Area 2 (upper right) 
     for (int j = (y-pad >=0)? y-pad : 0; j<=y && j<hei; j++) 
      for (int i = x; i<=x+pad && i<wid; i++) { 
       regions.sendData(2,data[(j*wid)+i]); 
      } 
     //Area 3 (bottom left) 
     for (int j = y; j<=y+pad && j<hei; j++) 
      for (int i = ((x-pad >=0) ? x-pad : 0); i<=x && i<wid; i++) { 
       regions.sendData(3,data[(j*wid)+i]); 
      } 
     //Area 0 (bottom right) 
     for (int j = y; j<=y+pad && j<hei; j++) 
      for (int i = x; i<=x+pad && i<wid; i++) { 
       regions.sendData(0,data[(j*wid)+i]); 
      } 
     return regions; 
    } 

    //Constructor 
    Kuwahara(const Mat& _image, int _kernel) : kernel(_kernel) { 
     image = _image.clone(); 
     wid = image.cols; hei = image.rows; 
     pad = kernel-1; 
    } 

    //Create new image and replace its pixels by the results of Kuwahara filter on the original pixels 
    Mat apply(){ 
     Mat temp; 
     temp.create(image.size(), CV_8U); 
     uchar* data = temp.data; 

     for (int j= 0; j<hei; j++) { 
      for (int i = 0; i<wid; i++) 
       data[j*wid+i] = getRegions(i,j).result(); 
     } 
     return temp; 
    } 
}; 

int main() { 
    Mat img = imread("limes.tif", 1); 
    Mat gray, dest; 
    int kernel = 15; 
    gray.create(img.size(), CV_8U); 
    cvtColor(img, gray, CV_BGR2GRAY); 

    Kuwahara filter(gray, kernel); 

    dest = filter.apply(); 

    imshow("Result", dest); 
    imwrite("result.jpg", dest); 
    waitKey(); 
} 

而這裏的結果: enter image description here

正如你可以看到它是從正確的結果不同,這些石灰的邊界似乎被複制和向上移動。如果我申請一個15×15濾波器,它給了我一個完整搞成這個樣子:

enter image description here

我已經花了我整整一天來調試,但至今沒有找到。我甚至用手對小圖像進行了計算,並與結果進行比較,看到沒有差異。 任何人都可以幫我找出我做錯了什麼? 很多很多,謝謝。

+1

這裏有很多代碼需要檢查,但我注意到的一個直接的事情是在你的'var()'函數中使用了整數算術,這可能導致值的截斷。我不確定在預期的範圍內它是否會顯着,但是以雙精度來做所有事情是值得的,看看它是否有所作爲。 – 2013-03-25 06:04:17

+0

@roger_rowland感謝您花時間閱讀我的代碼並指出錯誤。但是,當我使用'double'而不是'int'並強制所有算術計算處理'double'時,結果不會改變。 – 2013-03-25 06:22:42

+3

這不會解決你的問題,但是使用'std :: numeric_limits :: max()'而不是手動寫入'double'的最大值會是一個好的實踐。另外,你應該使用常量來命名你的區域,比如'UPPER_LEFT'而不是'1'。正如我所說,這些細節不會解決您的問題,但如果您的代碼易於閱讀和自我記錄,人們將更有可能提供幫助:) – Morwenn 2013-03-25 15:06:37

回答

6

事實證明我的代碼沒有問題,但我定義內核的方式是問題的根源。我的內核實際上是四個小的kuwahara部分之一,而內核的正確定義是每個像素計算數據的整個區域,因此包含所有四個部分的區域實際上是內核。所以當談到一個7x7的「內核」時,我實際上應用了一個15x15的內核,可怕的結果並非來自我想的15x15內核,而是來自31x31。在這樣的尺寸下,科威拉過濾器根本沒有意義,奇怪的結果是不可避免的。