Golf de plaque d'immatriculation: Reconnaissance


20

Voir aussi: Analyse

introduction

Vous travaillez sur une équipe de programmation gouvernementale qui a programmé les radars. Cependant, le groupe de personnes qui a programmé le calculateur de vitesse a pris trop de place, vous devez donc rendre le logiciel de reconnaissance de plaque d'immatriculation aussi petit que possible.

Défi

Étant donné l'image d'une plaque d'immatriculation, renvoyez le texte sur la plaque.

Plaques d'immatriculation

Voici tous les caractères que votre programme doit reconnaître:

ABCDEFG

H1JKLMN0

PQRSTUVW

XYZ01234

56789

Remarque

Sur les plaques d'immatriculation britanniques, les caractères pour I (i) et 1 (un) sont les mêmes et les caractères pour O (o) et 0 (zéro) sont les mêmes. Pour cette raison, supposez toujours que les caractères sont les nombres. Autrement dit, la plaque d'immatriculation suivante est 10 (un zéro):

Exemples

C0D3 GLF

B3T4 DCY

M1NUS 15

YET1CGN

Autres règles

L'accès à Internet et les bibliothèques et fonctions OCR sont interdits.

Les plaques d'immatriculation seront toujours identiques à celles illustrées ci-dessus. Toutes les plaques d'immatriculation seront à peu près de la même taille (il y aura des inexactitudes dues à la méthode de recadrage).

Si vous avez besoin de versions PNG sans perte de toutes les plaques d'immatriculation, je vous les fournirai.

Notation

Le programme le plus court en octets gagne.

Toutes les plaques d'immatriculation sont des captures d'écran de la barre de recherche de ce site


8
Rappelez-moi de traverser votre piège de vitesse. (Ma plaque d'immatriculation contient une lettre O.)
Neil

3
Oui, le titre de cette question est assez inexact. Que diriez-vous "OCR une plaque d'immatriculation britannique" ?
Lynn

3
@Neil My UK plaque d'immatriculation a un O et un 0 et ils semblent identiques. Il existe bien sûr des règles pour déterminer quelle est la bonne interprétation, mais ce serait un tout autre défi.
Level River St

2
C'est dommage que les caractères ne soient pas d'une largeur fixe. Cela pourrait créer des possibilités de code très courtes.
GuitarPicker

1
@YetiCGN Votre souhait est ma commande;)
Beta Decay

Réponses:


11

C, 409 octets (et je suis aussi surpris que quiconque)

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

Prend en entrée: la largeur ( w) et la hauteur ( h) de l'image, suivies des données RVB compressées sous la forme d'un tableau de chars ( d). Tous les autres paramètres de fonction sont des déclarations de variables déguisées. Ignore tout sauf le canal vert et applique un seuil de 32 comme passe initiale.

Généralement identique à la méthode de @ DavidC, sauf que cela vérifie qu'au moins 35% de chaque boîte d'échantillon est remplie. Espérons que cela rendra les changements d'échelle plus robustes, mais qui sait.

J'ai utilisé une méthode de force brute pour déterminer la taille de rééchantillonnage et le pourcentage de couverture à utiliser pour la meilleure fiabilité (c'est-à-dire le moins de cas d'un caractère ayant plusieurs interprétations). Il s'est avéré qu'une grille 4x5 avec une couverture de 35% était la meilleure. J'ai ensuite utilisé une deuxième méthode de force brute pour calculer la meilleure disposition des bits et la meilleure valeur modulo pour regrouper les données de caractères dans une courte chaîne - le bit bas en haut à gauche, augmentant en x puis y, avec la valeur finale% 101 tournée mieux, donnant cette table de correspondance:

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

La soustraction de 7 signifie que les initiales peuvent être supprimées et les 2 dernières peuvent être supprimées sans aucun travail supplémentaire. Cette suppression signifie que certaines entrées non valides peuvent entraîner une lecture de mémoire non valide, ce qui peut entraîner une erreur de segmentation sur des images particulières.

Usage:

Pour y mettre les images, j'ai écrit un wrapper en utilisant libpng. Il s'avère également que malgré le nom de fichier, les images de la question sont en fait des jpeg (!), Vous devrez donc les exporter manuellement en tant que pngs.

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

Panne

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 pour battre Python et Mathemetica avec paniquer C . Oooollllld school, yo.
Robert Fraser

+1 pour GAGNER avec C, comme, jamais pensé que cela pourrait arriver, hein
HyperNeutrino

12

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

