El primer mapa de la Luna

Analizando código fuente JavaScript para decodificar Captchas

  • February 1, 2009
  • cybercaronte
  • captcha 

    Siguiendo el hilo del post creado por nuestro colega tuxotron, vamos a ver cómo funciona un programa en JavaScript para pasar el test de un Captcha, en concreto para Megaupload.

    Nuestro interés está por encima de pasar o no el test del Megaupload, radica en la compleja estructura del programa y el objetivo final del mismo, que no es otro que emular al ser humano a la hora de analizar una imágen.

    Shaun Friedle  es el creador de este código. Utiliza el HTML 5 Canvas getImageData API para obtener los pixeles de la imágen. El script también incluye la implementación de una red neuronal escrita en JavaScript puro. Los datos extraidos de la imágen se envían a la red neuronal para adivinar exactamente qué caracteres se han usado, usando técnicas de OCR.

    El código fuente completo puedes encontrarlo aquí:

    http://userscripts.org/scripts/review/38736

    Analizándolo un poco:

    1. El Captcha es copiado al Canvas y convertido en escala de grises:

    function convert_grey(image_data){
      for (var x = 0; x < image_data.width; x++){
        for (var y = 0; y < image_data.height; y++){
          var i = x*4+y*4*image_data.width;
          var luma = Math.floor(image_data.data[i] * 299/1000 +
          image_data.data[i+1] * 587/1000 +
          image_data.data[i+2] * 114/1000); 
          image_data.data[i] = luma;
          image_data.data[i+1] = luma;
          image_data.data[i+2] = luma;
          image_data.data[i+3] = 255;
        }
      }
    }

    2. Luego se separa en tres matrices de pixeles diferentes por cada caracter:

    filter(image_data[0], 105);
    filter(image_data[1], 120);
    filter(image_data[2], 135);
    function filter(image_data, colour){
      for (var x = 0; x < image_data.width; x++){
        for (var y = 0; y < image_data.height; y++){
          var i = x*4+y*4*image_data.width;
          // Turn all the pixels of the certain colour to white
          if (image_data.data[i] == colour) {
            image_data.data[i] = 255;
            image_data.data[i+1] = 255;
            image_data.data[i+2] = 255;
         
          // Everything else to black
          } else {
            image_data.data[i] = 0;
            image_data.data[i+1] = 0;
            image_data.data[i+2] = 0;
          }
        }
      }
    }

    3. Luego se eliminan todos los pixeles que no podemos analizar (posiblemente sean basura), esto se consigue buscando pixeles blancos que están rodeados por otros pixeles negros:

    var i = x*4+y*4*image_data.width;
    var above = x*4+(y-1)*4*image_data.width;
    var below = x*4+(y+1)*4*image_data.width; 
     
    
    if (image_data.data[i] == 255 &&
      image_data.data[above] == 0 &&
      image_data.data[below] == 0)  {
      image_data.data[i] = 0;
      image_data.data[i+1] = 0;
      image_data.data[i+2] = 0;
    }

    4. Aquí empieza lo bueno, ahora tenemos que alimentar la red neuronal pero aún tenemos que hacer pasar la información por un nuevo filtro, analizar los bordes. Para ello el código crea un rectángulo usando los pixeles situados en los extremos, convirtiendo la imágen en una matriz de 20x25.  Al final de este punto tenemos dicha matriz que contiene un dibujo en blanco y negro:

    cropped_canvas.getContext("2d").fillRect(0, 0, 20, 25);
    var edges = find_edges(image_data[i]);
    cropped_canvas.getContext("2d").drawImage(canvas, edges[0], 
      edges[1], edges[2]-edges[0], edges[3]-edges[1], 0, 0,
      edges[2]-edges[0], edges[3]-edges[1]); 
    image_data[i] = cropped_canvas.getContext("2d").getImageData(0, 0,
      cropped_canvas.width, cropped_canvas.height);

    5. Ahora es cuando se alimenta la red neuronal con los datos obtenidos. Esta red analiza la información y reduce aún más los datos, ya que extraerá sólo 64 estados seleccionados según el criterio programado. Una vez seleccionados los puntos, la red se dedica a compararla con las letras del alfabeto una a una, hasta que consigue una que se aproxime a los valores que buscamos. En cada comparación se le asigna una puntuación a cada letra en función del éxito obtenido, por ejemplo "A" 80%, "B" 45%, etc ...

     Realmente espectacular todo lo que podemos aprender de este código.

    Noticia original:

    http://ejohn.org/blog/ocr-and-neural-nets-in-javascript/

