Número Plate Golf: reconhecimento

20

Veja também: Analisando

Introdução

Você está trabalhando em uma equipe de programação do governo, que está programando os radares de velocidade. No entanto, o grupo de pessoas que programou a calculadora de velocidade ocupou muito espaço; portanto, você deve tornar o software de reconhecimento da placa de número o menor possível.

Desafio

Dada a imagem de uma chapa de matrícula, retorne o texto na chapa.

Chapas de matrícula

A seguir, são apresentados todos os caracteres que seu programa deve reconhecer:

ABCDEFG

H1JKLMN0

PQRSTUVW

XYZ01234

56789

Nota

Nas chapas de matrícula britânicas, os caracteres para I (i) e 1 (um) são os mesmos e os caracteres para O (o) e 0 (zero) são os mesmos. Por esse motivo, sempre assuma que os caracteres são os números. Ou seja, a seguinte matrícula é 10 (um zero):

Exemplos

C0D3 GLF

B3T4 DCY

M1NUS 15

YET1CGN

Outras regras

As bibliotecas e funções de acesso à Internet e OCR não são permitidas.

As chapas de matrícula sempre serão idênticas às mostradas acima. Todas as chapas de matrícula terão aproximadamente o mesmo tamanho (haverá algumas imprecisões devido ao método de corte).

Se você precisar de versões PNG sem perdas de quaisquer chapas de matrícula, eu as fornecerei.

Pontuação

O programa mais curto em bytes vence.

Todas as chapas de matrícula são capturas de tela da barra de pesquisa deste site

Beta Decay
fonte
8
Lembre-me de atravessar sua armadilha de velocidade. (Meu número da placa contém uma letra O.)
Neil
3
Sim, o título desta pergunta é bastante impreciso. Que tal "OCR uma placa britânica" ?
21416 Lynn
3
@ Neil Minha chapa de matrícula do Reino Unido tem um O e um 0 e eles parecem idênticos. É claro que existem regras para determinar qual é a interpretação correta, mas esse seria outro desafio.
Level River St
2
É uma pena que os caracteres não tenham uma largura fixa. Isso poderia criar algumas possibilidades de código muito curtas.
GuitarPicker 19/08/16
1
@YetiCGN Seu desejo é um comando;)
Decay Beta

Respostas:

11

C, 409 bytes (e estou tão surpreso quanto qualquer um)

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: a largura ( w) e a altura ( h) da imagem, seguidas pelos dados RGB compactados como uma matriz de chars ( d). Todos os outros parâmetros de função são declarações variáveis ​​disfarçadas. Ignora tudo, exceto o canal verde, e aplica um limite de 32 como um passe inicial.

Principalmente o mesmo método do @ DavidC, exceto que isso verifica se pelo menos 35% de cada caixa de amostra está preenchida. Espero que isso torne mais robusto a escala de mudanças, mas quem sabe.

Usei um método de força bruta para descobrir qual tamanho de reamostragem e porcentagem de cobertura usar para obter a melhor confiabilidade (ou seja, o menor número de casos de um personagem com várias interpretações). Verificou-se que uma grade 4x5 com cobertura de 35% era a melhor. Em seguida, usei um segundo método de força bruta para calcular a melhor disposição de bits e valor do módulo para compactar os dados dos caracteres em uma sequência curta - o bit baixo na parte superior esquerda, aumentando em x então y, com o valor final% 101 ativado melhor, fornecendo esta tabela de pesquisa:

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

Subtrair 7 significa que as iniciais podem ser removidas e as duas últimas podem ser removidas sem nenhum trabalho extra. Essa remoção significa que certas entradas inválidas podem causar uma leitura de memória inválida e, portanto, podem falhar em determinadas imagens.

Uso:

Para colocar as imagens nele, escrevi um wrapper usando libpng. Além disso, apesar do nome do arquivo, as imagens na pergunta são na verdade jpegs (!), Então você precisará exportá-las manualmente como pngs primeiro.

#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;
}

Demolir

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]
      );
    }
  }
}
Dave
fonte
+1 para bater Python e Mathemetica com pânico C . Escola secundária, yo.
Robert Fraser
+1 para ganhar com C, como, nunca pensei que poderia acontecer, né
HyperNeutrino
12

