2012-09-27 20 views
5

Ho una grande quantità di fotografie scattate con un obiettivo fisheye. Poiché desidero eseguire alcune operazioni di elaborazione delle immagini (ad es. Rilevamento dei bordi) sulle foto, desidero rimuovere la distorsione a barilotto che influisce pesantemente sui miei risultati.Algoritmo di correzione della distorsione a barilotto per correggere l'obiettivo FishEye - impossibile implementare con Java

Dopo alcune ricerche e molti articoli letti ho trovato questo page: descrivono un algoritmo (e alcune formule) per risolvere questo problema.

M = a * Rcorr^3 + b * Rcorr^2 + c * Rcorr + d
rsrc = (a * Rcorr^3 + b * Rcorr^2 + c * Rcorr + d) * Rcorr

rsrc = distanza di un pixel dal centro dell'immagine sorgente
Rcorr = distanza di un pixel dal centro dell'immagine corretta
a, b, c = distorsione dell'immagine d = scala lineare dell'immagine

Ho usato questi formule e ha cercato di implementarlo in un'applicazione Java. Sfortunatamente non funziona e non sono riuscito a farlo funzionare. L'immagine "corretta" non assomiglia alla foto originale e mostra invece alcuni cerchi misteriosi nel mezzo. Guardate qui:

http://imageshack.us/f/844/barreldistortioncorrect.jpg/ (questo era una fotografia di una mucca bianca di fronte a una parete blu)

Ecco il mio codice:

protected int[] correction(int[] pixels) { 

    // 
    int[] pixelsCopy = pixels.clone(); 

    // parameters for correction 
    double paramA = 0.0; // affects only the outermost pixels of the image 
    double paramB = -0.02; // most cases only require b optimization 
    double paramC = 0.0; // most uniform correction 
    double paramD = 1.0 - paramA - paramB - paramC; // describes the linear scaling of the image 

    // 
    for(int x = 0; x < dstView.getImgWidth(); x++) { 
     for(int y = 0; y < dstView.getImgHeight(); y++) { 

      int dstX = x; 
      int dstY = y; 

      // center of dst image 
      double centerX = (dstView.getImgWidth() - 1)/2.0; 
      double centerY = (dstView.getImgHeight() - 1)/2.0; 

      // difference between center and point 
      double diffX = centerX - dstX; 
      double diffY = centerY - dstY; 
      // distance or radius of dst image 
      double dstR = Math.sqrt(diffX * diffX + diffY * diffY); 

      // distance or radius of src image (with formula) 
      double srcR = (paramA * dstR * dstR * dstR + paramB * dstR * dstR + paramC * dstR + paramD) * dstR; 

      // comparing old and new distance to get factor 
      double factor = Math.abs(dstR/srcR); 
      // coordinates in source image 
      double srcXd = centerX + (diffX * factor); 
      double srcYd = centerY + (diffX * factor); 

      // no interpolation yet (just nearest point) 
      int srcX = (int)srcXd; 
      int srcY = (int)srcYd; 

      if(srcX >= 0 && srcY >= 0 && srcX < dstView.getImgWidth() && srcY < dstView.getImgHeight()) { 

       int dstPos = dstY * dstView.getImgWidth() + dstX; 
       pixels[dstPos] = pixelsCopy[srcY * dstView.getImgWidth() + srcX]; 
      } 
     } 
    } 

    return pixels; 
} 

Le mie domande sono:
1) Questa formula è corretta?
2) Ho fatto un errore trasformando quella formula in un software?
3) Ci sono altri algoritmi là fuori (ad esempio How to simulate fisheye lens effect by openCV? o wiki/Distortion_ (ottica)), sono meglio?

Grazie per il vostro aiuto!

+0

La griglia quadrata di pixel vicino al bordo dice molto su quale sia il problema probabile. Che il tuo algoritmo funzioni o meno per qualsiasi foto, non ne ho idea. Una probabile ragione per cui non funziona è che potresti essere in grado di sovrascrivere la distorsione. – AJMansfield

+0

Come ho detto di seguito, ho provato a impostare b su un valore infinitamente piccolo. Dà un risultato diverso (nessuna correzione sferica più) ma non visualizza ancora la stessa immagine. Vedi qui: http://imageshack.us/f/191/barreldistortioncorrect.jpg/ – Lucas

+0

È possibile che un valore b infinitamente piccolo si annulli eccessivamente nella direzione _other_? – AJMansfield

risposta

7

