Matrícula Golf: Reconocimiento


20

Ver también: análisis

Introducción

Estás trabajando en un equipo de programación del gobierno, que ha estado programando las cámaras de velocidad. Sin embargo, el grupo de personas que ha programado la calculadora de velocidad ha ocupado demasiado espacio, por lo que debe hacer que el software de reconocimiento de matrículas sea lo más pequeño posible.

Desafío

Dada una imagen de una matrícula, devuelva el texto en la placa.

Placas de matrícula

Los siguientes son todos los caracteres que su programa debe reconocer:

ABCDEFG

H1JKLMN0

PQRSTUVW

XYZ01234

56789

Nota

En las matrículas británicas, los caracteres para I (i) y 1 (uno) son los mismos y los caracteres para O (o) y 0 (cero) son los mismos. Por esa razón, siempre asuma que los caracteres son los números. Es decir, la siguiente matrícula es 10 (un cero):

Ejemplos

C0D3 GLF

B3T4 DCY

M1NUS 15

YET1CGN

Otras reglas

El acceso a Internet y las bibliotecas y funciones de OCR están prohibidas.

Las matrículas siempre se verán idénticas a las que se muestran arriba. Todas las matrículas serán aproximadamente del mismo tamaño (habrá algunas imprecisiones debido al método de recorte).

Si necesita versiones PNG sin pérdidas de cualquier número de matrícula, se las proporcionaré.

Puntuación

El programa más corto en bytes gana.

Todas las matrículas son capturas de pantalla de la barra de búsqueda en este sitio


8
Recuérdame conducir a través de tu trampa de velocidad. (Mi número de matrícula contiene una letra O.)
Neil

3
Sí, el título de esta pregunta es bastante inexacto. ¿Qué tal "OCR una placa británica" ?
Lynn

3
@Neil Mi placa de número del Reino Unido tiene una O y un 0 y se ven idénticos. Por supuesto, hay reglas para determinar cuál es la interpretación correcta, pero eso sería otro desafío.
Level River St el

2
Es una pena que los caracteres no tengan un ancho fijo. Eso podría generar algunas posibilidades de código muy cortas.
GuitarPicker

1
@YetiCGN Tu deseo es mi comando;)
Beta Decay

Respuestas:


11

C, 409 bytes (y estoy tan sorprendido como cualquiera)

f(w,h,d,X,T,B,x,y,b,v,u,t,a)char*d;{for(x=X=0;++x<w;){for(y=b=h;y--;a=0)d[(y*w+x)*3+1]&224||(b=0,X||(X=x,T=B=y),T=y<T?y:T,B=y>B?y:B);if(X*b){for(B+=1-T,X=x-X,v=5;v--;)for(u=4;u--;a|=(b>X/4*(B/5)*.35)<<19-u*5-v)for(b=0,t=X/4;t--;)for(y=B/5;y--;)b+=!(d[((v*B/5+y+T)*w+x-X+u*X/4+t)*3+1]&224);X=!putchar("g------a----mj---et-u--6----7--8s4-c-x--q--d9xy5-0v--n-2-hw-k-----3---bf-----t-r---pzn-1---l"[a%101-7]);}}}

Toma como entrada: el ancho ( w) y la altura ( h) de la imagen, seguidos de los datos RGB empaquetados como una matriz de chars ( d). Todos los demás parámetros de la función son declaraciones de variables disfrazadas. Ignora todo excepto el canal verde y aplica un umbral de 32 como pase inicial.

Principalmente igual que el método de @ DavidC, excepto que esto verifica que al menos el 35% de cada cuadro de muestra esté lleno. Esperemos que eso lo haga más robusto para escalar los cambios, pero quién sabe.

Utilicé un método de fuerza bruta para averiguar qué tamaño de muestreo y porcentaje de cobertura utilizar para la mejor confiabilidad (es decir, la menor cantidad de casos de un personaje con múltiples interpretaciones). Resultó que una cuadrícula de 4x5 con una cobertura del 35% era la mejor. Luego utilicé un segundo método de fuerza bruta para calcular la mejor disposición de bits y el valor del módulo para empaquetar los datos de caracteres en una cadena corta: el bit bajo en la parte superior izquierda, que aumenta en x luego y, con el valor final% 101 convertido fuera mejor, dando esta tabla de búsqueda:

-------g------a----mj---et-u--6----7--8s4-c-x--q--d9xy5-0v--n-2-hw-k-----3---bf-----t-r---pzn-1---l--

Restar 7 significa que las iniciales pueden eliminarse, y las últimas 2 pueden eliminarse sin ningún trabajo adicional. Esta eliminación significa que ciertas entradas inválidas pueden causar una lectura de memoria inválida, por lo que pueden fallar en imágenes particulares.