Mathematica 1170 1270 1096 1059 650 528 570 551 525 498 bytes

A versão mais recente salva 27 bytes ao não exigir que a placa seja "aparada" antes de ser analisada. A penúltima versão salvou 26 bytes usando apenas 10 dos 24 pontos de amostra originais.

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 salvos com a idéia de LegionMammal978 de compactar a longa lista de números da base 10 como um único número da base 36. Ele eliminou outros 20 bytes do código final.

O salto de 528 para 570 bytes ocorreu devido a um código adicional para garantir que a ordem das letras retornadas correspondesse à ordem das letras na placa do carro. O centróide de cada letra contém a coordenada x, que revela as posições relativas das letras ao longo de x.


Código Ungolfed

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

visão global

A idéia básica é verificar se uma amostragem sistemática de pixels da imagem de entrada corresponde a pixels do mesmo local nas imagens de boa-fé. Grande parte do código consiste em assinaturas de bit para cada caractere,

O diagrama mostra os pixels que são amostrados das letras "J", "P", "Q" e "R".

jpqr

Os valores de pixel podem ser representados como matrizes. O escuro, negrito 1corresponde a células pretas. Os 0correspondem a glóbulos brancos.

jjjj

Estas são as regras de substituição de descriptografia 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"

Deve ser possível entender por que a regra para "0" é:

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

e, portanto, distinguível da letra "Q".


A seguir, são mostrados os 10 pontos usados ​​na versão final. Esses pontos são suficientes para identificar todos os caracteres.

reduzido


O que as funções fazem

plateCrop[img]remove o quadro e a borda esquerda da placa, deixa o fundo branco. Consegui eliminar essa função da versão final selecionando componentes de imagem, possíveis letras com altura entre 100 e 120 pixels.

platecrop


isolateLetters[img] remove as letras individuais da imagem cortada.

Podemos mostrar como funciona, mostrando para onde a imagem cortada e a saída plateCropsão inseridas isolateLetters. A saída é uma lista de caracteres individuais.

cartas


Coordinatessão 24 posições uniformemente distribuídas para verificar a cor do pixel. As coordenadas correspondem às da primeira 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 converte os pixels em binários.

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

codessão a assinatura de cada personagem. Os valores decimais são abreviações do código binário para células pretas (0) e brancas (1). Na versão de golfe, a base 36 é usada.

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"}};

(* decryptRulessão para substituir assinaturas por seus respectivos caracteres *)

decryptRules=Rule@@@codes;

f é a função que tira uma imagem de uma placa e retorna uma carta.

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

pratos

{"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"}


Golfe

O código é reduzido usando um único número decimal para representar todos os 24 bits (branco ou preto) para cada caractere. Por exemplo, a letra "J" usa a seguinte regra de substituição: 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 pode ser reembalado 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 é simplesmente a matriz para "J" que vimos acima.

%//MatrixForm

matriz

Outra economia vem da representação do alfabeto "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ"e não da lista de letras.

Por fim, todas as funções da versão longa, exceto h, foram integradas à função em fvez de definidas separadamente.


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
fonte
@DavidC Parece que o SE estragou tudo; tente substituir {1118623, 2518818, ..., 16645599}por isso .
LegionMammal978
@ LegionMammal978, sua sugestão levou a uma redução do código em mais de 100 bytes. Agora entendo melhor como o Mathematica lida com bases.
DavidC
@DavidC Além disso, parece que alguns espaços em branco se infiltraram no seu código de golfe e eu conto 571 bytes sem ele. Além disso, algumas funções podem ser convertidas para o formato infix. x[[All,2,1]]pode ser substituído por x[[;;,2,1]]. Flatten[x,1]é equivalente a Join@@xe Flatten[#,1]&/@xé equivalente a Join@@@x. Existem algumas outras otimizações menores que podem ser feitas. O código de 551 bytes depois desses campos de golfe.
usar o seguinte comando
Boas dicas e leitura cuidadosa. Obrigado.
21416
Você tentou minimizar o número de pontos de amostragem movendo-os?
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;}}}

Ungolfed:

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;
        }
    }
}

Basicamente, encontrei alguns pontos de referência específicos para verificar amarelo / preto para determinar a identidade de cada personagem.