Il bug principale si ha è che l'algoritmo specifica che r_corr e r_src sono in unità di min ((xDim-1)/2, (yDim-1)/2). Questo deve essere fatto per normalizzare il calcolo in modo che i valori dei parametri non dipendano dalla dimensione dell'immagine sorgente. Con il codice così com'è è necessario utilizzare valori molto più piccoli per paramB, ad es. ha funzionato bene per me con paramB = 0,00000002 (per un'immagine con dimensioni 2272 x 1704).

Si ha anche un bug nel calcolare la differenza dal centro che fa ruotare l'immagine risultante di 180 gradi rispetto all'immagine sorgente.

fissaggio entrambi questi bug dovrebbe darvi qualcosa di simile:

protected static int[] correction2(int[] pixels, int width, int height) { 
    int[] pixelsCopy = pixels.clone(); 

    // parameters for correction 
    double paramA = -0.007715; // affects only the outermost pixels of the image 
    double paramB = 0.026731; // most cases only require b optimization 
    double paramC = 0.0; // most uniform correction 
    double paramD = 1.0 - paramA - paramB - paramC; // describes the linear scaling of the image 

    for (int x = 0; x < width; x++) { 
     for (int y = 0; y < height; y++) { 
      int d = Math.min(width, height)/2; // radius of the circle 

      // center of dst image 
      double centerX = (width - 1)/2.0; 
      double centerY = (height - 1)/2.0; 

      // cartesian coordinates of the destination point (relative to the centre of the image) 
      double deltaX = (x - centerX)/d; 
      double deltaY = (y - centerY)/d; 

      // distance or radius of dst image 
      double dstR = Math.sqrt(deltaX * deltaX + deltaY * deltaY); 

      // distance or radius of src image (with formula) 
      double srcR = (paramA * dstR * dstR * dstR + paramB * dstR * dstR + paramC * dstR + paramD) * dstR; 

      // comparing old and new distance to get factor 
      double factor = Math.abs(dstR/srcR); 

      // coordinates in source image 
      double srcXd = centerX + (deltaX * factor * d); 
      double srcYd = centerY + (deltaY * factor * d); 

      // no interpolation yet (just nearest point) 
      int srcX = (int) srcXd; 
      int srcY = (int) srcYd; 

      if (srcX >= 0 && srcY >= 0 && srcX < width && srcY < height) { 
       int dstPos = y * width + x; 
       pixels[dstPos] = pixelsCopy[srcY * width + srcX]; 
      } 
     } 
    } 

    return pixels; 
} 

Con questa versione è possibile utilizzare i valori dei parametri da database di lenti esistenti come LensFun (anche se avrete bisogno di capovolgere il segno di ogni parametro) . La pagina che descrive l'algoritmo può ora essere trovata a http://mipav.cit.nih.gov/pubwiki/index.php/Barrel_Distortion_Correction

+0

Grazie per il tuo aiuto! – Lucas

+0

Ho lavorato a 360 panorami usando obiettivi fisheye. Ho usato ptiGui come riferimento per la distorsione dell'immagine e le cuciture. ma il problema è che quando metto i parametri a b c che ptgui fornisce per la distorsione nel codice, i risultati sono molto diversi. Infatti l'effetto di a è quasi opposto in Ptgui dal tuo codice. cosa pensi possa essere il problema ?? –

0

Probabilmente i parametri di distorsione radiale sono troppo grandi e l'immagine è stata compressa su una sfera. Prova a inserire valori più piccoli in a, b, c e d.

+0

Impostazione di un valore infinitamente piccolo per b (e lasciando a = c = 0), non c'è più alcuna sfera ma tutti i pixel nell'immagine sembrano confusi. Vedi qui: http://imageshack.us/f/191/barreldistortioncorrect.jpg/ Ciò che mi fa pensare che ci deve essere un problema con il mio codice e non con l'algoritmo. Se imposto a = b = c = 0 e d = 1 allora tutto funziona correttamente e l'immagine non viene modificata. – Lucas

0

I tuoi valori sono molto estremi, quindi vedi risultati estremi.

Prova a = 0, b = 0, c = 1. Questo non descrive alcuna correzione, se il tuo programma è corretto dovresti vedere l'immagine originale. Quindi modificare gradualmente c eb. La modifica degli incrementi di 0,1 è un buon inizio.

2

Penso tue cerchie sono causati da questa linea:

double srcYd = centerY + (diffX * factor); 

che sto indovinando dovrebbe essere:

double srcYd = centerY + (diffY * factor); 
+0

Grazie, questo ha aiutato! – Lucas

Problemi correlati