Usar JavaScript para descifrar códigos de verificación
Recientemente, apareció en Internet un script JavaScript que puede descifrar códigos de verificación: GreaseMonkey. ¡Este script desarrollado por "Shaun Friedle" puede resolver fácilmente el CAPTCHA del sitio Megaupload. Si no lo cree, puede probarlo usted mismo en http://herecomethelizards.co.uk/mu_captcha/ .
Ahora, el CAPTCHA proporcionado por el sitio Megaupload ha sido anulado por el código anterior. El código aquí está diseñado No, no muy bien. Pero lo que es más interesante es:
1. La interfaz de la aplicación Canvas getImageData en HTML 5 se puede utilizar para obtener datos de píxeles de la imagen del código de verificación. Usando Canvas, no solo podemos incrustar una imagen en un lienzo, sino también volver a extraerla más tarde.
2. El script anterior contiene una red neuronal implementada completamente en JavaScript.
3. Después de usar Canvas para extraer datos de píxeles de la imagen, enviarlos a la red neuronal y utilizar una tecnología de reconocimiento óptico de caracteres simple para inferir qué caracteres se utilizan en el código de verificación.
Al leer el código fuente, no solo podemos comprender mejor cómo funciona, sino también cómo se implementa este código de verificación. Como vio anteriormente, los CAPTCHA utilizados aquí no son muy complejos: cada CAPTCHA consta de tres caracteres, cada carácter usa un color diferente y solo usa caracteres del alfabeto de 26 letras, mientras que todos los caracteres usan la misma fuente.
El propósito del primer paso es obvio: copiar el código de verificación en el lienzo y convertirlo en una imagen en escala de grises.
función convert_grey(image_data){
para (var x = 0; x <image_data.width; x++){
para (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 +
datos_imagen.datos[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;
datos_imagen.datos[i+3] = 255;
}
}
}
Luego, divida el lienzo en tres matrices de píxeles separadas, cada una de las cuales contenga un carácter. Este paso es muy fácil de lograr porque cada personaje usa un color diferente, por lo que se puede distinguir por color.
filtro(datos_imagen[0], 105);
filtro (datos_imagen [1], 120);
filtro (datos_imagen [2], 135);
filtro de función (image_data, color) {
para (var x = 0; x <image_data.width; x++){
para (var y = 0; y <image_data.height; y++){
var i = x*4+y*4*image_data.width;
// Convierte todos los píxeles de un determinado color en blanco
si (image_data.data[i] == color) {
image_data.data[i] = 255;
datos_imagen.datos[i+1] = 255;
datos_imagen.datos[i+2] = 255;
// Todo lo demás a negro
} demás {
image_data.data[i] = 0;
datos_imagen.datos[i+1] = 0;
datos_imagen.datos[i+2] = 0;
}
}
}
}
Finalmente se eliminan todos los píxeles molestos irrelevantes. Para hacer esto, primero puede encontrar los píxeles blancos (coincidentes) que están rodeados por píxeles negros (no coincidentes) delante o detrás y luego eliminar los píxeles coincidentes.
var i = x*4+y*4*image_data.width;
var arriba = x*4+(y-1)*4*image_data.width;
var debajo = x*4+(y+1)*4*image_data.width;
si (image_data.data[i] == 255 &&
image_data.data[arriba] == 0 &&
image_data.data[abajo] == 0) {
image_data.data[i] = 0;
datos_imagen.datos[i+1] = 0;
datos_imagen.datos[i+2] = 0;
}
Ahora que tenemos una forma aproximada del personaje, el script realiza además la detección de bordes necesaria antes de cargarlo en la red neuronal. El script buscará los píxeles más a la izquierda, derecha, superior e inferior del gráfico, los convertirá en un rectángulo y luego volverá a convertir el rectángulo en una matriz de 20*25 píxeles.
cropped_canvas.getContext("2d").fillRect(0, 0, 20, 25);
var bordes = find_edges(image_data[i]);
cropped_canvas.getContext("2d").drawImage(lienzo, bordes[0], bordes[1],
bordes[2]-bordes[0], bordes[3]-bordes[1], 0, 0,
bordes[2]-bordes[0], bordes[3]-bordes[1]);
image_data[i] = cropped_canvas.getContext("2d").getImageData(0, 0,
lienzo_recortado.ancho, lienzo_recortado.alto);
Después del procesamiento anterior, ¿qué obtenemos? Una matriz de 20*25 que contiene un único rectángulo lleno de blanco y negro. ¡Eso es genial!
Luego, simplificaremos el rectángulo aún más. Extraemos estratégicamente puntos de la matriz como "fotorreceptores" que se introducirán en la red neuronal. Por ejemplo, un determinado fotorreceptor puede corresponder a un píxel situado en 9*6, con o sin píxeles. El script extrae una serie de dichos estados (muchos menos que el cálculo completo de la matriz de 20x25; solo se extraen 64 estados) y alimenta estos estados a la red neuronal.
Quizás se pregunte, ¿por qué no comparar píxeles directamente? ¿Es necesario utilizar una red neuronal? El punto es que queremos deshacernos de esas situaciones ambiguas. Si probó la demostración anterior, notará que comparar píxeles directamente es más propenso a errores que comparar a través de una red neuronal, aunque no sucede mucho. Pero tenemos que admitir que para la mayoría de los usuarios, la comparación directa de píxeles debería ser suficiente.
El siguiente paso es intentar adivinar las letras. Se importan a la red neuronal 64 valores booleanos (obtenidos de una de las imágenes de los personajes), así como una serie de datos precalculados. Uno de los conceptos de las redes neuronales es que los resultados que queremos obtener se conocen de antemano, por lo que podemos entrenar la red neuronal en consecuencia en función de los resultados. El autor del script puede ejecutar el script varias veces y recopilar una serie de mejores puntuaciones que pueden ayudar a la red neuronal a adivinar la respuesta trabajando hacia atrás a partir de los valores que las produjeron, pero estas puntuaciones no tienen un significado especial.
Cuando la red neuronal calcula los 64 valores booleanos correspondientes a una letra en el código de verificación, lo compara con un alfabeto precalculado y luego otorga una puntuación por la coincidencia con cada letra. (El resultado final puede ser similar: el 98% puede ser la letra A, el 36% puede ser la letra B, etc.)
Una vez procesadas las tres letras del código de verificación, saldrá el resultado final. Cabe señalar que este script no es 100% correcto (me pregunto si se puede mejorar la precisión de la puntuación si las letras no se convierten en rectángulos al principio), pero es bastante bueno, al menos para el propósito actual. Dilo. ¡Y todas las operaciones se completan en el navegador según la tecnología del cliente estándar!
Como complemento, este script debe considerarse como un caso especial. Esta tecnología puede funcionar bien en otros códigos de verificación simples, pero para códigos de verificación complejos, es un poco. fuera del alcance (especialmente este tipo de análisis basado en el cliente). Espero que más personas puedan inspirarse con este proyecto y desarrollar más cosas maravillosas, porque su potencial es tan grande.