Nick Mertin
fonte
Você tem certeza de que não há ajuste excessivo das imagens fornecidas e que ele reconhecerá as placas onde os caracteres são, por exemplo, deslocados em 10 pixels?
YetiCGN
@YetiCGN deve reconhecê-lo desde que o tamanho seja o mesmo e eles estejam na mesma posição vertical. Eu tentei com todos os exemplos fornecidos e funciona; por favor me avise se você encontrar um onde isso não acontece
Nick Mertin
Não quero instalar o Visual Studio apenas para isso, mas você pode tentar o i.imgur.com/i8jkCJu.png, que é um pouco menor em tamanho. Eu acho que é seguro supor que todos os envios serão imagens desse site específico. Inicialmente, meu comentário foi mais parecido com "e se for uma verdadeira digitalização de placas?" / "e se alguém mudasse todos os caracteres verticalmente em 10 pixels para formar um prato?"
precisa saber é o seguinte
@YetiCGN você não deve precisar VisualStudio para compilar, apenascsc.exe main.cs /r:System.Drawing.dll
VisualMelon
2

PHP - 1741 1674 1143 bytes

Foi criado pela primeira vez, aprendendo os perfis dos personagens nos primeiros exemplos, que resumiram cada caractere em seis números. Escolhi seis porque originalmente tinha cinco, e não funcionou tão bem quanto eu gostaria, mas seis parece funcionar muito melhor. Grande parte da otimização envolve a compressão desses perfis em contagens de bytes cada vez menores.

O primeiro e o segundo perfil *lhdfdne |nnmmkksão na verdade o blob azul com "GB" na parte inferior *e a borda direita |, que estamos ignorando. É mais seguro incluí-los para que o blob e a borda direita tenham algo a que corresponder.

Deve lidar com qualquer formato de imagem, qualquer escala razoável, desde que a proporção não mude muito, qualquer cor escura ou clara e até um pouco de ruído e sombreamento!

Ele precisa da borda, pelo menos na parte superior e inferior, que faz parte do 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,'|*');}

Salve como ocr.phpe execute a partir da linha 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 aqueles que estão interessados, aqui está o código de aprendizado. Salve como learn.phpe execute na linha de comando, sem 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;
}

?>

fonte
Você deve incluir os espaços na saída
Decay Beta
3
Isso não está nas especificações em A seguir estão todos os caracteres que seu programa deve reconhecer , apenas os caracteres AH, JN, PZ e 0-9. Nenhuma menção de espaços.
Ah, tudo bem, o seu está bem então
Decay Beta
"O primeiro e o segundo perfil [...] são na verdade o blob azul com" GB "na parte inferior e a borda direita, que estamos ignorando". Então, por que você os incluiu no código, especialmente se a chave da matriz com uma string vazia foi substituída? Plus: É permitido o uso de sintaxe curta e aberta para o código de golfe! :-)
YetiCGN
@YetiCGN - se não estiverem, o código tentará combiná-los com outra coisa! Eu não percebi que eles foram substituídos, por sorte o código ainda funcionava. Revisando. Você pode adaptar algumas das minhas alterações à sua resposta.
0

PHP, 971 970 bytes

Inspira-se fortemente sobre Yimin Rong 's resposta , que pode ser seriamente golfed para baixo, especialmente os índices de array, e colocados em um Phar com compressão gzip.

Faça o download do phar

Esta é minha versão base aprimorada em 1557 1535 bytes, salva simplesmente sob o nome de arquivo "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;}

Melhorias:

1ª etapa

  • Índices numéricos de matriz removidos e reordenados na matriz, índices de string como constantes implícitas

2ª etapa

  • Substituído intvalpor ~~(salva 8 bytes, duas ocorrências)
  • inicialização por loop removida quando desnecessário
  • file_get_contents($u)substituído por join('',file($u))(salva 5 bytes)
  • e alguns outros

Infelizmente, todas as melhorias no segundo estágio são convertidas apenas em um código com menos bytes compactados em gzip. :-D

E esse código foi usado para criar o 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();');

Teste com php ocr.phar http://i.imgur.com/i8jkCJu.pngou qualquer outra imagem de caso de teste.

YetiCGN
fonte