Blog

Juan Mellado, 7 Diciembre, 2011 - 15:42

Un experimento rápido y un poco tonto con js-aruco usando las sombras como marcadores de realidad aumentada.


Demo online:
www.inmensia.com/files/aruco/debug/debug.html

Juan Mellado, 5 Diciembre, 2011 - 20:15

Otra pieza del rompecabezas que estoy intentando montar. Esta vez una simple conversión de RGB a HSV en JavaScript. Hay mucha información acerca de como hacerlo en Internet, nada nuevo bajo el sol. He creado un pequeño programa que toma una imagen RGB y la convierte a HSV. Para poder ver los resultados he aplicado una pequeña transformación para devolverlos a RGB (no a los valores originales, claro).

Para los que sólo les interese el código, pongo la función de conversión. Admite como entrada los componentes RGB en el rango [0..1] y devuelve un objeto con los componentes HSV. H un ángulo dentro del rango [0..360], y S y V un porcentaje dentro del rango [0..1].

function rgb2hsv(r, g, b){
  var v = Math.max(r, g, b),
      delta = v - Math.min(r, g, b),
      s = v === 0? 0: delta / v,
      h = 0;
   
  if (0 !== delta){
 
    if (v === r){
      h = 60 * (g - b) / delta;
    }else if (v === g){
      h = 120 + (60 * (b - r) / delta);
    }else{
      h = 240 + (60 * (r - g) / delta);
    }
   
    if (h < 0){
      h += 360;
    }
  }
   
  return {h: h, s: s, v: v};
}
Temas: JavaScript
Juan Mellado, 2 Diciembre, 2011 - 11:28

Google ha lanzado la versión alpha de Google APIs Client Library for JavaScript. O lo que es lo mismo, la posibilidad de acceder a través de JavaScript a todos los servicios de Google (Google+, Calendar, Translate, URL Shortener, Maps, ...). Ya existía en versión beta para otros lenguajes como PHP, Python, Java y .NET. Esta nueva versión para JavaScript se une al resto de versiones que se encuentran en fase alpha como las de Ruby, Objective C, GWT y Go.

He hecho una pequeña prueba ejecutando el API de Google+, lanzando consultas para obtener todas las novedades públicas aparecidas recientemente en mi cuenta de Google+ con el siguiente código:

<script src="https://apis.google.com/js/client.js?onload=init">
...
function init(){
  gapi.client.setApiKey('YOUR_API_KEY');

  gapi.client.load('plus', 'v1', function(){

      var request = gapi.client.plus.activities.list({
          'userId': '101388150953455772115',
          'collection': 'public'
        });

      request.execute( function(response){
        //Aquí se recibe la respuesta
        ...
        });
    });
}

