Skip to content

chrome

Algo viejo, algo nuevo

DartAndo otra vez trasteando con Dart, el nuevo lenguaje de programación de Google para la web. Aunque ellos en vez de “lenguaje” lo llaman “plataforma”. He cogido un proyecto viejo y lo estoy adaptando al nuevo lenguaje, sobre todo para probar la nueva versión del IDE, de Dartium, que es un build especial de Chrome que lleva incluida la máquina virtual de Dart, y dart2js, que es el tercer conversor de Dart a JavaScript que liberan.

Dart está todavía en desarrollo, lo que quiere decir que ahora mismo lo único que se puede hacer es trabajar sobre la versión alpha. Todavía le queda mucho camino por recorrer, pero las primeras sensaciones es que es mucho mejor que JavaScript para proyectos grandes que requieran un mínimo de organización. Tiene más de “Java” que de “Script”.

En el IDE han añadido algunas cosas básicas que las primeras versiones que liberaron no tenían. ¡Ya se puede renombrar ficheros! Aunque lo más importante es que han añadido un pequeño depurador. Desgraciadamente está muy verde. La depuración paso a paso no está muy fina todavía, por ejemplo en los switch/case obliga a pasar por cada case en vez de ir directamente al que cumple la condición como es lo habitual. Aunque lo peor es que no se pueden añadir watches, por lo que al final se acaba depurando a la antigua usanza, tirando mensajes por consola, convirtiendo los desarrollos actuales en toda una heroicidad.

Por lo que respecta al lenguaje en sí, poco que decir, es parecido a Java por ejemplo, con sus clases, interfaces y demás. Aunque me preocupa un poco la diferencia de tipos con JavaScript, en particular de los enteros. En Dart los enteros no tienen un tamaño fijo predeterminado, a diferencia de JavaScript que son de 64 bits. Esto hace que las operaciones de manipulación de bits se comporten de forma distinta en un lenguaje y otro. Sobre todo porque en JavaScript además este tipo de operaciones se truncan a 32 bits. Muchas aplicaciones no utilizan este tipo de operaciones, pero curiosamente en el tipo de desarrollos que yo suelo hacer son bastante habituales, ya que manipulo grandes cantidades de información a nivel binario.

El API está evolucionando bastante rápido, aunque eso implica que es normal encontrar algunas discrepancias entre la documentación y la implementación. Por ejemplo, el generador de números aleatorios es una función de la clase Math, aunque la documentación indica que se encuentra en una clase aparte Random. En realidad no tiene mucha importancia, y si me he dado cuenta es porque el generador no funciona bien, siempre genera la misma secuencia de números, y tratando de resolverlo estuve estudiando un poco más a fondo la documentación.

Por último, un tema importante a tener en cuenta es que las aplicaciones en Dart están pensadas para ejecutarse desde línea de comandos o desde un navegador, pero la única forma de tener una interface gráfica hoy en día es ejecutarlas desde un navegador. Tengo curiosidad por saber como evolucionará esto, sobre todo porque tener que implementar una aplicación típica de escritorio es un requerimiento normal. Quizás la solución sea desarrollar bindings con algunas librerías ya existentes. De hecho, desde Dart se pueden realizar llamadas a código C/C++, a la manera de los métodos native de Java por ejemplo. Es una vía interesante a estudiar.

js-aruco: De 2 a 3 dimensiones

Realidad AumentadaHe actualizado los fuentes de js-aruco con los últimos cambios en los que he estado trabajando últimamente. La principal novedad es la posibilidad de calcular la pose en tres dimensiones de un marcador a partir de su proyección en dos dimensiones. Aunque también hay otros cambios importantes, así como varias mejoras realizadas para optimizar el rendimiento.

He publicado una nueva demo online con todos los cambios realizados:
http://inmensia.com/files/aruco/debug-posit/debug-posit.html

Estimación de la Pose
Para calcular las poses en tres dimensiones he utilizado el método descrito en “Iterative Pose Estimation using Coplanar Feature Points”. Este no es el método que utiliza la librería original que tomé como punto de partida, pero lo he preferido porque no requiere los parámetros de calibración de la cámara. Sus únicos parámetros de entrada son el tamaño real del marcador y la longitud focal de la cámara. El primero es sencillo, es lo que mide realmente de lado a lado el marcador (en milímetros). Y aunque el segundo suena un poco más difícil de calcular, en realidad se puede aproximar asumiendo simplemente que es igual al ancho de la imagen con la que se esté trabajando (en pixels).

El algoritmo devuelve dos poses estimadas caracterizadas por una matriz de rotación y un vector de traslación. Esto es así porque la proyección de un cuadrilátero en dos dimensiones puede corresponderse con dos posiciones distintas del mismo cuadrilátero en tres dimensiones. Para averiguar cual es la proyección más correcta, si es que existe alguna, se calcula el error que existe entre el modelo del marcador de partida, y el resultado de aplicar cada rotación y traslación calculada a la proyección dada. La pose estimada que produce el menor error se considera la más correcta de las dos.

