Tag: GNU-Linux

OpenCV – Mouse Event Handler (parte 1)

Para aquellos que os estáis introduciendo un poco en temas de visión artificial, seguramente ya habréis oído hablar de OpenCV, pero para los que no, os explico someramente lo que es.

OpenCV es una librería de visión artificial desarrollada originalmente por Intel y licenciada como BSD. OpenCV está escrito en C/C++ y es multiplataforma GNU/Linux, Mac OS XWindows, IOS y Android. El proyecto proporciona un entorno de programación fácil de utilizar, altamente eficiente y sin dependencias a librerías de terceros.

Una de las herramientas esenciales cuando se trabaja en visión es la visualización e interacción con los resultados (véase imagen). OpenCV cubre perfectamente este tipo de necesidades. Para ello voy a presentar un ejemplo sencillo de manejo de ventanas con eventos de ratón.

GMM-GC-segmentation

En el siguiente código se muestra una función que renderiza una ventana:

void showWindow()
{
  	//Create an autoresize window
	namedWindow("My Window", 1);

	//Set the callback function for any mouse event
	setMouseCallback("My Window", mouseEventHandler, NULL);

	//Show the image
	frame = cv::imread("0001_.jpg");
	imshow("My Window", frame);

	//Wait until user press some key
	waitKey(0);
}

El método "namedWindow" nos permite crear una ventana con un nombre que la identifique y que además se autodimensione dependiendo el contenido que muestre. Para cargar un fichero de imagen basta con usar "imread" especificando el nombre o path del fichero para cargarlo en un objeto Mat de OpenCV. La variable "frame" es del tipo Mat mencionado, que se ha definido como global fuera de la función. Por último "waitKey" nos permite tener renderizándose la ventana hasta que se presione alguna tecla o se cierre desde el aspa de cierre de la ventana.

Habréis apreciado que me he saltado el método "setMouseCallback", pero lo he hecho para enlazar con el siguiente fragmento de código. Dicho método, nos permite asociar un callback a una ventana usando su nombre de identificación(en nuestro caso "My Windows") y pudiendo pasar parámetros adicionales (en nuestro caso no se pasa ninguno y se pasa un NULL).

void mouseEventHandler(int event, int x, int y, int flags, void* param)
{
	if  ( event == EVENT_LBUTTONDOWN )
	{
		cout << "Left button of the mouse is clicked DOWN - position (" << x << ", " << y << ")" << endl;
	}
	else if  ( event == EVENT_LBUTTONUP )
	{
		cout << "Left button of the mouse is clicked UP - position (" << x << ", " << y << ")" << endl;
	}
	else if  ( event == EVENT_LBUTTONDBLCLK )
	{
		cout << "Left button of the mouse is double clicked - position (" << x << ", " << y << ")" << endl;
	}
	else if  ( event == EVENT_RBUTTONDOWN )
	{
		cout << "Right button of the mouse is clicked DOWN - position (" << x << ", " << y << ")" << endl;
	}
	else if  ( event == EVENT_RBUTTONUP )
	{
		cout << "Right button of the mouse is clicked UP - position (" << x << ", " << y << ")" << endl;
	}
	else if  ( event == EVENT_RBUTTONDBLCLK )
	{
		cout << "Right button of the mouse is double clicked - position (" << x << ", " << y << ")" << endl;
	}
	else if  ( event == EVENT_MBUTTONDOWN )
	{
		cout << "Middle button of the mouse is clicked DOWN - position (" << x << ", " << y << ")" << endl;
	}
	else if  ( event == EVENT_MBUTTONUP )
	{
		cout << "Middle button of the mouse is clicked UP - position (" << x << ", " << y << ")" << endl;
	}
	else if  ( event == EVENT_MBUTTONDBLCLK )
	{
		cout << "Middle button of the mouse is double clicked - position (" << x << ", " << y << ")" << endl;
	}
	else if ( event == EVENT_MOUSEMOVE )
	{
		cout << "Mouse move over the window - position (" << x << ", " << y << ")" << endl;
	}
}

Cada vez que se produzca alguno de los siguientes eventos:

  • EVENT_MOUSEMOVE
  • EVENT_LBUTTONDOWN
  • EVENT_RBUTTONDOWN
  • EVENT_MBUTTONDOWN
  • EVENT_LBUTTONUP
  • EVENT_RBUTTONUP
  • EVENT_MBUTTONUP
  • EVENT_LBUTTONDBLCLK
  • EVENT_RBUTTONDBLCLK
  • EVENT_MBUTTONDBLCLK