Uso:

Para obtener las imágenes, escribí un contenedor usando libpng. También resulta que a pesar del nombre del archivo, las imágenes en la pregunta son en realidad jpegs (!), Por lo que primero deberá exportarlas manualmente como png.

#include <png.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, const char *const *argv) {
    if(argc < 2) {
        fprintf(stderr, "Usage: %s <file.png>\n", argv[0]);
        return 1;
    }

    const char *file = argv[1];

    FILE *const fp = fopen(file, "rb");
    if(fp == NULL) {
        fprintf(stderr, "Failed to open %s for reading\n", file);
        return 1;
    }

    png_structp png_ptr = png_create_read_struct(
        PNG_LIBPNG_VER_STRING, NULL, NULL, NULL
    );

    if(!png_ptr) {
        fclose(fp);
        fprintf(stderr, "Failed to initialise LibPNG (A)\n");
        return 1;
    }

    png_infop info_ptr = png_create_info_struct(png_ptr);

    if(!info_ptr) {
        png_destroy_read_struct(&png_ptr, NULL, NULL);
        fclose(fp);
        fprintf(stderr, "Failed to initialise LibPNG (B)\n");
        return 1;
    }

    if(setjmp(png_jmpbuf(png_ptr))) {
        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
        fclose(fp);
        fprintf(stderr, "Error while reading PNG\n");
        return 1;
    }

    png_init_io(png_ptr, fp);
    png_set_sig_bytes(png_ptr, 0);

    png_read_png(
        png_ptr, info_ptr,
        PNG_TRANSFORM_STRIP_16 |
        PNG_TRANSFORM_GRAY_TO_RGB |
        PNG_TRANSFORM_STRIP_ALPHA,
        NULL
    );
    const png_bytep *const rows = png_get_rows(png_ptr, info_ptr);
    const int w = png_get_image_width(png_ptr, info_ptr);
    const int h = png_get_image_height(png_ptr, info_ptr);
    unsigned char *const data = malloc(w*h*3 * sizeof(unsigned char));
    for(int y = 0; y < h; ++ y) {
        for(int x = 0; x < w; ++ x) {
            memcpy(&data[y*w*3], rows[y], w * 3 * sizeof(unsigned char));
        }
    }
    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
    fclose(fp);

    f(w, h, (char*) data);

    free(data);

    return 0;
}

Descompostura

f(                          // Function
    w,h,d,                  // Parameters: width, height, RGB data
    X,T,B,x,y,b,v,u,t,a     // Variables
)char*d;{                   // K&R syntax to save lots of type decls
  for(x=X=0;++x<w;){        // Loop through each column of the image:
    for(y=b=h;y--;a=0)      //  Loop through pixels in column:
      d[(y*w+x)*3+1]&224||( //   If green < 32: (char could be signed or unsigned)
        b=0,                //    This is not a blank line
        X||(X=x,T=B=y),     //    Start a new character if not already in one
        T=y<T?y:T,          //    Record top of character
        B=y>B?y:B           //    Record bottom of character
      );
    if(X*b){                //  If we just found the end of a character:
      // Check cell grid & record bits into "a"
      for(B+=1-T,X=x-X,v=5;v--;)
        for(u=4;u--;a|=(b>X/4*(B/5)*.35)<<19-u*5-v)
          // Calculate coverage of current cell
          for(b=0,t=X/4;t--;)
            for(y=B/5;y--;)
              b+=!(d[((v*B/5+y+T)*w+x-X+u*X/4+t)*3+1]&224);

      // Look up meaning of "a" in table & print, reset X to 0
      X=!putchar(
        "g------a----mj---et-u--6----7--8s4-c-x--q--d9x"
        "y5-0v--n-2-hw-k-----3---bf-----t-r---pzn-1---l"
        [a%101-7]
      );
    }
  }
}

1 por golpear con Python y Mathemetica volviendo loco C . Oooollllld school, yo.
Robert Fraser

+1 por GANAR con C, como, nunca pensé que eso podría pasar, eh
HyperNeutrino

12

Mathematica 1170 1270 1096 1059 650 528 570 551525 498 bytes

La última versión ahorra 27 bytes al no requerir que la placa se "recorte" antes de analizarla. La penúltima versión ahorró 26 bytes al usar solo 10 de los 24 puntos de muestra originales.