La dernière version économise 27 octets en n'exigeant pas que la plaque soit "découpée" avant d'être analysée. L'avant-dernière version a économisé 26 octets en utilisant seulement 10 des 24 points d'échantillonnage d'origine.

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 octets enregistrés grâce à l'idée de LegionMammal978 de regrouper la longue liste de numéros de base 10 en un seul numéro de base 36. Il a analysé encore 20 octets du code final.

Le saut de 528 à 570 octets était dû à un code supplémentaire pour s'assurer que l'ordre des lettres retournées correspondait à l'ordre des lettres sur la plaque d'immatriculation. Le centroïde de chaque lettre contient la coordonnée x, qui révèle les positions relatives des lettres le long de x.


Code non 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

Aperçu

L'idée de base est de vérifier si un échantillonnage systématique des pixels de l'image d'entrée correspond aux pixels du même emplacement sur les images authentiques. Une grande partie du code se compose des signatures de bits pour chaque caractère,

Le diagramme montre les pixels qui sont échantillonnés à partir des lettres "J", "P", "Q" et "R".

jpqr

Les valeurs des pixels peuvent être représentées sous forme de matrices. Les sombres et les gras 1correspondent aux cellules noires. Les 0correspond aux globules blancs.

jjjj

Ce sont les règles de remplacement du déchiffrement pour 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"

Il devrait être possible de comprendre pourquoi la règle pour "0" est:

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

et donc distinguable de la lettre "Q".


Ce qui suit montre les 10 points utilisés dans la version finale. Ces points sont suffisants pour identifier tous les personnages.

réduit


Que font les fonctions

plateCrop[img]supprime le cadre et le bord gauche de la plaque, rend l'arrière-plan blanc. J'ai pu éliminer cette fonction de la version finale en sélectionnant des composants d'image, des lettres possibles d'une hauteur comprise entre 100 et 120 pixels.

platecrop


isolateLetters[img] supprime les lettres individuelles de l'image recadrée.

Nous pouvons montrer comment cela fonctionne en montrant où l'image recadrée, sortie de plateCropva comme entrée pour isolateLetters. La sortie est une liste de caractères individuels.

des lettres


Coordinatessont 24 positions uniformément réparties pour vérifier la couleur des pixels. Les coordonnées correspondent à celles de la première figure.

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 convertit les pixels en binaire.

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

codessont la signature de chaque personnage. Les valeurs décimales sont des abréviations du code binaire pour les cellules noires (0) et blanches (1). Dans la version golf, la base 36 est utilisée.

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

(* decryptRulessont pour remplacer les signatures par leur caractère respectif *)

decryptRules=Rule@@@codes;

f est la fonction qui prend une image d'une plaque d'immatriculation et renvoie une lettre.

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

assiettes

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


Golfé

Le code est raccourci en utilisant un seul nombre décimal pour représenter les 24 bits (blancs ou noirs) pour chaque caractère. Par exemple, la lettre « J » utilise la règle de remplacement suivant: 1118623 -> "J".

1118623 correspond à

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}

qui peut être reconditionné comme

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

qui est simplement la matrice pour "J" que nous avons vue ci-dessus.

%//MatrixForm

matrice

Une autre économie vient de la représentation de l'alphabet "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ"plutôt que d'une liste de lettres.

Enfin, toutes les fonctions de la version longue, sauf h, ont été intégrées à la fonction fplutôt que définies séparément.


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 On dirait que SE a tout gâché; essayez de remplacer {1118623, 2518818, ..., 16645599}par ceci .
LegionMammal978

@ LegionMammal978, votre suggestion a conduit à un raccourcissement du code de plus de 100 octets. Je comprends maintenant mieux comment Mathematica gère les bases.
DavidC

@DavidC En outre, il semble que des espaces blancs se soient glissés dans votre code de golf, et je compte 571 octets sans lui. De plus, certaines fonctions peuvent être converties sous forme d'infixe. x[[All,2,1]]peut être remplacé par x[[;;,2,1]]. Flatten[x,1]est équivalent à Join@@x, et Flatten[#,1]&/@xest équivalent à Join@@@x. Il y a quelques autres optimisations mineures qui peuvent être faites. Le code de 551 octets après ces golfs.
LegionMammal978

Bons conseils et lecture attentive. Merci.
DavidC

Avez-vous essayé de minimiser le nombre de points d'échantillonnage en les déplaçant?
Sparr

4

C #, 1040 1027 octets

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

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

Fondamentalement, j'ai trouvé des points de référence spécifiques pour vérifier le jaune / noir afin de déterminer l'identité de chaque personnage.


