Blog
Calibración de la cámara Opencv
- Publicado por: Rafael Fernandez
- Categoría: Blog OpenCV Procesamiento de Imagenes
Aprenderemos sobre las distorsiones en la cámara, los parámetros intrínsecos y extrínsecos de la cámara, etc
Aprenderemos a encontrar estos parámetros, imágenes no distorsionadas, etc.
Fundamentos
Las cámaras baratas de hoy en día introducen mucha distorsión en las imágenes. Dos distorsiones principales son la distorsión radial y la distorsión tangencial.
Debido a la distorsión radial, las líneas rectas aparecerán curvadas. Su efecto es mayor a medida que nos alejamos del centro de la imagen. Por ejemplo, la imagen se muestra abajo. Pero puedes ver que el borde no es una línea recta y no coincide. Todas las líneas rectas esperadas están abultadas.
Esta distorsión se resuelve de la siguiente manera:
Del mismo modo, otra distorsión es la distorsión tangencial que ocurre porque la toma de imágenes no está perfectamente alineada paralelamente al plano de imagen. Por lo tanto, algunas áreas en la imagen pueden verse más cercanas de lo esperado. Se resuelve como sigue:
En resumen, necesitamos encontrar cinco parámetros, conocidos como coeficientes de distorsión dados por:
Además de esto, necesitamos encontrar más información, como los parámetros intrínsecos y extrínsecos de una cámara. Los parámetros intrínsecos son específicos de una cámara. Incluye información como distancia focal (f_x, f_y), centros ópticos (c_x, c_y), etc. También se llama matriz de cámara. Depende sólo de la cámara, por lo que una vez calculada, se puede almacenar para fines futuros. Se expresa como una matriz de 3×3:
Los parámetros extrínsecos corresponden a vectores de rotación y traslación que traducen las coordenadas de un punto 3D a un sistema de coordenadas.
Para las aplicaciones estéreo, estas distorsiones necesitan ser corregidas primero. Para encontrar todos estos parámetros, lo que tenemos que hacer es proporcionar algunas imágenes de muestra de un patrón bien definido (p. ej., tablero de ajedrez). Encontramos algunos puntos específicos en él (esquinas cuadradas en tablero de ajedrez). Conocemos sus coordenadas en el espacio del mundo real y conocemos sus coordenadas en imagen. Con estos datos, algún problema matemático se resuelve en segundo plano para obtener los coeficientes de distorsión. Ese es el resumen de toda la historia. Para obtener mejores resultados, necesitamos al menos 10 patrones de prueba.
Código de Calibración de la cámara
Como se mencionó anteriormente, necesitamos al menos 10 patrones de prueba para la calibración de la cámara. OpenCV viene con algunas imágenes del tablero de ajedrez (ver samples/cpp/left01. jpg — left14. jpg), así que lo utilizaremos. Para entender, considere sólo una imagen de un tablero de ajedrez. Los datos de entrada importantes necesarios para la calibración de la cámara son un conjunto de puntos 3D del mundo real y sus puntos de imagen 2D correspondientes. Los puntos de imagen 2D están bien, que podemos encontrar fácilmente en la imagen. (Estos puntos de imagen son lugares donde dos cuadrados negros se tocan entre sí en tableros de ajedrez)
¿Qué hay de los puntos 3D del espacio del mundo real? Esas imágenes se toman de una cámara estática y los tableros de ajedrez se colocan en diferentes lugares y orientaciones. Así que necesitamos conocer los valores (X, Y, Z). Pero por simplicidad, podemos decir que el tablero de ajedrez se mantuvo estacionario en el plano XY, (así que Z=0 siempre) y la cámara fue movida en consecuencia. Esta consideración nos ayuda a encontrar sólo los valores X, Y. Ahora para los valores X, Y, simplemente podemos pasar los puntos como (0,0), (1,0), (2,0),… lo que denota la ubicación de los puntos. En este caso, los resultados que obtendremos serán en la escala de tamaño del tablero de ajedrez cuadrado. Pero si conocemos el tamaño cuadrado (digamos 30 mm), y podemos pasar los valores como (0,0), (30,0), (60,0),…, obtenemos los resultados en mm. (En este caso, no sabemos el tamaño del cuadrado ya que no tomamos esas imágenes, por lo que pasamos en términos de tamaño del cuadrado).
Los puntos 3D se denominan puntos de objeto y los puntos de imagen 2D se denominan puntos de imagen.
Configuración de Calibración de la cámara
Así que para encontrar patrón en el tablero de ajedrez, usamos la función, cv2.findChessboardCorners(). También necesitamos pasar qué tipo de patrón estamos buscando, como la cuadrícula 8×8, la cuadrícula 5×5, etc. En este ejemplo, utilizamos una cuadrícula 7×6. (Normalmente un tablero de ajedrez tiene 8×8 cuadrados y 7×7 esquinas internas). Devuelve los puntos de las esquinas y el retval será True si se obtiene el patrón. Estas esquinas se colocarán en orden (de izquierda a derecha, de arriba a abajo)
Es posible que esta función no pueda encontrar el patrón requerido en todas las imágenes. Por lo tanto, una buena opción es escribir el código de tal manera que, se inicia la cámara y comprobar cada fotograma para el patrón requerido. Una vez obtenido el patrón, busque las esquinas y guárdelo en una lista. También proporciona un cierto intervalo antes de leer el siguiente cuadro para que podamos ajustar nuestro tablero de ajedrez en diferente dirección. Continúe este proceso hasta que se obtenga el número requerido de buenos patrones. Incluso en el ejemplo que se ofrece aquí, no estamos seguros de las 14 imágenes dadas, cuántas son buenas. Así que leemos todas las imágenes y tomamos las buenas.
En vez de tablero de ajedrez, podemos usar alguna cuadrícula circular, pero luego usar la función cv2.findCirclesGrid () para encontrar el patrón. Se dice que un número menor de imágenes es suficiente cuando se usa una cuadrícula circular.
Una vez que encontramos las esquinas, podemos aumentar su precisión usando cv2.cornerSubPix (). También podemos dibujar el patrón usando cv2.drawChessboardCorners (). Todos estos pasos están incluidos en el siguiente código:
import numpy as np import cv2 import glob # termination criterio criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) # preparar puntos de objeto, como (0,0,0,0), (1,0,0,0), (2,0,0,0)...., (6,5,0) objp = np.zeros((6*7,3), np.float32) objp[:,:2] = np.mgrid[0:7,0:6].T.reshape(-1,2) # Arrays para almacenar puntos de objeto y puntos de imagen de todas las imágenes. objpoints = [] # 3d point in real world space imgpoints = [] # 2d points in image plane. images = glob.glob('*.jpg') for fname in images: img = cv2.imread(fname) gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) # Encuentra las esquinas del tablero de ajedrez ret, corners = cv2.findChessboardCorners(gray, (7,6),None) # Si se encuentran, añada puntos de objeto, puntos de imagen (después de refinarlos) if ret == True: objpoints.append(objp) corners2 = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria) imgpoints.append(corners2) # Dibuja y muestra las esquinas img = cv2.drawChessboardCorners(img, (7,6), corners2,ret) cv2.imshow('img',img) cv2.waitKey(500) cv2.destroyAllWindows()
Una imagen con el patrón dibujado en ella se muestra a continuación:
Calibración
Así que ahora tenemos nuestros puntos de objeto y puntos de imagen listos para ser calibrados. Para ello utilizamos la función, cv2.calibrateCamera (). Devuelve la matriz de la cámara, coeficientes de distorsión, vectores de rotación y traslación, etc.
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1],None,None)
No deformación
Tenemos lo que estábamos intentando. Ahora podemos tomar una imagen y no distorsionarla. OpenCV viene con dos métodos, veremos ambos. Pero antes de eso, podemos refinar la matriz de la cámara basada en un parámetro de escalado libre usando cv2.getOptimalNewCameraMatrix (). Si el parámetro de escala alpha=0, devuelve la imagen sin distorsión con píxeles no deseados mínimos. Así que puede incluso eliminar algunos píxeles en las esquinas de la imagen. Si alfa=1, todos los píxeles se mantienen con algunas imágenes negras adicionales. También devuelve un ROI de imagen que se puede utilizar para recortar el resultado.
Así que tomamos una nueva imagen (left02. jpg en este caso.)
1. Usando cv2. unistort ()
Este es el camino más corto. Simplemente llame a la función y utilice el ROI obtenido arriba para recortar el resultado.
# undistort dst = cv2.undistort(img, mtx, dist, None, newcameramtx) # crop the image x,y,w,h = roi dst = dst[y:y+h, x:x+w] cv2.imwrite('calibresult.png',dst)
2. Uso del remapeo
Este es un camino curvo. Primero encuentra una función de mapeo desde una imagen distorsionada a una imagen no distorsionada. A continuación, utilica la función remap.
# undistort mapx,mapy = cv2.initUndistortRectifyMap(mtx,dist,None,newcameramtx,(w,h),5) dst = cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR) # crop the image x,y,w,h = roi dst = dst[y:y+h, x:x+w] cv2.imwrite('calibresult.png',dst)
Ambos métodos dan el mismo resultado. Vea el resultado a continuación:
Resultado de calibración
Sin calibrar:
Se puede ver en el resultado que todos los bordes son rectos.
Ahora puede almacenar la matriz de la cámara y los coeficientes de distorsión usando las funciones de escritura en Numpy (np. savez, np. savetxt etc) para usos futuros.
Error de re-proyección
El error de re-proyección da una buena estimación de cuán exactos son los parámetros encontrados. Esto debería estar lo más cerca posible de cero. Dadas las matrices intrínsecas, de distorsión, rotación y traslación, primero transformamos el punto objeto punto a punto de imagen usando cv2.projectPoints (). Luego calculamos la norma absoluta entre lo que obtuvimos con nuestra transformación y el algoritmo de búsqueda de esquinas. Para encontrar el error medio se calcula la media aritmética de los errores calculados para todas las imágenes de calibración.
mean_error = 0 for i in xrange(len(objpoints)): imgpoints2, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist) error = cv2.norm(imgpoints[i],imgpoints2, cv2.NORM_L2)/len(imgpoints2) tot_error += error print "total error: ", mean_error/len(objpoints)
Si has llegado hasta aquí enhorabuena. Inicia nuestro curso Python de OpenCV para aprender de
procesamiento de imágenes, análisis de video, reconocimientos faciales y mucho mas:
[…] va a ser una pequeña sección. Durante la última sesión de calibración de la cámara, se ha encontrado la matriz de la cámara, los coeficientes de distorsión, etc. Dada una imagen […]
[…] ➡ Calibración de la cámara Opencv […]
[…] va a ser una pequeña sección. Durante la última sesión de calibración de la cámara, se ha encontrado la matriz de la cámara, los coeficientes de distorsión, etc. Dada una imagen […]