z=Partition;h@i_:=i~PixelValue~#/.{_,_,_,z_}:>⌈z⌉&/@z[{45,99,27,81,63,81,9,63,45,63,9,45,45,45,63,45,45,27,45,9},2];f@p_:=h/@SortBy[Select[p~ColorReplace~Yellow~ComponentMeasurements~{"Image","Centroid"},100<Last@ImageDimensions@#[[2,1]]<120&],#[[2,2,1]]&][[All,2,1]]/.Thread[IntegerDigits[#,2,10]&/@(z[IntegerDigits[Subscript["ekqeuiv5pa5rsebjlic4i5886qsmvy34z5vu4e7nlg9qqe3g0p8hcioom6qrrkzv4k7c9fdc3shsm1cij7jrluo", "36"]],4]/.{a__Integer}:> FromDigits[{a}])-> Characters@"BD54TARP89Q0723Z6EFGCSWMNVYXHUJKL1"]

122 bytes guardados a través de la idea de LegionMammal978 de empaquetar la larga lista de números de base 10 como un solo número de base 36. Recortó otros 20 bytes del código final.

El salto de 528 a 570 bytes se debió a un código adicional para garantizar que el orden de las letras devueltas correspondiera al orden de las letras en la placa. El centroide de cada letra contiene la coordenada x, que revela las posiciones relativas de las letras a lo largo de x.


Código sin golf

