2013-03-25 14 views
10

Ich implementiere einen Kuwahara-Filter in C++, mit OpenCV zum Öffnen und Anzeigen von Bildern. Die Idee ist ziemlich geradlinig, aber irgendwie habe ich komisches Ergebnis daraus bekommen. Hier die cose:Seltsames Ergebnis aus dem Kuwahara-Filter

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

Und hier ist das Ergebnis: enter image description here

Wie Sie es anders als das richtige Ergebnis sehen können, die Grenzen dieser Limes scheinen dupliziert zu werden und nach oben bewegt. Wenn ich einen 15x15 Filter anwenden, es gibt mir ein völliges Durcheinander wie folgt aus:

enter image description here

ich meine ganzen Tag zu debuggen ausgegeben haben, aber bisher nichts gefunden wird. Ich habe sogar kleine Bilder von Hand berechnet und mit dem Ergebnis verglichen und sehe keine Unterschiede. Kann mir jemand helfen, herauszufinden, was ich falsch gemacht habe? Vielen vielen Dank.

+1

Es gibt eine Menge Code, um dort zu überprüfen, aber eine unmittelbare Sache, die ich bemerkte, ist in Ihrer 'var()' -Funktion Sie verwenden Integer-Arithmetik an Orten und dies kann zu einigen Kürzungen von Werten führen. Ich bin mir nicht sicher, ob es mit der erwarteten Reichweite signifikant sein wird, aber es lohnt sich, alles mit doppelter Genauigkeit zu machen, um zu sehen, ob das einen Unterschied macht. –

+0

@roger_rowland Vielen Dank, dass Sie sich die Zeit genommen haben, meinen Code zu lesen und auf diesen Fehler hinzuweisen. Das Ergebnis ändert sich jedoch nicht, wenn ich 'double' anstelle von' int' verwende und alle arithmetischen Berechnungen dazu zwinge, mit 'double' umzugehen. –

+3

Das wird Ihr Problem nicht lösen, aber es wäre gut, 'std :: numeric_limits :: max()' zu verwenden, anstatt manuell den Maximalwert von 'double' zu ​​schreiben. Außerdem sollten Sie Konstanten verwenden, um Ihre Bereiche zu benennen, sagen Sie "UPPER_LEFT" anstelle von "1". Wie gesagt, diese Details werden Ihr Problem nicht lösen, aber die Leute werden eher helfen, wenn Ihr Code einfach zu lesen und selbstdokumentiert ist :) – Morwenn

Antwort

6

Es stellt sich heraus, dass mit meinem Code nichts falsch ist, aber die Art, wie ich einen Kernel definiert habe, war die Ursache des Problems. Mein Kernel ist eigentlich einer von vier kleinen Kuwahara-Abschnitten, während die korrekte Definition eines Kernels der gesamte Bereich ist, in dem Daten für jedes Pixel berechnet werden. Daher ist der Bereich, der alle vier Abschnitte enthält, tatsächlich der Kernel. Also, wenn über einen 7x7 "Kernel" gesprochen wurde, habe ich tatsächlich einen 15x15 angewendet, und das schreckliche Ergebnis kam nicht von einem 15x15 Kernel, wie ich dachte, sondern von einem 31x31. Bei dieser Größe macht Kuwahara Filter einfach keinen Sinn und bizarre Ergebnisse sind unvermeidlich.

Verwandte Themen