Como se observa, hay que cargar el script de cliente (https://apis.google.com/js/client.js) indicando la función a la que queramos que se llame cuando termine de cargarse el script (?onload=init). Una vez cargado el script hay que establecer la clave personal de acceso al API (gapi.client.setApiKey) que podemos conseguir gratuitamente en la APIs Console. Y a continuación invocar al API que se quiera (gapi.client.load), como el de Google+ por ejemplo (plus), indicando la función que se quiere ejecutar, como la de consulta de actividad por ejemplo (gapi.client.plus.activities.list), y donde queremos recibir la respuesta (request.execute).

Para los que quieran probar sin tener que escribir código, las APIs se pueden consultar y ejecutar de forma online a través del APIs Explorer. Una aplicación muy útil para consultar parámetros y examinar resultados.

Utilizando mi ID de usuario en Google+ (101388150953455772115) he hecho una consulta para obtener mis últimas novedades públicas (public) y he obtenido el siguiente objeto respuesta:

Google API Client - JavaScript

Como se observa, hay un ID, un título, una fecha, una serie de urls y un array de objetos. Ese array es el que contiene las últimas novedades aparecidas en mi cuenta. Veamos el primero:

Google API Client - JavaScript

Nuevamente un id, un título, fechas y urls. Con esta información básica ya se podría montar un pequeño listado de novedades en una página web con los títulos y fecha, e incluso actualizarlo periódicamente desde los propios clientes en JavaScript. Pero aún podemos añadir más detalles examinando los campos actor y object:

Google API Client - JavaScript

Google API Client - JavaScript

El actor es el usuario que generó la actividad, y tenemos su id, nombre, url, e incluso un enlace a la imagen de su avatar.

El object es lo publicado, con su contenido completo, el número de veces que se ha compartido, el número de usuarios que han pulsado el botón "+1" sobre él y los comentarios que ha generado. Incluyendo además una lista de adjuntos, que es lo compartido (enlace, foto, vídeo, ...):

Google API Client - JavaScript

Un último punto que no hay que olvidar, es que el número de llamadas que se pueden realizar con un mismo ID está limitado por API y día. Cada API tiene un límite que Google denomina "de cortesía". Cuando en un día se supera dicho límite deja de funcionar hasta el día siguiente.

Juan Mellado, 1 Diciembre, 2011 - 18:26

Erosión y Dilatación son dos operadores morfológicos que se aplican dentro del campo del procesamiento de imágenes para encontrar mínimos y máximos locales. Su principio de funcionamiento consiste en recorrer todos los pixels de una imagen con una ventana deslizante de tamaño fijo, y para cada pixel calcular el mínimo (erosión) o máximo (dilatación) de entre todos los pixels cubiertos por la ventana.

He creado una sencilla implementación en JavaScript, y un pequeño programa para poder comprobar el resultado de una forma visual. El programa genera una nueva imagen de forma aleatoria cada cinco segundos, la convierte a escala de grises, y le aplica erosión y dilatación.

Como nota personal, comentar que la imagen resultante de la dilatación la encuentro bastante "festiva". El aumento del tamaño de los puntos los hace parece un montón de confeti cayendo.

Con respecto a la implementación, he utilizado un tamaño fijo de ventana de 3x3, de forma que para cada pixel de la imagen se busca el mínimo (o máximo) para los nueve pixels cubiertos por la ventana. No obstante, para ser más estrictos con la definición formal de estos operadores, tendría que haber tenido en cuenta un parámetro de entrada que se conoce como elemento estructurante. Este parámetro es una matriz con el mismo tamaño que la ventana, es decir un array de 3x3 en mi caso, con valores que pueden ser 0 ó 1. Los valores a 0 indican que los pixels vecinos correspondientes no se tienen que tener en cuenta para el cálculo, y los valores a 1 que si se tienen que tener en cuenta. En mi caso quería tener en cuenta todos los vecinos, que es lo mismo que considerar un array con todos sus valores a 1, por lo que he podido obviarlo. De igual forma que he obviado el cálculo en el borde de la imagen para simplificar la implementación.

Como se observa en las imágenes que se van generando, parece que en la erosión desaparece el ruido debido a los puntos aleatorios, mientras que en la dilatación se acentúa. Pero en realidad es un poco más complejo. Lo que se pierde son los valores altos cercanos a valores bajos en la erosión, y los valores bajos cercanos a valores altos en la dilatación. Pensando en ceros y unos se entiende más fácilmente.

Erode - Dilate

No obstante, hay que tener en cuenta que los operadores morfológicos se comportan "visualmente" de forma distinta sobre imágenes binarias (blanco y negro) que sobre imágenes en escala de grises. En las imágenes binarias la erosión "reduce" y la dilatación "aumenta". En las imágenes de escala de grises no necesariamente. La explicación es que en las imágenes binarias el valor 0 normalmente lo hacemos corresponder mentalmente con el color negro del fondo y el 1 con el blanco del frente. Al erosionar un imagen binaria se toma el mínimo que es el 0, por lo que desaparece el frente y hay más color de fondo. Pero en un imagen de escala de grises con el fondo claro mentalmente lo hacemos al revés. Esperamos que desaparezca el punto oscuro de frente y prevalezca el fondo claro, pero como el color de frente es "menor" (negro = valor 0) entonces lo que se consigue es el efecto contrario.

Si alguien está pensando que las imágenes que se están generando automáticamente en este post están invertidas con respecto a lo que debería salir teniendo en cuenta la explicación que he puesto... ¡tiene razón! Me he tomado la licencia de invertirlas para que el resultado sea más intuitivo de entender. Erosionar adegalza. Dilatar engorda. En la práctica es sólo una mera búsqueda de mínimos y máximos.

Por último, una característica a tener en cuenta de estos operadores es que se pueden aplicar varias veces seguidas, para erosionar o dilatar una imagen ya previamente erosionada o dilatada. Y que se pueden mezclar tomando la salida de uno como entrada de otro. De hecho, aplicar una dilatación después de una erosión se llama "apertura" (opening), y aplicar una erosión después de una dilatación se llama "cierre" (closing").

Juan Mellado, 30 Noviembre, 2011 - 15:37

Al Independent Games Festival del año que viene, más conocido como IGF 2012, se han presentado a la competición principal casi 570 juegos para todo tipo de plataformas. Desde la organización han montado una página con el detalle de cada uno, incluyendo sinopsis, imagen y vídeo.

IGF 2012

Dedicando apenas un minuto a cada uno de ellos llevaría del orden de unas diez horas echarles un vistazo rápido a todos. Y a ese tiempo habría que sumar además el necesario para revisar los casi 300 trabajos presentados en la competición de juegos realizados por estudiantes, que también tiene su página correspondiente.

Mucho que ver ahí, con paciencia. No es de esperar que todos y cada uno de ellos sean originales, pero siempre puede haber alguna sorpresa. "Los buenos artistas copian, los grandes artistas roban" dijo Picasso.

No envidio al jurado.