2013-11-15 12 views
5

Sto tentando di determinare la direzione del gradiente dell'immagine utilizzando i risultati del metodo Sobel di openCV.Determinazione della direzione del gradiente dell'immagine da sobel?

Capisco che questo dovrebbe essere un compito molto semplice. Ho copiato i metodi da un numero di risorse e risposte da qui, ma qualunque cosa facciamo, le direzioni risultanti sono sempre tra 0 e 57 gradi (mi aspetterei che l'intervallo sia compreso tra 0 e 360).

Credo che tutte le profondità siano corrette. Ho provato a calcolare la direzione utilizzando i dati 16S e i dati 8U.

Non riesco a vedere dove sto andando male? Qualcuno può individuare il mio errore?

void getGradients(IplImage* original, cv::Mat* gradArray) 
{ 
    cv::Mat original_Mat(original, true); 

    // Convert it to gray 
    cv::cvtColor(original_Mat, original_Mat, CV_RGB2GRAY); 
    //cv::blur(original_Mat, original_Mat, cv::Size(7,7)); 

    /// Generate grad_x and grad_y 
    cv::Mat grad_x = cv::Mat::zeros(original->height, original->width, CV_16S); 
    cv::Mat grad_y = cv::Mat::zeros(original->height, original->width, CV_16S); 

    cv::Mat abs_grad_x = cv::Mat::zeros(original->height, original->width, CV_8U); 
    cv::Mat abs_grad_y = cv::Mat::zeros(original->height, original->width, CV_8U);; 

    /// Gradient X 
    cv::Sobel(original_Mat, grad_x, CV_16S, 1, 0, 3); 
    cv::convertScaleAbs(grad_x, abs_grad_x); 

    /// Gradient Y 
    cv::Sobel(original_Mat, grad_y, CV_16S, 0, 1, 3); 
    cv::convertScaleAbs(grad_y, abs_grad_y); 

    uchar* pixelX = abs_grad_x.data; 
    uchar* pixelY = abs_grad_y.data; 
    uchar* grad1 = gradArray[0].data; 
    uchar* grad2 = gradArray[1].data; 
    uchar* grad3 = gradArray[2].data; 
    uchar* grad4 = gradArray[3].data; 
    uchar* grad5 = gradArray[4].data; 
    uchar* grad6 = gradArray[5].data; 
    uchar* grad7 = gradArray[6].data; 
    uchar* grad8 = gradArray[7].data; 
    int count = 0; 
    int min = 999999; 
    int max = 0; 

    for(int i = 0; i < grad_x.rows * grad_x.cols; i++) 
    { 
      int directionRAD = atan2(pixelY[i], pixelX[i]); 
      int directionDEG = directionRAD/PI * 180; 

      if(directionDEG < min){min = directionDEG;} 
      if(directionDEG > max){max = directionDEG;} 

      if(directionDEG >= 0 && directionDEG <= 45)   { grad1[i] = 255; count++;}   
      if(directionDEG >= 45 && directionDEG <= 90)  { grad2[i] = 255; count++;}   
      if(directionDEG >= 90 && directionDEG <= 135)  { grad3[i] = 255; count++;}   
      if(directionDEG >= 135 && directionDEG <= 190)  { grad4[i] = 255; count++;}   
      if(directionDEG >= 190 && directionDEG <= 225)  { grad5[i] = 255; count++;}   
      if(directionDEG >= 225 && directionDEG <= 270)  { grad6[i] = 255; count++;}  
      if(directionDEG >= 270 && directionDEG <= 315)  { grad7[i] = 255; count++;} 
      if(directionDEG >= 315 && directionDEG <= 360)  { grad8[i] = 255; count++;} 

      if(directionDEG < 0 || directionDEG > 360) 
      { 
       cout<<"Weird gradient direction given in method: getGradients."; 
      }    
    } 
} 

risposta

6

Stai usando aritmetica intera in modo che i calcoli per radianti e gradi stanno soffrendo gravemente da troncamento.

anche atan2 dà un risultato nella gamma -PI-+PI, quindi se volete un valore in gradi nella gamma 0..360 avrete bisogno di aggiungere una correzione a 180 gradi:

 double directionRAD = atan2(pixelY[i], pixelX[i]); 
     int directionDEG = (int)(180.0 + directionRAD/M_PI * 180.0); 

Nota l'uso di double anziché di int per directionRAD.

Suggerimento pro: impara a utilizzare un debugger per scorrere il codice, ispezionando le variabili man mano che procedi - questo renderà la risoluzione di bug semplici come questa molto più semplice dell'attesa di risposte su StackOverflow.

+0

Grazie per il consiglio Paul. Ho corretto il problema del troncamento come suggerito ma non ha fatto molta differenza. Ricevo ancora un numero limitato di gradienti da 0 a 90 (o da 180 a 270 con la correzione a 180 gradi applicata). Non ridimensiono più i valori e quindi utilizzo i valori 16S non elaborati forniti dall'operazione Sobel. Ho attraversato il processo per un'intera immagine ma non riesco a vedere dove sta andando male. Hai qualche idea? Grazie. – CVirtuous

+0

Hai rimosso anche l'operazione abs? Se è così, allora ti suggerisco di pubblicare il tuo ultimo codice come una nuova domanda. –

+0

Sì, l'ho fatto. Pubblicherò una nuova domanda ora. Grazie. – CVirtuous

2

Si prende e il valore assoluto dei gradienti, che mappa tutti gli angoli da [-180; Da 180] a [0; 90]. Inoltre usi la divisione intera.

+0

Grazie old-ufo. Ho corretto il problema della divisione intera come da consiglio di Paul e utilizzo solo i dati 16S direttamente dall'operazione Sobel.Tuttavia ottengo ancora un intervallo limitato di indicazioni da 0 a 90. Riesci a vedere qualcos'altro che non funziona nel mio codice? – CVirtuous

+0

Per favore, aggiorna il codice nel tuo post, così potremmo trovare un bug nella versione _current_. –

4

È possibile ottenere la derivata x dx e la derivata Y dy utilizzando l'operatore Sobel. Quindi puoi usare la formula per calcolare l'ampiezza e la direzione del gradiente. G=sqrt(dx^2+dy^2), theta=arctan(dy/dx). È possibile trovare questo è solo convertire il sistema di coordinate di Descartes (x, y) in coordinate polari (rho, theta)!

C'è qualcosa di sbagliato nel codice che si ottiene il valore assoluto di dx e dy, che rende sempre la direzione nel primo quadrante del sistema di coordinate cartesiane. E la funzione che hai usato convertScaleAbs converte il risultato in 8-bit, il che si traduce nell'errore di troncamento.

Ho una demo per calcolare la magnitudine in parte in base al codice.

const string imgname = "F:/OpenCV/square.jpg"; 
    Mat img = imread(imgname, CV_LOAD_IMAGE_COLOR); 

    // 1. convert it to gray value 
    Mat gray; 
    cvtColor(img, gray, CV_BGR2GRAY); 
    // 2. blur the image 
    blur(gray, gray, Size(7, 7)); 
    // 3. sobel 
    Mat grad_x, grad_y; 
    Scharr(gray, grad_x, CV_32FC1, 1, 0); 
    Scharr(gray, grad_y, CV_32FC1, 0, 1); 
    // 4. calculate gradient magnitude and direction 
    Mat magnitude, direction; 
    bool useDegree = true; // use degree or rad 
    // the range of the direction is [0,2pi) or [0, 360) 
    cartToPolar(grad_x, grad_y, magnitude, direction, useDegree); 

    // test, the histogram of the directions 
    vector<int> cnt(8, 0); // 0-45, 45-90, ..., 315-360 

    for(auto iter = direction.begin<float>(); iter != direction.end<float>(); ++iter) 
    { 
     int idx = static_cast<int>(*iter)/45; 
     ++cnt[idx]; 
    } 

    Mat scaled; 
    convertScaleAbs(magnitude, scaled); 
    imshow("magnitude", scaled); 
    for(auto v : cnt) 
     cout << v << " "; 

A test picture magnitude visulization result