He acabado realizando dos implementaciones, la primera basada en el código original utilizado por Daniel DeMenthon escrito en C, y la segunda basada en el código en C# descrito en un artículo por Andrew Kirillow. Dan resultados bastantes similares, aunque hay ciertas diferencias en el cálculo del método del error. Las he exportado con el mismo nombre, ya que la idea es utilizar un método u otro, y para ello basta con incluir posit1.js o posit2.js según cual método se prefiera. Está explicado en la web del proyecto.

Como añadido, he tenido que implementar en JavaScript el algoritmo de descomposición en valores singulares de una matriz, más conocido por su siglas en inglés “SVD”. Para ello he seguido la implementación original descrita en “Numerical Recipes in C – Second Edition”.

Stack Box Blur
Para aumentar el rendimiento general de la librería he sustituido el cálculo del Gaussian Blur, que se utilizaba para realizar el Threshold Adaptativo, por un Stack Box Blur.

El cálculo del Gaussian Blur era la parte que más tiempo de proceso llevaba de toda la librería. Utilizaba un tamaño de kernel de 7, lo que implicaba que para cada pixel de la imagen se tenían que realizar del orden de un centenar de accesos a memoria y otras tantas operaciones matemáticas. El Stack Box Blur utiliza un tamaño de kernel de 2, reduce considerablemente el número de accesos a memoria utilizando una pequeña pila (stack), evita tener que utilizar un buffer intermedio del mismo tamaño que la imagen, y gracias a una tabla precalculada apenas realiza unas pocas operaciones aritméticas por pixel. Y lo mejor de todo es que el resultado final es apenas indistinguible del original Gaussian Blur cuando se utiliza para calcular el Threshold Adaptativo.

He implementado una versión adaptada a las necesidades de la librería, que en este punto concreto trabaja con imágenes en escala de grises, utilizando un sólo canal, frente a las implementaciones originales, que sólo permiten trabajar con imágenes de tres o cuatro canales.

Supersampling
Una de las cosas que tenía pendiente prácticamente desde que implementé la librería era mejorar el warp. Este proceso es el que extrae de la imagen las áreas donde se encuentran los marcadores detectados y las proyecta en cuadriláteros con la idea de reconstruir el aspecto original real de cada uno de ellos. Su principal carencia era la falta de algún tipo de interpolación entre los pixeles adyacentes, por lo que las imágenes resultantes no eran todo lo buenas que podían llegar a ser cuando los marcadores estaban en ángulos “complicados” en vez de directamente enfrentados a la cámara.

Los cambios realizados han sido de dos tipos. Por una parte optimización del código existente, y por otra parte adición de supersampling. La optimización no ha sido demasiado difícil, ya que originalmente la función no estaba nada optimizada y ha sido fácil obtener una ganancia de rendimiento rápidamente. Desgraciadamente la implementación del supersampling se ha comido la ganancia y algo más. No estoy nada contento con la implementación, se basa en el uso de un par de decenas de variables locales, y eso resulta difícil que la máquina virtual pueda optimizarlo tratando de cachear valores en registros del microprocesador. La parte positiva es que las imágenes que se obtienen ahora son de muchísima más calidad, con bordes rectos en vez de dentados como ocurría antes.

Rendimiento
Utilizando como referencia la última demo, el rendimiento global del sistema en mi equipo es de 60 FPS estables con un consumo de entre el 7 y el 9 por ciento de CPU. Los FPS están perfectos, ya que es el máximo que permite el navegador, y la CPU no está demasiado mal, aunque me haría un poco más feliz ver una cantidad menor ahí. Afortunadamente creo que aún hay bastante margen de mejora.

js-aruco: Coplanar POSIT

En el vídeo adjunto se pueden ver las primeras pruebas que estoy haciendo sobre js-aruco, mi detector de marcadores de realidad aumentada, con el objeto de añadirle un algoritmo que resuelva el llamado “problema de la pose” para obtener la orientación de un objeto 3D a partir de su proyección en 2D.

El código es una implementación en JavaScript del método llamado “Coplanar POSIT”, y lo que se ve en el vídeo es la ejecución en Chrome 18 con el flag “–enable-media-stream” activo.

En la parte superior está la captura de la webcam y un simple cubo que se mueve en función de como lo hace el marcador. La distancia la calcula bastante bien en general, pero con las rotaciones aún tengo problemas. En la parte inferior se muestran las dos posibles orientaciones que devuelve el algoritmo. La que tiene menor error a la izquierda, y la de mayor error a la derecha. Todavía es bastante inestable y queda trabajo por hacer, pero ya empiezan a verse los resultados.

Referencias