Se invocará a la función callback "mouseEventHandler". En el código se puede observar que la sucesión de los distintos "if" permite identificar el tipo de evento. En el caso concreto del movimiento del ratón, además se imprimen las coordenadas de la ventana a las que se ha desplazado el ratón, utilizando los parámetros "x" e "y" que recibe "mouseEventHandler".

A continuación dejo el código completo de la arquitectura básica para el manejo de eventos:

 

MeshlabServer – Aplicación de filtros de Meshlab en lote

En mi día a día trabajo con información tridimensional y hago tratamiento de la misma mediante una herramienta que se ha convertido para mí en un imprescindible. Se trata de Meshlab.

Meshlab es una herramienta Open Source  muy potente que permite el manejo y procesamiento de información 3D. Lo que hace realmente potente a Meshlab, son la cantidad de filtros para procesar esa información, y que más de uno ha pensado en usar como herramienta para hacer cierto procesamiento de un Dataset de información.

Cuando se descargan los binarios de Meshlab, se incluyen dos ejecutables: meshlab y meshlabserver. El primero es obvio para qué sirve, porque se trata del ejecutable que lanza Meshlab para ser utilizado por medio de la interfaz gráfica. El segundo es una ejecutable que nos permite procesar un Dataset completo, usando los filtros de Meshlab, mediante la configuración de un script MLX y prescindiendo de la interfaz gráfica.

Generar el script es tan sencillo como abrir Meshlab y aplicar todos los filtros que deseemos en el Dataset. Veamos un ejemplo.

Yoshi

Tenemos la malla de un modelo 3D de Yoshi como Dataset, pero dicho modelo tiene un agujero en la parte inferior.

Yoshi_with_hole

Meshlab dispone entre sus filtros de uno que se llama "Close Holes", que nos vendrá de perlas para este caso. Vamos a la sección "Filters" y los buscamos.

Meshlab_close_hole

Parametrizamos el filtro como mejor consideremos y le damos a aplicar. Como podemos ver el agujero de la parte inferior queda cerrado.

Yoshi_no_hole

Para generar el script del filtro que acabamos de aplicar y que vemos que da buenos resultados, volvemos a ir a "Filters", y seleccionamos "Show current filter script". Se nos abrirá una ventana como la siguiente:

Meshlab_script

Aquí están todas las acciones que van a constituir nuestro script, en nuestro caso sólo vamos a usar "Close Holes". En esta ventana podemos editar el orden de los filtros que vamos a aplicar, los parámetros  de cada script, borrar un  filtro del script... Cuando todo está en orden guardamos el script, el cual será guardado en formato MLX.

Para aplicar las acciones en lote sólo tenemos que usar el ejecutable meshlabserver indicándole los parámetros necesarios para su funcionamiento (input, output y script de filtros XML). Para ver la parametrización de meshlabserver podemos acudir a su comando de ayuda de la siguiente forma:

meshlabserver -help
Usage:
    meshlabserver arg1 arg2 ...
where args can be:
 -i [filename...]  mesh(s) that has to be loaded
 -o [filename...]  mesh(s) where to write the result(s)
 -s filename		    script to be applied
 -d filename       dump on a text file a list of all the filtering fucntion
 -l filename       the log of the filters is ouput on a file
 -om options       data to save in the output files: vc -> vertex colors, vf -> vertex flags, vq -> vertex quality, vn-> vertex normals, vt -> vertex texture coords,  fc -> face colors, ff -> face flags, fq -> face quality, fn-> face normals,  wc -> wedge colors, wn-> wedge normals, wt -> wedge texture coords
Example:
	'meshlabserver -i input.obj -o output.ply -s meshclean.mlx -om vc fq wn'

Notes:

There can be multiple meshes loaded and the order they are listed matters because
filters that use meshes as parameters choose the mesh based on the order.
The number of output meshes must be either one or equal to the number of input meshes.
If the number of output meshes is one then only the first mesh in the input list is saved.
The format of the output mesh is guessed by the used extension.
Script is optional and must be in the format saved by MeshLab.

De esta forma podemos aplicar una sucesión de operaciones de Meshlab en el Dataset que queramos con muy poco esfuerzo.

