Golf z tablicą rejestracyjną: Uznanie

20

Zobacz także: Analiza składni

Wprowadzenie

Pracujesz w rządowym zespole programistycznym, który programuje fotoradary. Jednak grupa osób, które zaprogramowały kalkulator prędkości, zajęła zbyt dużo miejsca, więc musisz zminimalizować oprogramowanie do rozpoznawania tablic rejestracyjnych.

Wyzwanie

Biorąc pod uwagę obraz tablicy rejestracyjnej, umieść tekst na tablicy.

Tablice rejestracyjne

Oto wszystkie znaki, które program musi rozpoznać:

ABCDEFG

H1JKLMN0

PQRSTUVW

XYZ01234

56789

Uwaga

Na brytyjskich tablicach rejestracyjnych znaki dla I (i) i 1 (jeden) są takie same, a znaki dla O (o) i 0 (zero) są takie same. Z tego powodu zawsze zakładaj, że znaki są liczbami. Czyli następująca tablica rejestracyjna to 10 (jedno zero):

Przykłady

C0D3 GLF

B3T4 DCY

M1NUS 15

YET1CGN

Inne zasady

Dostęp do Internetu oraz biblioteki i funkcje OCR są niedozwolone.

Tablice rejestracyjne zawsze będą wyglądały identycznie jak te pokazane powyżej. Wszystkie tablice rejestracyjne będą mniej więcej tego samego rozmiaru (będą pewne niedokładności z powodu metody przycinania).

Jeśli potrzebujesz bezstratnych wersji tablic rejestracyjnych w formacie PNG, dostarczę je ci.

Punktacja

Najkrótszy program w bajtach wygrywa.

Wszystkie tablice rejestracyjne to zrzuty ekranu paska wyszukiwania w tej witrynie

Rozpad beta
źródło
8
Przypomnij mi, żebym przejechał twoją pułapkę prędkości. (Moja tablica rejestracyjna zawiera literę O.)
Neil
3
Tak, tytuł tego pytania jest dość niedokładny. Co powiesz na „OCR brytyjską tablicę rejestracyjną” ?
Lynn,
3
@Neil Moja tablica rejestracyjna w Wielkiej Brytanii ma zarówno O, jak i 0 i wyglądają identycznie. Istnieją oczywiście zasady określające, która interpretacja jest poprawna, ale byłoby to zupełnie inne wyzwanie.
Level River St
2
Szkoda, że ​​postacie nie mają stałej szerokości. To może stworzyć bardzo krótkie możliwości kodu.
GuitarPicker,
1
@YetiCGN Twoje życzenie jest dla mnie rozkazem;)
Rozpad Beta

Odpowiedzi:

11

C, 409 bajtów (i jestem równie zaskoczony jak wszyscy)

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

Pobiera jako dane wejściowe: szerokość ( w) i wysokość ( h) obrazu, a następnie spakowane dane RGB jako tablica chars ( d). Wszystkie pozostałe parametry funkcji są zmiennymi deklaracjami w przebraniu. Ignoruje wszystko oprócz kanału zielonego i stosuje próg 32 jako początkowe przejście.

Przeważnie to samo co metoda @ DavidC, z tym wyjątkiem, że co najmniej 35% każdego pola próbki jest wypełnione. Mamy nadzieję, że dzięki temu skalowanie zmian jest bardziej niezawodne, ale kto wie.

Użyłem metody brute-force, aby dowiedzieć się, który rozmiar ponownego próbkowania i procent pokrycia należy użyć dla najlepszej niezawodności (tj. Najmniejszej liczby przypadków, w których jeden znak ma wiele interpretacji). Okazało się, że najlepsza była siatka 4x5 z 35% pokryciem. Następnie użyłem drugiej metody brute-force, aby obliczyć najlepsze ustawienie bitu i wartość modulo, aby spakować dane znaku do krótkiego ciągu - niski bit w lewym górnym rogu, rosnący w x, a następnie y, z końcową wartością% 101 zmienioną najlepiej, podając tę ​​tabelę odnośników:

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

Odejmowanie 7 oznacza, że ​​inicjały można usunąć, a ostatnie 2 można usunąć bez dodatkowej pracy. Usunięcie to oznacza, że ​​niektóre nieprawidłowe dane wejściowe mogą spowodować nieprawidłowy odczyt pamięci, co może spowodować uszkodzenie poszczególnych obrazów.

Stosowanie:

Aby umieścić w nim obrazy, napisałem opakowanie za pomocą libpng. Okazuje się również, że pomimo nazwy pliku obrazy w pytaniu są w rzeczywistości JPEG (!), Więc najpierw musisz je ręcznie wyeksportować jako 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;
}

Awaria

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
źródło
+1 za pobicie Python i Mathemetica z wariować C . Oooollllld school, yo.
Robert Fraser,
+1 za ZWYCIĘSTWO z C, jakby, nigdy nie
sądziłem
12

Mathematica 1170 1270 1096 1059 650 528 570 551 525 498 bajtów