coordinates=Flatten[Table[{x,y},{y,99,0,-18},{x,9,72,18}],1];
h[img_] :=ArrayReshape[PixelValue[img, #] /. {_, _, _, z_} :>  ⌈z⌉  & /@ coordinates, {6, 4}];
plateCrop[img_]:=ColorReplace[ImageTrim[img,{{100,53},{830,160}}],Yellow];
codes={{{15,13,15,13,13,15},"B"},{{15,8,8,8,9,15},"C"},{{15,13,13,13,13,15},"D"},{{15,8,14,8,8,15},"E"},{{15,8,14,8,8,8},"F"},{{15,8,8,11,9,15},"G"},{{6,6,6,6,15,9},"A"},{{9,9,15,15,9,9},"H"},{{8,8,8,8,8,15},"L"},{{9,15,15,15,13,9},"M"},{{15,9,9,9,9,15},"0"},{{9,10,12,14,10,9},"K"},{{9,13,13,11,11,9},"N"},{{8,8,8,8,8,8},"1"},{{1,1,1,1,9,15},"J"},{{15,9,15,14,8,8},"P"},{{15,9,9,9,15,15},"Q"},{{15,9,15,14,10,11},"R"},{{15,8,12,3,1,15},"S"},{{9,15,6,6,6,6},"V"},{{15,6,6,6,6,6},"T"},{{9,15,15,15,15,15},"W"},{{9,9,9,9,9,15},"U"},{{9,14,6,6,14,9},"X"},{{9,14,6,6,6,6},"Y"},{{15,3,2,4,12,15},"Z"},{{15,9,9,9,9,15},"0"},{{8,8,8,8,8,8},"1"},{{15,1,3,6,12,15},"2"},{{15,1,3,1,9,15},"3"},{{2,6,6,15,2,2},"4"},{{7,12,14,1,1,15},"5"},{{15,8,14,9,9,15},"6"},{{15,1,2,2,6,4},"7"},{{15,9,15,9,9,15},"8"},{{15,9,15,1,9,15},"9"}};
decryptRules=Rule@@@codes;
isolateLetters[img_]:=SortBy[Select[ComponentMeasurements[plateCrop[img],{"Image","Centroid"}],ImageDimensions[#[[2,1]]][[2]]>100&],#[[2,2,1]]&][[All,2,1]]
f[plate_]:=FromDigits[#,2]&/@#&/@h/@isolateLetters[plate]/.decryptRules

Visión general

La idea básica es verificar si un muestreo sistemático de píxeles de la imagen de entrada coincide con los píxeles de la misma ubicación en las imágenes de buena fe. Gran parte del código consiste en las firmas de bits para cada carácter,

El diagrama muestra los píxeles que se toman de las letras "J", "P", "Q" y "R".

jpqr

Los valores de píxel se pueden representar como matrices. Los oscuros y en negrita 1corresponden a las celdas negras. Los 0corresponden a glóbulos blancos.

jjjj

Estas son las reglas de reemplazo de descifrado para JPQ R.

{1, 1, 1, 1, 9, 15} -> "J",
{15, 9, 15, 14, 8, 8} -> "P",
{15, 9, 9, 9, 15, 15 } -> "Q",
{15, 9, 15, 14, 10, 11} -> "R"

Debería ser posible entender por qué la regla para "0" es:

{15, 9, 9, 9, 9, 15} -> "0"

y por lo tanto distinguible de la letra "Q".


A continuación se muestran los 10 puntos utilizados en la versión final. Estos puntos son suficientes para identificar a todos los personajes.

reducido


¿Qué hacen las funciones?

plateCrop[img]elimina el marco y el borde izquierdo de la placa, hace que el fondo sea blanco. Pude eliminar esta función de la versión final seleccionando componentes de imagen, posibles letras que tenían entre 100 y 120 píxeles de alto.

cultivo


isolateLetters[img] elimina las letras individuales de la imagen recortada.

Podemos mostrar cómo funciona mostrando dónde va la imagen recortada, la salida de plateCropcomo entrada isolateLetters. La salida es una lista de caracteres individuales.

letras


Coordinatesson 24 posiciones distribuidas uniformemente para verificar el color del píxel. Las coordenadas corresponden a las de la primera figura.

coordinates=Flatten[Table[{x,y},{y,99,0,-18},{x,9,72,18}],1];

{{9, 99}, {27, 99}, {45, 99}, {63, 99}, {9, 81}, {27, 81}, {45, 81}, {63, 81}, { 9, 63}, {27, 63}, {45, 63}, {63, 63}, {9, 45}, {27, 45}, {45, 45}, {63, 45}, {9, 27}, {27, 27}, {45, 27}, {63, 27}, {9, 9}, {27, 9}, {45, 9}, {63, 9}}


h Convierte los píxeles a binario.

h[img_] :=ArrayReshape[PixelValue[img, #] /. {_, _, _, z_} :>  ⌈z⌉  & /@ coordinates, {6, 4}];

codesson la firma de cada personaje. Los valores decimales son abreviaturas del código binario para las celdas negras (0) y blancas (1). En la versión de golf, se utiliza la base 36.

codes={{{15, 9, 9, 9, 9, 15}, "0"}, {{8, 8, 8, 8, 8, 8}, "1"}, {{15, 1, 3,6,12, 15}, "2"}, {{15, 1, 3, 1, 9, 15}, "3"}, {{2, 6, 6, 15, 2, 2}, "4"}, {{7, 12, 14, 1, 1, 15},"5"}, {{15, 8, 14, 9, 9, 15}, "6"}, {{15, 1, 2, 2, 6, 4},"7"}, {{15, 9, 15, 9, 9, 15}, "8"}, {{15, 9, 15, 1, 9, 15},"9"}, {{6, 6, 6, 6, 15, 9}, "A"}, {{15, 13, 15, 13, 13, 15}, "B"}, {{15, 8, 8, 8, 9, 15}, "C"}, {{15, 13, 13, 13, 13, 15}, "D"}, {{15, 8, 14, 8, 8, 15}, "E"}, {{15, 8, 14, 8, 8, 8},"F"}, {{15, 8, 8, 11, 9, 15}, "G"}, {{9, 9, 15, 15, 9, 9}, "H"}, {{1, 1, 1, 1, 9, 15}, "J"}, {{9, 10, 12, 14, 10, 9}, "K"}, {{8, 8, 8, 8, 8, 15}, "L"}, {{9, 15, 15, 15, 13, 9}, "M"}, {{9, 13, 13, 11, 11, 9}, "N"}, {{15, 9, 15, 14, 8, 8}, "P"}, {{15, 9, 9, 9, 15, 15}, "Q"}, {{15, 9, 15, 14, 10, 11}, "R"}, {{15, 8, 12, 3, 1, 15}, "S"}, {{15, 6, 6, 6, 6, 6}, "T"}, {{9, 9, 9, 9, 9, 15}, "U"}, {{9, 15, 6, 6, 6, 6}, "V"}, {{9, 15, 15, 15, 15, 15}, "W"}, {{9, 14, 6, 6, 14, 9}, "X"}, {{9, 14, 6, 6, 6, 6}, "Y"}, {{15, 3, 2, 4, 12, 15}, "Z"}};

(* decryptRulesson para reemplazar firmas con sus respectivos caracteres *)

decryptRules=Rule@@@codes;

f es la función que toma una imagen de una matrícula y devuelve una letra.

f[plate_]:=FromDigits[#,2]&/@#&/@h/@isolate[plateCrop@plate]/.decryptRules;

platos

{"A", "B", "C", "D", "E", "F", "G"}
{"H", "1", "J", "K", "L", "M", "N", "0"}
{"P", "Q", "R", "S", "T", "U", "V", "W"}
{"X", "Y", "Z", "0", "1", "2", "3", "4"}
{"5", "6", "7", "8", "9"}


Golfed

El código se acorta usando un solo número decimal para representar los 24 bits (blanco o negro) para cada carácter. Por ejemplo, la letra "J" utiliza la siguiente regla de sustitución: 1118623 -> "J".

1118623 corresponde a

IntegerDigits[1118623 , 2, 24]

{0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1}

que se puede reenvasar como

ArrayReshape[{0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1}, {6, 4}]

{{0, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 0, 1}, {1, 0, 0, 1} , {1, 1, 1, 1}}

que es simplemente la matriz para "J" que vimos arriba.

%//MatrixForm

matriz

Otro ahorro proviene de representar el alfabeto en "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ"lugar de una lista de letras.

Finalmente, todas las funciones de la versión larga, excepto h, se integraron en la función en flugar de definirse por separado.


h@i_:=ArrayReshape[i~PixelValue~#/.{_,_,_,z_}:>⌈z⌉&/@Join@@Table[{x,y},{y,99,0,-18},{x,9,72,18}],{6,4}];f@p_:=#~FromDigits~2&/@(Join@@@h/@SortBy[Select[p~ImageTrim~{{100,53},{830,160}}~ColorReplace~Yellow~ComponentMeasurements~{"Image","Centroid"},Last@ImageDimensions@#[[2,1]]>100&],#[[2,2,1]]&][[;;,2,1]])/.Thread[IntegerDigits[36^^1c01agxiuxom9ds3c3cskcp0esglxf68g235g1d27jethy2e1lbttwk1xj6yf590oin0ny1r45wc1i6yu68zxnm2jnb8vkkjc5yu06t05l0xnqhw9oi2lwvzd5f6lsvsb4izs1kse3xvx694zwxz007pnj8f6n,8^8]->Characters@"J4A51LUHKNYXVMW732ZTCGSFE60Q98PRDB"]

@DavidC Parece que SE lo estropeó; intenta reemplazarlo {1118623, 2518818, ..., 16645599}con esto .
LegionMammal978

@ LegionMammal978, su sugerencia condujo a un acortamiento del código en más de 100 bytes. Ahora entiendo mejor cómo Mathematica maneja las bases.
DavidC

@DavidC Además, parece que algún espacio en blanco se coló en su código de golf, y cuento 571 bytes sin él. Además, algunas funciones se pueden convertir en forma infija. x[[All,2,1]]puede ser reemplazado con x[[;;,2,1]]. Flatten[x,1]es equivalente a Join@@x, y Flatten[#,1]&/@xes equivalente a Join@@@x. Hay algunas otras optimizaciones menores que se pueden hacer. El código de 551 bytes después de estos campos de golf.
LegionMammal978

Buenos consejos y lectura cuidadosa. Gracias.
DavidC

¿Intentó minimizar la cantidad de puntos de muestreo moviéndolos?
Sparr

4

C #, 1040 1027 bytes

using System;using System.Drawing;class _{static Bitmap i;static bool b(int x,int y)=>i.GetPixel(x,y).GetBrightness()<.4;static char l(int x,int y){if(y<45)return b(x+5,145)?((b(x+30,100)||b(x+30,50))?(b(x+68,94)?(b(x+40,50)?'D':b(x+40,120)?(b(x+45,80)?'M':'N'):'H'):b(x,97)?(b(x+30,140)?'E':b(x+60,70)?(b(x+50,140)?'R':'P'):'F'):b(x+65,45)?(b(x+5,100)?'K':b(x+30,145)?'Z':'X'):'B'):b(x+30,140)?'L':'1'):b(x+30,55)?(b(x+60,70)?'7':'T'):b(x+2,100)?'U':b(x+30,70)?'W':b(x+15,100)?'V':'Y';if(y<70)return b(x+50,110)?(b(x+50,70)?(b(x+10,110)?(b(x+30,100)?(b(x+55,80)?'8':'6'):b(x+55,80)?'0':'G'):b(x+10,70)?(b(x+60,80)?'9':'S'):b(x+60,120)?'3':'2'):'G'):b(x+30,125)?'Q':'C';if(y>150)return'A';if(y>120)return'J';else return b(x+10,135)?'5':'4';}static void Main(string[]z){i=new Bitmap(Console.ReadLine());bool s=true;int w=int.MinValue;for(int x=100;x<800;++x){for(int y=40;y<160;++y)if(s){if(b(x,y)){if(w>50)Console.Write(' ');Console.Write(l(x,y));s=false;goto e;}}else if(b(x,y))goto e;if(!s){s=true;w=0;}else++w;e:continue;}}}

Sin golf:

using System;
using System.Drawing;

class _
{
    static Bitmap bmp;
    static bool b(int x, int y) => bmp.GetPixel(x, y).GetBrightness() < .4;
    static char l(int x, int y)
    {
        if (y < 45)
            return b(x + 5, 145) ? ((b(x + 30, 100) || b(x + 30, 50)) ? (b(x + 68, 94) ? (b(x + 40, 50) ? 'D' : b(x + 40, 120) ? (b(x + 45, 80) ? 'M' : 'N') : 'H') : b(x, 97) ? (b(x + 30, 140) ? 'E' : b(x + 60, 70) ? (b(x + 50, 140) ? 'R' : 'P') : 'F') : b(x + 65, 45) ? (b(x + 5, 100) ? 'K' : b(x + 30, 145) ? 'Z' : 'X') : 'B') : b(x + 30, 140) ? 'L' : '1') : b(x + 30, 55) ? (b(x + 60, 70) ? '7' : 'T') : b(x + 2, 100) ? 'U' : b(x + 30, 70) ? 'W' : b(x + 15, 100) ? 'V' : 'Y';
        if (y < 70)
            return b(x + 50, 110) ? (b(x + 50, 70) ? (b(x + 10, 110) ? (b(x + 30, 100) ? (b(x + 55, 80) ? '8' : '6') : b(x + 55, 80) ? '0' : 'G') : b(x + 10, 70) ? (b(x + 60, 80) ? '9' : 'S') : b(x + 60, 120) ? '3' : '2') : 'G') : b(x + 30, 125) ? 'Q' : 'C';
        if (y > 150)
            return 'A';
        if (y > 120)
            return 'J';
        if (y > 95)
            return b(x + 10, 135) ? '5' : '4';
        return '-';
    }
    static void Main(string[] args)
    {
        bmp = new Bitmap(Console.ReadLine());
        bool state = true;
        int space = int.MinValue;
        for (int x = 100; x < 800; ++x)
        {
            for (int y = 40; y < 160; ++y)
                if (state)
                {
                    if (b(x, y))
                    {
                        if (space > 50)
                            Console.Write(' ');
                        Console.Write(l(x, y));
                        state = false;
                        goto bad;
                    }
                }
                else if (b(x, y))
                    goto bad;
            if (!state)
            {
                state = true;
                space = 0;
            }
            else
                ++space;
            bad:
            continue;
        }
    }
}

Básicamente encontré algunos puntos de referencia específicos para verificar amarillo / negro para determinar la identidad de cada personaje.


¿Está seguro de que las imágenes proporcionadas no están sobreajustadas y que reconocerá las placas donde los caracteres se desplazan, por ejemplo, 10 píxeles?
YetiCGN

@YetiCGN debería reconocerlo siempre y cuando el tamaño sea el mismo y estén en la misma posición vertical. He intentado con todos los ejemplos proporcionados, y funciona; por favor avíseme si encuentra uno donde no lo hace
Nick Mertin

No quiero instalar Visual Studio solo por esto, pero puedes probar i.imgur.com/i8jkCJu.png, que es un poco más pequeño. Creo que es seguro asumir que todas las presentaciones serán imágenes de ese sitio web en particular. Inicialmente, mi comentario fue más parecido a "¿y si es un escaneo de placa real?" / "¿y si alguien más cambiara todos los caracteres verticalmente por 10 píxeles para hacer una placa?"
YetiCGN

@YetiCGN no debería necesitar VisualStudio para compilar, solocsc.exe main.cs /r:System.Drawing.dll
VisualMelon

2

PHP - 1741 1674 1143 bytes

Primero se configuró aprendiendo los perfiles de los personajes de los primeros ejemplos, que luego resumieron cada personaje en seis números. Elegí seis porque originalmente tenía cinco, y no funcionó tan bien como me gustaría, pero seis parece funcionar mucho mejor. Gran parte de la optimización implica comprimir estos perfiles en recuentos de bytes cada vez más pequeños.

El primer y segundo perfil *lhdfdny |nnmmkkson en realidad la mancha azul con "E" en la parte inferior *y el borde derecho |, lo que estamos haciendo caso omiso. Es más seguro incluirlos para que la burbuja y el borde derecho tengan algo contra lo que comparar.

Debe manejar cualquier formato de imagen, cualquier escala razonable siempre que la relación de aspecto no cambie demasiado, cualquier color oscuro o claro e incluso un poco de ruido y sombreado.

Necesita el borde, al menos en la parte superior e inferior, que es parte del perfil.

<?php $X=[];foreach(str_split('*lhdfdn|nnmmkkA<njjk;BOnKB`^Chn::E7DHn?1X`EnkGGD4Fn_330!Gnj9G[IHnX!!XnJ%(##knKnX.EN6LnX!!!!Mn_<:bnNn^77_nPn^33@6QhfBDjnRn_8LaDSOlYYnUT$$nn$$Uh_##^nV9c][n;W_nWTlhXHnLTiCY4LhnM5ZJbnmaI0ng88lk1nnnnnn2C[__n`34B?Kna4+=Fnb"5NnUReX6gnKKaM7*4Xnb=8gkIIne9K`KKni',7)as$s){$t=[];foreach(str_split(substr($s,1))as$u)$t[]=ord($u)-11;$X[$s[0]]=$t;}echo m(r($argv[1]),$X)."\n";function r($u){$a=[];$i=imagecreatefromstring(file_get_contents($u));$w=imagesx($i);$h=imagesy($i);$s=[];for($x=0;$x<$w;$x++){$s[$x]=0;for($y=0;$y<$h;$y++){$p=imagecolorsforindex($i,imagecolorat($i,$x,$y));if(3*$p['red']+6*$p['green']+$p['blue']<1280)$s[$x]++;}}$j=0;$k=[];for($x=0;$x<$w;$x++){if($s[$x]>$h/10)for($o=0;$o<6;$o++)$k[]=$s[$x];elseif(count($k)){$a[]=$k;$j++;$k=[];}}$b=[];foreach($a as$v){$t=[];$u=array_chunk($v,intval(count($v)/6));foreach($u as$c)$t[]=array_sum($c)/count($c);$m=99/max($t);$e=[];foreach($t as$x)$e[]=intval($x*$m+0.5);$b[]=$e;}return$b;}function m($A,$X){$r='';foreach($A as$a){$s=INF;$c='';foreach($X as$k=>$x){$t=0;for($i=0;$i<6;$i++)$t+=pow($a[$i]-$x[$i],2);if($s>$t){$s=$t;$c=$k;}}$r.=$c;}return trim($r,'|*');}

Guardar como ocr.php, luego ejecutar desde la línea de comando:

$ php ocr.php http://i.imgur.com/UfI63md.png
ABCDEFG

$ php ocr.php http://i.imgur.com/oSAK7dy.png
H1JKLMN0

$ php ocr.php http://i.imgur.com/inuIHjm.png
PQRSTUVW

$ php ocr.php http://i.imgur.com/Th0QkhT.png
XYZ01234

$ php ocr.php http://i.imgur.com/igH3ZPQ.png
56789

$ php ocr.php http://i.imgur.com/YfVwebo.png
10

$ php ocr.php http://i.imgur.com/3ibQARb.png
C0D3GLF

$ php ocr.php http://i.imgur.com/c7XZqhL.png
B3T4DCY

$ php ocr.php http://i.imgur.com/ysBgXhn.png
M1NUS15

Para aquellos que estén interesados, aquí está el código de aprendizaje. Guardar como learn.phpy ejecutar desde la línea de comandos, sin argumentos.

<?php

define('BANDS', 6);

main();

function main()
{
    $glyphs = [];

    learn($glyphs, 'http://imgur.com/UfI63md.png', '*ABCDEFG|');
    learn($glyphs, 'http://imgur.com/oSAK7dy.png', '*H1JKLMN0|');
    learn($glyphs, 'http://imgur.com/inuIHjm.png', '*PQRSTUVW|');
    learn($glyphs, 'http://imgur.com/Th0QkhT.png', '*XYZ01234|');
    learn($glyphs, 'http://imgur.com/igH3ZPQ.png', '*56789|');

    $profiles = summarize($glyphs);

    foreach ($profiles as $glyph=>$profile)
    {
        print $glyph;
        foreach ($profile as $value)
            print chr($value + 11);
        print "\n";
    }
}

function learn(&$glyphs, $url, $answer)
{
    $image = imagecreatefromstring(file_get_contents($url));
    $width = imagesx($image);
    $height = imagesy($image);
    $counts = [];
    for ($x = 0; $x < $width; $x++)
    {
        $counts[$x] = 0;
        for ($y = 0; $y < $height; $y++)
        {
            $pixel = imagecolorsforindex($image, imagecolorat($image, $x, $y));
            if (3 * $pixel['red'] + 6 * $pixel['green'] + $pixel['blue'] < 1280)
                $counts[$x]++;
        }
    }

    $index = 0;
    $expanded = [];
    for ($x = 0; $x < $width; $x++)
    {
        if ($counts[$x] > $height / 10)
            for ($inner = 0; $inner < BANDS; $inner++)
                $expanded[] = $counts[$x];
        else if (count($expanded)) {
            $glyphs[$answer[$index]] = $expanded;
            $index++;
            $expanded = [];
        }
    }
}

function summarize($glyphs)
{
    $profiles = [];
    foreach ($glyphs as $glyph=>$expanded)
    {
        $averages = [];
        $bands = array_chunk($expanded, count($expanded) / BANDS);
        foreach ($bands as $band)
            $averages[] = array_sum($band) / count($band);
        $scaling = 99 / max($averages);
        $profile = [];
        foreach ($averages as $average)
            $profile[] = intval($average * $scaling + 0.5);
        $profiles[$glyph] = $profile;
    }
    return $profiles;
}

?>

Debe incluir los espacios en la salida
Decaimiento Beta

3
Eso no está en las especificaciones debajo de Los siguientes son todos los caracteres que su programa debe reconocer , solo los caracteres AH, JN, PZ y 0-9. No se mencionan espacios.

Oh, está bien, el tuyo está bien entonces
Beta Decay

"El primer y segundo perfil [...] son ​​en realidad la burbuja azul con" GB "en la parte inferior y el borde derecho, que estamos ignorando". Entonces, ¿por qué los incluiste en el código, especialmente si se sobrescribe la clave de matriz con una cadena vacía? Además: ¡Se permite usar una sintaxis abierta corta para golf de código! :-)
YetiCGN

@YetiCGN: si no lo están, ¡el código intentará asociarlos con otra cosa! No me di cuenta de que estaban sobrescritos, por suerte el código aún funcionaba. Revisando Es posible que pueda adaptar algunos de mis cambios a su respuesta.

0

PHP, 971 970 bytes

Se basa en gran medida en la respuesta de Yimin Rong , que se puede reducir seriamente, especialmente los índices de matriz, y poner en un Phar con compresión gzip.

Descargar el phar

Esta es mi versión base mejorada en 1557 1535 bytes, guardada simplemente bajo el nombre de archivo "o":

<?$X=[[99,92,45,45,97,96],[99,99,99,99,99,99],[56,80,84,84,99,85],[41,55,52,64,99,86],[32,50,59,99,87,23],[67,99,74,71,90,77],[92,99,64,64,86,66],[31,41,77,99,87,50],[92,96,62,62,99,90],[64,85,64,64,99,94],''=>[99,99,98,98,96,96],A=>[49,99,95,95,96,48],B=>[68,99,64,55,85,83],C=>[93,99,47,47,58,44],D=>[61,99,52,38,77,85],E=>[99,96,60,60,57,41],F=>[99,84,40,40,37,22],G=>[99,95,46,60,80,62],H=>[99,77,22,22,77,99],1=>[99,99,99,99,99,99],J=>[26,29,24,24,96,99],K=>[99,77,35,58,67,43],L=>[99,77,22,22,22,22],M=>[99,84,49,47,87,99],N=>[99,83,44,44,84,99],P=>[99,83,40,40,53,43],Q=>[93,91,55,57,95,99],R=>[99,84,45,65,86,57],S=>[68,97,78,78,99,74],T=>[25,25,99,99,25,25],U=>[93,84,24,24,83,99],V=>[46,88,82,80,99,48],W=>[84,99,76,73,97,93],X=>[61,99,65,73,94,56],Y=>[41,65,93,99,66,42],Z=>[63,87,99,98,86,62]];echo m(r($argv[1]),$X);function r($u){$a=[];$i=imagecreatefromstring(join('',file($u)));$w=imagesx($i);$h=imagesy($i);$s=[];for(;$x<$w;$x++){$s[$x]=0;for($y=0;$y<$h;$y++){$p=imagecolorsforindex($i,imagecolorat($i,$x,$y));if(3*$p[red]+6*$p[green]+$p[blue]<1280)$s[$x]++;}}$j=0;$k=[];for(;$z<$w;$z++){if($s[$z]>$h/10)for($o=0;$o<6;$o++)$k[]=$s[$z];elseif(count($k)){$a[]=$k;$j++;$k=[];}}$b=[];foreach($a as$v){$t=[];$u=array_chunk($v,~~(count($v)/6));foreach($u as$c)$t[]=array_sum($c)/count($c);$m=99/max($t);$e=[];foreach($t as$x)$e[]=~~($x*$m+.5);$b[]=$e;}return$b;}function m($A,$X){$r='';foreach($A as$a){$s=INF;$c='';foreach($X as$k=>$x){$t=0;for($i=0;$i<6;)$t+=($a[$i]-$x[$i++])**2;if($s>$t){$s=$t;$c=$k;}}$r.=$c;}return$r;}

Mejoras:

1ra etapa

  • Los índices de matriz numéricos se eliminaron y reordenaron, los índices de cadena como constantes implícitas

2da etapa

  • Reemplazado intvalcon ~~(ahorra 8 bytes, dos ocurrencias)
  • inicialización for-loop eliminada donde no es necesario
  • file_get_contents($u)reemplazado con join('',file($u))(ahorra 5 bytes)
  • y algunos otros

Desafortunadamente, todas las mejoras de la segunda etapa solo se traducen en 1 byte menos código comprimido. :-RE

Y este código se usó para crear el Phar:

<?php
$phar = new Phar('o.phar');
$phar->addFile('o');
$phar['o']->compress(Phar::GZ);
$phar->setStub('<?Phar::mapPhar(o.phar);include"phar://o.phar/o";__HALT_COMPILER();');

Pruebe con php ocr.phar http://i.imgur.com/i8jkCJu.pngo con cualquier otra de las imágenes del caso de prueba.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.