Chrome – El paradigma de la Memoria Libre es Memoria Inútil

Actualmente existe una debate activo sobre el manejo de recursos y en concreto de la memoria RAM. Sobre la memoria RAM se trabaja de manera exhaustiva para poder hacer funcionar de manera fluida todos los programas/procesos de un sistema. Por un lado, el sistema gestiona los recursos de una máquina de manera transparente para las aplicaciones que corren en un sistema, pero por otro lado cada programa es responsable de la gestión de memoria que necesita para funcionar. Podemos entender al sistema como un planificador de recursos hardware y a los programas como clientes que solicitan memoria para poder ejecutarse. Con este planteamiento cada aplicación solicita memoria al sistema, gestiona la cantidad de memoria que necesita y es el encargado a su vez de liberar aquella que no vaya a usar para que el sistema pueda asignársela a otro programa o proceso.

Gestión de la RAM

El planteamiento de usar toda la memoria RAM es correcto siempre y cuando el sistema operativo que gobierna la máquina tenga asumido que las aplicaciones van a hacer el máximo uso posible de la RAM disponible. Este modelo de la gestión de la memoria entiende que la memoria RAM no utilizada que queda libre es memoria inútil. Un claro ejemplo de este diseño, es el propio Android para el cual siempre está la memoria RAM completamente ocupada.

RAM_usage_Android

Uso de memoria RAM en Android

Contrario al paradigma anterior se encuentran los sistemas clásicos como Windows y Linux que entienden que las aplicaciones harán un uso razonable de la RAM, activando ciertos mecanismos en el caso de exista escasez de la misma. En el caso de Windows existe un elemento llamado Memoria Virtual que consiste en usar el disco duro que es mucho más lento para suplir la demanda de memoria RAM que pueda haber en el sistema. Windows gestiona de manera transparente esta Memoria Virtual y la descuenta de la capacidad total del disco duro.

RAM_usage_Windows

Uso de memoria RAM en Windows

De manera similar en sistema Linux se aplica  una técnica similar denominada Swap, en la cual se configura una partición durante la instalación del sistema, que sirva de zona de intercambio con la RAM cuando la demanda de RAM en el sistema no se pueda satisfacer.

Chrome y su gula por la RAM

Recientemente leí en una entrada de Xataka, un artículo que habla sobre las razones por las que Chrome consume tanta RAM cuando se ejecuta. Un diseño que divide cada pestaña  en un proceso independiente, para que en el caso de un error en alguna de ellas, no perjudique la experiencia de navegación en el resto de pestañas, no tiene ningún problema. Incluso resulta una elección acertada, que además  consigue que la experiencia de navegar con Chrome sea mucho más robusta ante errores. El problema viene de asumir que Chrome es el único programa que consume memoria y además consume toda la que puede. Digamos que Chrome extiende la filosofía de Android, y de la propia Google , de mantener siempre toda la RAM en uso.

El problema de este planteamiento de consumo de memoria que hace Chrome, es que tanto Windows como Linux degradan su funcionamiento al empezar hacer uso de Memoria Virtual  y de Swapping respectivamente. Al usar los HD como memoria RAM, el sistema en general empieza a ir mucho más lento y se degrada la experiencia de uso. En el caso de usar discos SSD el problema se mitiga bastante al ser más rápido en su acceso. Chrome nunca reducirá su consumo a pesar de existir falta de memoria RAM y obligará frecuentemente a que nuestros sistemas usen nuestro HD para cubrir la demanda de RAM.

¿Qué podemos hacer?

En la práctica podríamos decir que hacer un uso moderado de la creación de pestañas. Si aún así al usar tu Chrome notas como tu equipo muere poco a poco, una buena opción es revisar las Extensiones que no uses y deshabilitarlas o desinstalarlas. Las extensiones a veces no gestionan bien la memoria que usan y además se cargan para cada pestaña, en vez de cargarse para el conjunto de todas las pestañas. Osea que ¡ojo! a la instalación indiscriminada de extensiones.

Como opción adicional existen extensiones como The Great Suspender que se encarga de liberar memoria de aquellas pestañas que le digamos o de manera automática según condiciones de tiempo de inactividad u otras condiciones. Es realmente sencillo de usar y notaremos una mejora significativa en la experiencia de uso de nuestro equipo.

Si esto no te es suficiente, siempre puedes usar un navegador más liviano como Firefox, Opera o el renovado Windows Edge.