2013-03-25 9 views
10

OpenCVで画像を開いて表示するための桑原フィルターをC++で実装しています。アイデアはかなりストレートですが、どういうわけか私はそこから奇妙な結果を得ました。ここにCOSE:桑原フィルターの奇妙な結果

#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()'関数にあります。これは、整数演算を使用しているため、値が切り捨てられることがあります。予想される範囲ではそれが重要かどうかはわかりませんが、それが何か違いがあるかどうかを確認するには倍精度ですべてを行う価値があります。 –

+0

@roger_rowland私のコードを読んで間違いを指摘してくれてありがとう。しかし、 'int'の代わりに' double'を使い、 'double'を扱うためにすべての算術演算を強制すると、結果は変わりません。 –

+3

これで問題は解決しませんが、手動で 'double'の最大値を書くのではなく、' std :: numeric_limits :: max() 'を使うのが良いでしょう。また、定数を使って領域の名前を付けるべきです。例えば、 '1'ではなく' UPPER_LEFT'と言うべきです。私が言ったように、それらの詳細はあなたの問題を解決することはできませんが、あなたのコードが読みやすく自己文書化されている場合、人々はより助けになるでしょう:) – Morwenn

答えて

6

私のコードには何も問題はないことが判明しましたが、私がカーネルを定義した方法が問題の原因でした。私のカーネルは、実際には4つの小さなkuwaharaセクションのうちの1つですが、カーネルの正しい定義は、各ピクセルのデータが計算される領域全体であるため、4つのセクションすべてを含む領域は実際にはカーネルです。だから、7x7の "カーネル"について言えば、私は実際に15x15を適用しました。恐ろしい結果は、私が思ったように15x15のカーネルからではなく、31x31から来ました。そのサイズでは、桑原フィルターは意味をなさないだけでなく、奇妙な結果が避けられません。