2012-09-27 22 views
5

Tengo una gran cantidad de fotografías tomadas con una lente ojo de pez. Como quiero hacer algo de procesamiento de imágenes (por ejemplo, detección de bordes) en las fotos, quiero eliminar la distorsión del cañón que afecta mucho a mis resultados.Algoritmo de corrección de distorsión de barril para corregir la lente de ojo de pez: no se ha implementado con Java

Después de algunas investigaciones y muchos artículos de lectura encontré esto page: Describen un algoritmo (y algunas fórmulas) para resolver este problema.

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

rsrc = distancia de un píxel del centro de la imagen de origen
Rcorr = distancia de un píxel del centro en la imagen corregida
a, b, c = distorsión de la imagen d = escalado lineal de la imagen

He utilizado estos fórmulas y trató de implementar esto en una aplicación Java. Lamentablemente, no funciona y no pude hacerlo funcionar. La imagen "corregida" no se parece en nada a la fotografía original y en su lugar muestra algunos círculos misteriosos en el medio. Mira aquí:

http://imageshack.us/f/844/barreldistortioncorrect.jpg/ (esto solía ser una fotografía de una vaca blanca delante de una pared azul)

Aquí está mi código:

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; 
} 

Mis preguntas son:
1) ¿Es esta fórmula correcta?
2) ¿He cometido un error al convertir esa fórmula en una pieza de software?
3) Existen otros algoritmos (por ejemplo, How to simulate fisheye lens effect by openCV? o wiki/Distortion_ (óptica)), ¿son mejores?

Gracias por su ayuda!

+0

La cuadrícula de píxeles cerca del borde dice mucho sobre cuál es el problema. Ya sea que su algoritmo funcione o no para cualquier foto, no tengo ni idea. Una posible razón por la que no funciona es que es posible que esté sobre-corrigiendo la distorsión. – AJMansfield

+0

Como mencioné a continuación intenté establecer b en un valor infinitamente pequeño. Da un resultado diferente (ya no hay corrección esférica) pero aún no muestra la misma imagen. Vea aquí: http://imageshack.us/f/191/barreldistortioncorrect.jpg/ – Lucas

+0

¿Podría un valor infinitamente pequeño b estar sobrecorrigiendo en la _otra dirección? – AJMansfield

Respuesta

7

El error principal que tiene es que el algoritmo se especifica que r_corr y r_src están en unidades de min ((xDim-1)/2, (yDim-1)/2). Eso debe hacerse para normalizar el cálculo de modo que los valores de los parámetros no dependan del tamaño de la imagen de origen. Con el código tal como está, necesitarás usar valores mucho más pequeños para paramB, p. funcionó bien para mí con paramB = 0.00000002 (para una imagen con dimensiones 2272 x 1704).

También tiene un error al calcular la diferencia desde el centro que hace que la imagen resultante se gire 180 grados en comparación con la imagen de origen.

fijación tanto estos errores deben darle algo como esto:

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 esta versión se puede utilizar valores de los parámetros de las bases de datos existentes, como lentes LensFun (aunque tendrá que dar la vuelta al signo de cada parámetro) . La página que describe el algoritmo ahora se puede encontrar en http://mipav.cit.nih.gov/pubwiki/index.php/Barrel_Distortion_Correction

+0

¡Gracias por tu ayuda! – Lucas

+0

He estado trabajando en 360 panorama usando lentes ojo de pez. He utilizado ptiGui como referencia para la distorsión de la imagen y la costura. pero el problema es que cuando pongo los parámetros a b c que ptgui proporciona para la distorsión en su código, los resultados son muy diferentes. De hecho, el efecto de a es casi opuesto en Ptgui a partir de tu código. ¿cuál crees que podría ser el problema? –

0

Probablemente sus parámetros de distorsión radial son demasiado grandes, y la imagen se empaquetó en una esfera. Intente poner valores más pequeños en a, b, c y d.

+0

Establecer un valor infinitamente pequeño para b (y dejando a = c = 0), ya no hay esfera, pero todos los píxeles de la imagen parecen estar mezclados. Vea aquí: http://imageshack.us/f/191/barreldistortioncorrect.jpg/ Lo que me hace pensar que debe haber un problema con mi código y no con el algoritmo. Si configuro a = b = c = 0 yd = 1, todo funciona bien y la imagen no se modifica. – Lucas

0

Sus valores son muy extremos, por lo que verá resultados extremos.

Pruebe a = 0, b = 0, c = 1. Eso no describe ninguna corrección en absoluto, si su programa es correcto, debería ver la imagen original. Luego, cambie gradualmente c y b. Cambiar en incrementos de 0.1 es un buen comienzo.

2

creo que sus círculos son causados ​​por esta línea:

double srcYd = centerY + (diffX * factor); 

la que supongo debe ser:

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

¡Gracias, esto ayudó! – Lucas

Cuestiones relacionadas