Los 10 mandamientos del Programador Java

  • January 31, 2009
  • tuxotron
  • programming

    Aunque el título se refiera al programador Java, estas reglas son aplicables a cualquier tipo de programador. A excepción de la regla número 6, que es específica al lenguaje de programación Java.

    1. Comenta el código. Cuando estamos escribiendo código nuevo, es fácil entender lo que se está haciendo, pero si no tocamos ese código por un periodo de tiempo y tenemos que volver al mismo, ya no es tan obvio. Comentar el código te ayudará a entender más rápidamente la lógica del programa.

    2. No compliques las cosas. Muchas veces queremos solucionar algún problema de la forma más enredada porque está guay. Busca la forma más simple de resolver las cosas. Esto te ayudará a entender el código mejor y a mantenerlo de una manera más eficiente y es menos propenso a errores.

    3. Menos es más, no es siempre bueno. Muchos lenguajes de programación te permiten concatenar funciones y muchas veces queremos hacer varias cosas a la vez en una línea. Esto dificulta la lectura y la lógca del código.

    4. Evita el hard coding. Usa las constantes, de este modo si necesitamos cambiar este valor, sólo tenemos que hacerlo en la constante y no en el resto del código. Si es valor es algo que va a ir cambiando a lo largo de la vida del programa, sería mejor usar ficheros externos de configuración (XML, propiedades, base de datos, etc), de esta forma no tenemos que modificar el código, recompilar y redistribuir la nueva versión.

    5. No reinventes . Aprovéchate de los marcos de trabajo (frameworks) existentes y de los patrones de diseño. Están ampliamente probados.

    6. Cuidado con los prints y la concatenación de Strings. Normalmente tendemos a escribir prints por todo el programa, con la intención de depurar nuestra aplicación. Esto puede afectar seriamente al rendimiento de la aplicación. Usa algún tipo de mecanismo para que esos prints solo se ejecuten cuando estemos en la fase de desarrollo. La concatenación de String puede ser otra de las operaciones que afectan al rendimiento del programa. Si vas a hacer muchas operaciones de concatenación, en Java la clase String es inmutable, por lo tanto cada vez que haces una concatenación estás desreferenciando los Strings que estas concatenando y creando un nuevo String. En Java tienes disponibles 2 clases que aumentan drásticamente el rendimiento para este tipo de operaciones. StringBuffer y StringBuilder. StringBuilder es incluso más rápido que StringBuffer, ya que este no es thread-safe. Por lo tanto si el código de desde el que estas haciendo las concatenaciones no es multihilo usa StringBuilder, si no, usa StringBuffer.

    7. Pon atención al interfaz de usuario. El aspecto del interfaz de usuario, la forma de navegar por el mismo y la comodidad a la hora de usar el mismo, van a depender mucho de la aceptación y el éxito de tu aplicación. Sigue el mismo estilo en toda la aplicación, escoge cuidadosamente el título de las ventanas, etiquetas de texto, etc. Sigue el mismo diseño que otros sistemas ampliamente aceptados. Pon tu interfaz a pruebas con tu mujer, marido, novi@, amig@, etc. Para ver como se mueve por la aplicación, etc.

    8. Documentación. Es lo que personalmente odio más. Escribir documentación es muy pesado y en el momento en el que estamos trabajando en un proyecto todo tiene sentido y fácil de seguir. Pero de nuevo, cuando tenemos que volver al proyecto después de un tiempo ya no nos parecerá tan obvio como pensabamos. Además si el proyecto no era lo suficiente grande como para requerir a más de una persona para trabajar en el mismo, recuerda que el proyecto puede crecer y requerir a uno o más programadores incorporarse al proyecto. O simplemente el proyecto pasa a manos de otra persona. Es una de las tareas más pesadas, pero de las que más que se agradecen a lo largo del tiempo.

    9. Unidades de testeo. Es una muy buena y reomendada práctica que debemos hacer incluso antes de escribir nuestros paquetes, librerías, etc. Y son las unidades de testeo. Esto nos va a ayudar de una forma muy rápida si nuestras funciones funcionan como deben. Nos va ahorrar mucho tiempo en el futuro (cada vez que tengamos que modificar nuestras funciones) y es otra de las tácticas que nos ayudará a reducir el número de errores en nuestro programa.

    10. Calidad, no cantidad. Es mejor entregar un programa con las funciones básicas bien desarrolladas, a entregar un programa lleno de funciones y que cada dos por tres el mismo se cuelgue o no haga lo que se supone que tiene que hacer.

    Esta artículo no es una traducción del original. He usado el original como base, añadiendo mis propios puntos de vista basados en mi experiencia personal.