Najnowsza wersja pozwala zaoszczędzić 27 bajtów, ponieważ nie wymaga „przycinania” tablicy przed parsowaniem. Przedostatnia wersja zaoszczędziła 26 bajtów, wykorzystując tylko 10 oryginalnych 24 punktów próbnych.

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 bajty zaoszczędzone dzięki pomysłowi LegionMammal978 spakowania długiej listy liczb podstawowych 10 jako pojedynczej liczby podstawowej 36. Oddzielił kolejne 20 bajtów od końcowego kodu.

Skok z 528 do 570 bajtów wynikał z dodatkowego kodu, aby upewnić się, że kolejność zwracanych liter odpowiada kolejności liter na tablicy rejestracyjnej. Środek ciężkości każdej litery zawiera współrzędną x, która ujawnia względne położenie liter wzdłuż x.


Kod niepoznany

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

Przegląd

Podstawową ideą jest sprawdzenie, czy systematyczne próbkowanie pikseli z obrazu wejściowego jest zgodne z pikselami z tego samego miejsca na obrazach bonafide. Znaczna część kodu składa się z sygnatur bitowych dla każdego znaku,

Schemat pokazuje piksele próbkowane z liter „J”, „P”, „Q” i „R”.

jpqr

Wartości pikseli mogą być reprezentowane jako macierze. Ciemne, pogrubione 1odpowiadają czarnym komórkom. Te 0odpowiadają białym komórkom.

jjjj

Są to zasady zastępowania odszyfrowywania 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”

Powinno być możliwe zrozumienie, dlaczego reguła dla „0” to:

{15, 9, 9, 9, 9, 15} -> „0”

i dlatego można je odróżnić od litery „Q”.


Poniżej przedstawiono 10 punktów wykorzystanych w ostatecznej wersji. Punkty te są wystarczające do zidentyfikowania wszystkich znaków.

zredukowany


Jakie funkcje pełnią

plateCrop[img]usuwa ramkę i lewą krawędź z płyty, sprawia, że ​​tło jest białe. Udało mi się wyeliminować tę funkcję z ostatecznej wersji, wybierając komponenty obrazu, możliwe litery o wysokości od 100 do 120 pikseli.

platecrop


isolateLetters[img] usuwa pojedyncze litery z przyciętego obrazu.

Możemy pokazać, jak to działa, pokazując, gdzie przycięty obraz, wyjście z plateCropidzie jako dane wejściowe isolateLetters. Dane wyjściowe to lista pojedynczych znaków.

listy


Coordinatesto 24 równomiernie rozmieszczone pozycje do sprawdzania koloru pikseli. Współrzędne odpowiadają tym na pierwszym rysunku.

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 konwertuje piksele na binarne.

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

codessą sygnaturą każdego znaku. Wartości dziesiętne są skrótami kodu binarnego dla czarnych (0) i białych (1) komórek. W wersji golfowej stosowana jest podstawa 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"}};

(* decryptRulessłużą do zastępowania podpisów ich odpowiednimi postaciami *)

decryptRules=Rule@@@codes;

f to funkcja, która pobiera obraz tablicy rejestracyjnej i zwraca literę.

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

talerze

{„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”}


Grał w golfa

Kod jest skracany za pomocą pojedynczej liczby dziesiętnej reprezentującej wszystkie 24 bity (biały lub czarny) dla każdego znaku. Na przykład litera „J” stosuje następującą regułę zastępczy: 1118623 -> "J".

1118623 odpowiada

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}

który można ponownie zapakować jako

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

który jest po prostu matrycą „J”, którą widzieliśmy powyżej.

%//MatrixForm

matryca

Kolejne oszczędności wynikają z reprezentowania alfabetu "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ"zamiast listy liter.

