2013-03-25 13 views
10

Sto implementando un filtro Kuwahara in C++, con OpenCV per facilitare l'apertura e la visualizzazione delle immagini. L'idea è abbastanza semplice, ma in qualche modo ho ottenuto risultati strani da esso. Qui' l'Cose:Strano risultato dal filtro 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(); 
} 

Ed ecco il risultato: enter image description here

Come si può vedere che è diverso dal risultato corretto, i confini di quei tigli sembrano essere duplicati e spostati verso l'alto. Se applico un filtro 15x15, mi dà un disastro completo come questo:

enter image description here

Ho passato tutta la mia giornata per eseguire il debug, ma finora nulla è stato trovato. Ho persino eseguito il calcolo su piccole immagini a mano e confrontato con il risultato e non vedo differenze. Qualcuno potrebbe aiutarmi a scoprire cosa ho fatto di sbagliato? Molte molte grazie.

+1

C'è un sacco di codice da controllare, ma una cosa immediata che ho notato è nella funzione 'var()' stai usando l'aritmetica dei numeri interi in alcuni punti e questo può portare a qualche troncamento dei valori. Non sono sicuro se sarà significativo con l'intervallo previsto, ma vale la pena fare tutto con una precisione doppia per vedere se questo fa alcuna differenza. –

+0

@roger_rowland Grazie per aver dedicato tempo a leggere il mio codice e far notare questo errore. Tuttavia, il risultato non cambia quando uso 'double' invece di' int' e costringendo tutti i calcoli aritmetici a trattare con 'double'. –

+3

Questo non risolverà il tuo problema, ma sarebbe una buona pratica usare 'std :: numeric_limits :: max()' invece di scrivere manualmente il valore massimo di 'double'. Inoltre, dovresti usare le costanti per nominare le tue aree, dì "UPPER_LEFT" invece di "1". Come ho già detto, questi dettagli non risolveranno il tuo problema, ma le persone saranno più propensi ad aiutarti se il tuo codice è facile da leggere e autodocumentato :) – Morwenn

risposta

6

Si scopre che non c'è niente di sbagliato nel mio codice, ma il modo in cui ho definito un kernel è stata la fonte del problema. Il mio kernel è in realtà una delle quattro piccole sezioni di kuwahara, mentre la corretta definizione di un kernel è l'intera area in cui i dati vengono calcolati per ciascun pixel, quindi l'area che contiene tutte e quattro le sezioni è in realtà il kernel. Quindi, quando ho parlato di un "kernel" 7x7, in realtà ho applicato uno 15x15, e il risultato orribile non proveniva da un kernel 15x15 come pensavo, ma da un 31x31. A quelle dimensioni, il filtro Kuwahara semplicemente non ha senso e risultati bizzarri sono inevitabili.

Problemi correlati