Êtes-vous sûr qu'il n'y a pas de surcapacité sur les images fournies et qu'il reconnaîtra les plaques d'immatriculation où les caractères sont par exemple décalés de 10 pixels?
YetiCGN

@YetiCGN, il doit le reconnaître tant que la taille est la même et qu'ils sont dans la même position verticale. J'ai essayé avec tous les exemples fournis, et cela fonctionne; s'il vous plaît laissez-moi savoir si vous en trouvez un où il ne le fait pas
Nick Mertin

Je ne veux pas installer Visual Studio juste pour cela, mais vous pouvez essayer i.imgur.com/i8jkCJu.png qui est un peu plus petit. Je pense qu'il est prudent de supposer que toutes les soumissions seront des images de ce site Web particulier. Au départ, mon commentaire était plus du genre "et si c'était un vrai scan de plaque?" / "Et si quelqu'un d'autre déplaçait tous les caractères verticalement de 10 pixels pour faire une plaque?"
YetiCGN

@YetiCGN vous ne devriez pas avoir besoin de VisualStudio pour compiler, justecsc.exe main.cs /r:System.Drawing.dll
VisualMelon

2

PHP - 1741 1674 1143 octets

Il a d'abord été mis en place en apprenant les profils des personnages à partir des premiers exemples, qui ont ensuite résumé chaque personnage en six chiffres. J'ai choisi six parce que j'en avais à l'origine cinq, et cela n'a pas fonctionné aussi bien que je le souhaiterais, mais six semble fonctionner beaucoup mieux. Une grande partie de l'optimisation consiste à compresser ces profils en nombres d'octets de plus en plus petits.

Le premier et le deuxième profil *lhdfdnet |nnmmkksont en fait la goutte bleue avec "GB" en bas *, et la bordure droite |, que nous ignorons. Il est plus sûr de les inclure afin que la goutte et la bordure droite aient quelque chose à faire correspondre.

Devrait gérer n'importe quel format d'image, toute mise à l'échelle raisonnable à condition que le rapport d'aspect ne change pas trop, aucune couleur foncée sur claire, et même un peu de bruit et d'ombrage!

Il a besoin de la bordure, au moins en haut et en bas, cela fait partie du profil.

<?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,'|*');}

Enregistrez sous ocr.php, puis exécutez à partir de la ligne de commande:

$ 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

Pour ceux qui sont intéressés, voici le code d'apprentissage. Enregistrez sous learn.phpet exécutez à partir de la ligne de commande, aucun argument.

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

?>

Vous devez inclure les espaces dans la sortie
Beta Decay

3
Ce n'est pas dans les spécifications sous Les éléments suivants sont tous les caractères que votre programme doit reconnaître , juste les caractères AH, JN, PZ et 0-9. Aucune mention d'espaces.

Oh, d'accord, le tien va bien alors
Beta Decay

"Le premier et le deuxième profil [...] sont en fait la goutte bleue avec" GB "en bas, et la bordure droite, que nous ignorons." Alors pourquoi les avez-vous inclus dans le code, surtout si la clé du tableau avec une chaîne vide est remplacée? Plus: Il est permis d'utiliser une syntaxe ouverte courte pour le golf de code! :-)
YetiCGN

@YetiCGN - s'ils ne le sont pas, le code tentera de les faire correspondre à autre chose! Je ne savais pas qu'ils étaient remplacés, heureusement que le code fonctionnait toujours. Révision. Vous pourrez peut-être adapter certaines de mes modifications à votre réponse.

0

PHP, 971 970 octets

S'inspire largement de la réponse de Yimin Rong , qui peut être sérieusement minimisée, en particulier les indices de tableau, et placée dans un Phar avec compression gzip.

Téléchargez le phar

Ceci est ma version de base améliorée à 1557 1535 octets, enregistrée simplement sous le nom de fichier "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;}

Améliorations:

1ère étape

  • Les indices de tableau numérique ont été supprimés et réorganisés, les indices de chaîne sous forme de constantes implicites

2e étape

  • Remplacé intvalpar ~~(enregistre 8 octets, deux occurrences)
  • initialisation for-loop supprimée là où cela n'est pas nécessaire
  • file_get_contents($u)remplacé par join('',file($u))(économise 5 octets)
  • et quelques autres

Malheureusement, toutes les améliorations de la deuxième étape ne se traduisent qu'en 1 octet de code moins compressé. :-RÉ

Et ce code a été utilisé pour créer le 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();');

Testez avec php ocr.phar http://i.imgur.com/i8jkCJu.pngou toute autre image du scénario de test.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.