Na koniec wszystkie funkcje z długiej wersji, oprócz h, zostały zintegrowane z funkcją, fa nie zdefiniowane osobno.


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
źródło
@DavidC Wygląda na to, że SE pomieszało; spróbuj wymienić {1118623, 2518818, ..., 16645599}z tego .
LegionMammal978,
@ LegionMammal978, twoja sugestia doprowadziła do skrócenia kodu o ponad 100 bajtów. Teraz rozumiem lepiej, jak Mathematica obsługuje bazy.
DavidC,
@DavidC Wygląda również na to, że jakaś biała spacja prześlizgnęła się do twojego golfowego kodu, a ja liczę 571 bajtów bez niego. Dodatkowo niektóre funkcje można konwertować do postaci infix. x[[All,2,1]]można zastąpić x[[;;,2,1]]. Flatten[x,1]jest równoważne Join@@xi Flatten[#,1]&/@xjest równoważne z Join@@@x. Istnieje kilka innych drobnych optymalizacji, które można wykonać. Kod 551 bajtów po tych golfach.
LegionMammal978,
Ładne wskazówki i uważna lektura. Dzięki.
DavidC,
Czy próbowałeś zminimalizować liczbę punktów próbkowania, przesuwając je?
Sparr
4

C #, 1040 1027 bajtów

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

Nie golfowany:

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

Zasadniczo znalazłem pewne konkretne punkty odniesienia, aby sprawdzić żółty / czarny, aby określić tożsamość każdego znaku.

Nick Mertin
źródło
Czy na pewno nie ma nadmiaru w dostarczonych obrazach i że rozpozna tablice rejestracyjne tam, gdzie znaki są przesunięte o 10 pikseli?
YetiCGN,
@YetiCGN powinien to rozpoznać, o ile rozmiar jest taki sam i znajdują się w tej samej pozycji pionowej. Próbowałem ze wszystkimi podanymi przykładami i działa; daj mi znać, jeśli znajdziesz taki, w którym go nie ma
Nick Mertin,
Nie chcę instalować programu Visual Studio tylko w tym celu, ale możesz wypróbować i.imgur.com/i8jkCJu.png, który jest nieco mniejszy. Myślę, że można bezpiecznie założyć, że wszystkie zgłoszenia będą obrazami z tej konkretnej witryny. Początkowo mój komentarz był bardziej podobny do „co, jeśli to prawdziwy skan płyty?” / "co jeśli ktoś przesunie wszystkie znaki w pionie o 10 pikseli, aby zrobić płytkę?"
YetiCGN,
@YetiCGN nie powinieneś potrzebować VisualStudio do kompilacji, po prostucsc.exe main.cs /r:System.Drawing.dll
VisualMelon
2

PHP - 1741 1674 1143 bajtów

Najpierw utworzono go, ucząc się profili postaci z kilku pierwszych przykładów, które następnie podsumowały każdą postać na sześć liczb. Wybrałem sześć, ponieważ pierwotnie miałem pięć, i to nie działało tak dobrze, jak bym chciał, ale sześć wydaje się działać znacznie lepiej. Znaczna część optymalizacji polega na ściskaniu tych profili na coraz mniejszą liczbę bajtów.

Pierwszy i drugi profil, *lhdfdna |nnmmkkwłaściwie niebieski kropla z „GB” u dołu *i prawą ramką |, które ignorujemy. Bezpieczniej jest je uwzględnić, aby kropelka i prawa krawędź miały coś do dopasowania.

Powinien obsługiwać dowolny format obrazu, każde rozsądne skalowanie, pod warunkiem, że proporcje nie zmieniają się zbytnio, żadnego ciemnego na jasnym kolorze, a nawet odrobiny szumu i cieniowania!

Potrzebuje granicy, przynajmniej u góry iu dołu, która jest częścią profilu.

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

Zapisz jako ocr.php, a następnie uruchom z wiersza poleceń:

$ 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

Dla zainteresowanych, oto kod do nauki. Zapisz jako learn.phpi uruchom z wiersza poleceń, bez argumentów.

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

?>

źródło
Powinieneś dołączyć spacje do wyniku
Beta Decay
3
Nie ma tego w specyfikacjach w części Poniżej znajdują się wszystkie znaki, które program musi rozpoznać , tylko znaki AH, JN, PZ i 0-9. Brak wzmianki o spacjach.
Och, ok, twoje jest w porządku
Beta Decay
„Pierwszy i drugi profil [...] to w rzeczywistości niebieski kropla z„ GB ”u dołu i prawą ramką, którą ignorujemy.” Dlaczego więc umieściłeś je w kodzie, zwłaszcza jeśli klucz tablicy z pustym ciągiem jest zastępowany? Plus: Dozwolone jest stosowanie krótkiej otwartej składni do golfa kodowego! :-)
YetiCGN,
@YetiCGN - jeśli nie są, kod spróbuje dopasować je do czegoś innego! Nie zdawałem sobie sprawy, że zostały nadpisane, na szczęście kod nadal działał. Korekta Być może będziesz w stanie dostosować niektóre moje zmiany do swojej odpowiedzi.
0

PHP, 971 970 bajtów

Rysuje mocno na Yimin Rong „s odpowiedzi , które mogą być poważnie grałem w dół, zwłaszcza indeksów tablicy, i umieścić w Phar z kompresją gzip.

Pobierz phar

To jest moja ulepszona wersja podstawowa o 1557 1535 bajtach, zapisana pod nazwą pliku „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;}

Ulepszenia:

1. etap

  • Indeksy tablic numerycznych usunięto i ponownie uporządkowano tablicę, indeksy łańcuchów jako stałe niejawne

2. etap

  • Zastąpione intvalz ~~(oszczędność 8 bajtów, dwie occurences)
  • inicjalizacja pętli for usunięta, gdy nie jest to konieczne
  • file_get_contents($u) zastąpiony przez join('',file($u)) (zapisuje 5 bajtów)
  • i kilka innych

Niestety, wszystkie ulepszenia drugiego etapu przekładają się tylko na 1 bajt kodu gzipowanego. :-RE

I ten kod został użyty do utworzenia Phara:

<?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();');

Przetestuj za pomocą php ocr.phar http://i.imgur.com/i8jkCJu.pngdowolnego innego obrazu przypadku testowego.

YetiCGN
źródło