Realidad Aumentada (OpenCV+ArUco)

La realidad aumentada busca combinar el mundo real con uno virtual permitiendo la interacción entre ambos mundos, lo que haremos será extender la realidad con objetos creados por computador que se adaptarán a nuestro mundo real.

Lo primero que necesitamos es saber como combinaremos los objetos virtuales con el mundo real, para ello usaremos ArUco una librería que nos permite detectar marcas, conocer su posición y rotación, la misma esta construida con OpenCV.

Realidad aumentada detección de marcas
Estas marcas son creadas por la aplicación aruco_create_board.exe incluida con aruco, para usarla la compilamos y mediante línea de comandos indicamos los siguientes argumentos X:Y cantidad de marcas a crear horizontal y vertical, nombre de la imagen de salida .png y la configuración de las marcar .yml, si deseamos generar un conjunto de marcas como el de la imagen de arriba usamos el siguiente comando en la carpeta donde se encuentra en aruco_create_sample.exe: aruco_create_sample.exe 3:2 image-test.png board.yml

En la imagen vemos las marcas y como son detectadas por ArUco usando funciones de OpenCV como detección de bordes y análisis de figuras, se detecta el identificador de la único de la marca y las esquinas de la misma, estos puntos serán usados para crear la matriz del modelo 3D del objeto que se desea proyectar sobre la marca.

//cargar la imagen de prueba que contiene las marcas a detectar
cv::Mat InImage;
InImage = cv::imread("data/image-test.png");

//detectar las marcas presentes en la imagen de entrada
MDetector.detect(InImage, Markers);

//dibujar las esquinas y los ejes en cada una de las marcas detectadas  
for (unsigned int i = 0; i<Markers.size(); i++) {
	Markers[i].draw(InImage, Scalar(0, 0, 255), 2);
}

//mostrar la imagen de salida
cv::imshow("ArUco", InImage);
cv::waitKey(0);

Calcular ejes (X,Y,Z) para realidad aumentada
Vemos como ArUco calcula los ejes (X,Y,Z) que nos permitirán proyectar un objeto 3D sobre la marca que detectamos, para generar gráficos 3D utilizaremos OpenGL que es perfectamente compatible con OpenCV y ArUco.

//funciones auxiliares para dibujar los ejes y un cubo 3D sobre la marca detectada
if (CamParam.isValid() && MarkerSize != -1) {
	for (unsigned int i = 0; i < Markers.size(); i++) {
		CvDrawingUtils::draw3dCube(InImage, Markers[i], CamParam);
		CvDrawingUtils::draw3dAxis(InImage, Markers[i], CamParam);
	}
}

Para profundizar en los fundamentos matemáticos y técnicos de los marcadores como detectarlos, obtener los coordenadas y proyectarlas en OpenGL pueden leer este documento: www.librorealidadaumentada.com

Realidad Aumentada en Tiempo Real


Hasta este punto hemos visto las marcas y como detectarlas sobre una imagen estática, implementaremos estos principios a una entrada de video proveniente de nuestra webcam en tiempo real.

Lo primero que debemos hacer es crear una ventana con soporte para OpenGL por lo que requerimos tener la librería OpenCV compilada con OpenGL activado, creamos el contexto OpenGL e indicamos al callback de dibujo.

String ARWindowName = "Realidad Aumentada (OpenCV+ArUco)";
namedWindow(ARWindowName, WINDOW_OPENGL);
setOpenGlContext(ARWindowName);
setOpenGlDrawCallback(ARWindowName, ARDraw, NULL);

Seguido abrimos la cámara y hacemos coincidir el tamaño del video con la ventana, además cargamos los parámetros de calibración de la cámara.

Utilizamos un video pregrabado solo para demostración, el que desee usar su webcam debe cambiar el código para leer de la cámara: VideoCapture video(0); además de generar el archivo de calibración .yml para su cámara, para ello se pueden usar las aplicación aruco_calibration.exe incluida en ArUco.

//abrir la camara o video 
VideoCapture video("data/video.avi");

if (!video.isOpened()) { getchar(); return -1; }

double height = video.get(CAP_PROP_FRAME_HEIGHT);
double width = video.get(CAP_PROP_FRAME_WIDTH);

//hacer el tamaño de la ventana igual el tamaño de video
resizeWindow(ARWindowName, width, height);

//cargar los parametros de calibacion de la camara
TheCameraParams.readFromXMLFile("data/intrinsics.yml");

Para cada cuadro capturado del video o cámara intentamos detectar los marcas e indicamos a OpenGL que debe actualizar la ventana y  volver a dibujar las figuras 3D en las nuevas coordenadas detectadas.
 
void detectarMarcador(String ARWindowName) {
	cv::undistort(TheInputImage, TheUndInputImage, TheCameraParams.CameraMatrix, TheCameraParams.Distorsion);
	PPDetector.detect(TheUndInputImage, TheMarkers, TheCameraParams.CameraMatrix, Mat(), TheMarkerSize, false);
	cv::updateWindow(ARWindowName);
}

Ahora es el turno de OpenGL para dibujar las figuras 3D, primero debemos dibujar la imagen capturada de la cámara sobre la superficie de la ventana, tomaremos la captura y la dibujaremos sobre la ventana, luego sobre ella dibujaremos los objetos 3D.

glPixelZoom(1, -1);
glRasterPos2i(0, TheGlWindowSize.height);
glDrawPixels(TheGlWindowSize.width, TheGlWindowSize.height, GL_BGR_EXT, GL_UNSIGNED_BYTE, TheInputImage.data);

Para dibujar gráficos 3D requerimos la matriz de proyección, nos la proporciona ArUco por medio del método: aruco::CameraParameters.glGetProjectionMatrix y la matriz del modelo que ArUco calcula a partir de las marcas detectadas con el método: aruco::Marker.glGetModelViewMatrix(double *mvm)

TheCameraParams.glGetProjectionMatrix(TheInputImage.size(), TheGlWindowSize, proj_matrix, 0.05, 10);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glLoadMatrixd(proj_matrix);

for (unsigned int m = 0; m < TheMarkers.size(); m++)
{
	TheMarkers[m].glGetModelViewMatrix(modelview_matrix);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glLoadMatrixd(modelview_matrix);

	drawCubeModel();
}

La función drawCubeModel() dibujar un cubo 3D sobre cada una de las marcas detectadas.

ar-opecv
Este proyecto esta configurado para ser generado con CMake e incluye todos los archivo necesarios para su ejecución incluida la librería ArUco, OpenCV por su tamaño no se incluye por lo que el lector deberá tenerla instalada con OpenGL y FFMPEG activado.

GitHub: Realidad Aumentada con OpenCV y ArUco

Comentarios

Temas relacionados

Entradas populares de este blog

tkinter Grid

Conectar SQL Server con Java

JavaFX Uso de ComboBox

tkinter Canvas

JavaFX Reproducción de Audio y Video