– “Iterative Pose Estimation using Coplanar Feature Points”
Denis Oberkampf, Daniel F. DeMenthon, Larry S. Davis

– js-aruco: Augmented Reality Marker Detector
http://code.google.com/p/js-aruco/

– Three.js: 3D Engine
https://github.com/mrdoob/three.js

Compilando Chrome en Visual C++ 2010 Express

Hará cosa de un año que me bajé los fuentes de Chrome y los estuve compilando. Muchas novedades ha habido en el navegador desde entonces, así que me he decidido a bajarme los fuentes actualizados y volver a compilarlos. Y si bien la primera vez utilice la versión 2008 Express de Microsoft Visual C++, esta vez he optado por utilizar la 2010 Express.

Chromium - Visual C++ 2010 Express

Esta es la secuencia de pasos que he seguido utilizando Windows 7 Ultimate 64 bits:

Instalación

1) Instalar Microsoft Visual C++ 2010 Express

2) Instalar Microsoft Windows SDK for Windows 7 and Framework .NET 4

3) Instalar Microsoft Visual Studio 2010 Service Pack 1

4) Instalar Microsoft Visual C++ 2010 SP1 Compiler Update for the Windows SDK 7.1

5) Instalar Microsoft Windows Driver Kit Version 7.1.0 en un directorio <DDK>

6) Instalar Microsoft DirectX SDK en un directorio <SDX>

7) Dar permiso de modificación y sustituir “v7.0A” por “v7.1” en los ficheros:

    C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Platforms\Win32\PlatformToolsets\v100\
Microsoft.Cpp.Win32.v100.props

    C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Platforms\x64\PlatformToolsets\v100\
Microsoft.Cpp.x64.v100.props

Configuración

8) Descargar depot_tools y descomprimir el .zip en un directorio <depot_tools>

9) Descargar chromium_tarball y descomprimir el .tar en un directorio <sources>

10) Añadir el directorio <depot_tools> al PATH del sistema

11) Ejecutar por primera vez las depot tools para actualizarlas:
    <depot_tools>\glclient

12) Editar el fichero <sources>\.glclient y añadir la url de una rama probada de los fuentes:
    “safesync_url” : “http://chromium-status.appspot.com/lkgr”

13) Actualizar los fuentes:
    <sources>\glclient sync

14) Crear la solución y los proyectos para Visual Studio 2010 Express:
    <sources>\set GYP_MSVS_VERSION=2010e
    <sources>\gclient runhooks

Compilación

15) Abrir solución de Visual Studio (ignorar mensaje de advertencia sobre properties):
    <sources>\chrome\chome.sln

16) Configurar el proyecto “chrome” como inicial

17) Añadir las siguientes rutas al principio de la lista de directorios de includes (Win32 y x64):
    <SDX>\Include

18) Añadir las siguientes rutas al final de la lista de directorios de includes (Win32 y x64):
    <DDK>\inc\atl71
    <DDK>\inc\mfc42

19) Añadir las siguientes rutas al principio de la lista de directorios de libs:
    Win32: <SDX>\Lib\x86
    x64: <SDX>\Lib\x64

20) Añadir las siguientes rutas al final de la lista de directorios de libs:
    Win32: <DDK>\lib\ATL\i386
    x64: <DDK>\lib\ATL\amd64

21) Lanzar el build sobre el proyecto “chrome”

El tiempo total en mi máquina para una compilación sólo del navegador Chrome para Win32 Debug sin los tests es de unos 40 minutos, lo que está bastante bien teniendo en cuenta la cantidad de proyectos y dependencias.

El orden de instalación que he puesto difiere un tanto de las instrucciones oficiales, pero es la mejor forma que he encontrado para evitar los errores con los que me he ido topando al instalar los distintos productos de Microsoft que necesitaba. La lista de pasos ahora la tengo clara, pero como de costumbre ha sido todo un proceso de prueba y error. Al final opté por bajarme las .ISO de cada instalación en vez de utilizar los instaladores webs, porque esperar por cada descarga cada vez era muy tedioso.

Con respecto a la instalación en particular del compilador, comentar que finalmente tuve que instalar primero el Visual C++, luego el SDK de Windows, luego el Service Pack 1 del Visual C++ y por último un parche para arreglarlo todo, aunque aún así es necesario modificar dos ficheros de manera manual, ya que se empeña en tomar la versión 7.0A del SDK de Windows en vez la más actualizada 7.1.

Finalmente, puede que sorprenda que haya tenido que instalar el SDK de DirectX, ya que este último pasó a formar parte del SDK de Windows hace tiempo, pero es necesario porque los fuentes de Chrome utilizan D3DX, que sólo está incluida en el SDK de DirectX. De igual forma, es necesario instalar el DDK porque la versión Express de Visual C++ no trae los includes y librería de ATL.

¡Feliz compilación a todos!