Номерний знак Гольф: Розпізнавання


20

Дивіться також: Парсінг

Вступ

Ви працюєте над урядовою командою з програмування, яка програмувала камери швидкості. Однак група людей, які запрограмували калькулятор швидкості, зайняла занадто багато місця, тому вам доведеться зробити програмне забезпечення для розпізнавання номерних знаків якомога меншим.

Виклик

Давши зображення номерного знака, поверніть текст на табличці.

Номерні знаки

Нижче наведено всі символи, які ваша програма повинна розпізнати:

ABCDEFG

H1JKLMN0

PQRSTUVW

XYZ01234

56789

Примітка

На британських номерних знаках символи для I (i) та 1 (one) є однаковими, а символи для O (o) та 0 (zero) - однаковими. З цієї причини завжди вважайте, що символи - це числа. Тобто наступний номерний знак - 10 (один нуль):

Приклади

C0D3 GLF

B3T4 DCY

M1NUS 15

YET1CGN

Інші правила

Доступ до Інтернету та бібліотеки та функції OCR заборонено.

Номерні таблички завжди будуть виглядати ідентично тим, що показано вище. Усі номерні таблички будуть приблизно однакового розміру (через метод обрізки будуть деякі неточності).

Якщо вам потрібні версії PNG без втрат будь-яких табличок, я надамо їх вам.

Оцінка балів

Виграє найкоротша програма в байтах.

Усі номерні знаки є скріншотами панелі пошуку на цьому сайті


8
Нагадайте мені проїхати через вашу швидкісну пастку. (Мій номерний знак містить букву О.)
Ніл

3
Так, назва цього питання досить неточна. Як щодо "OCR - британський номерний знак" ?
Лінн

3
@Neil Номерний знак у Великобританії має як O, так і 0, і вони виглядають однаково. Звичайно, є правила, які визначають правильне тлумачення, але це буде зовсім іншим завданням.
Рівень р. Св.

2
Дуже погано, що символи не фіксованої ширини. Це може створити дуже короткі можливості коду.
GuitarPicker

1
@YetiCGN Ваше бажання - це моя команда;)
Beta Decay

Відповіді:


11

C, 409 байт (і я настільки ж здивований, як і всі)

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

В якості введення приймає: ширину ( w) та висоту ( h) зображення, а потім упаковані дані RGB як масив chars ( d). Всі інші параметри функції - це змінні декларації у маскуванні. Ігнорує все, крім зеленого каналу, і застосовує поріг 32 як початковий прохід.

В основному такий же, як і метод @ DavidC, за винятком цього, перевіряється, чи заповнено щонайменше 35% кожного зразка. Сподіваємось, це робить більш надійним масштабування змін, але хто знає.

Я використовував метод грубої сили, щоб з'ясувати, який розмір і відсоток покриття використовувати для найкращої надійності (тобто найменші випадки одного персонажа, що мають багато інтерпретацій). Виявилося, що краща сітка 4x5 з 35% покриттям. Потім я використав другий метод грубої сили, щоб обчислити найкраще розташування бітів та значення модуля, щоб упакувати дані символів у коротку рядок - низький біт у верхньому лівому куті, збільшуючись у x, а y, остаточне значення% 101 повернуто найкраще, даючи цю таблицю пошуку:

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

Віднімання 7 означає, що початкові можуть бути видалені, а останні 2 можна видалити без зайвих робіт. Це видалення означає, що певні недійсні введення можуть спричинити неправильне зчитування пам’яті, тому воно може сегментуватися за певними зображеннями.

Використання:

Щоб увімкнути зображення, я написав обгортку за допомогою libpng. Також виявляється, що, незважаючи на ім’я файлу, зображення у питанні є насправді jpegs (!), Тому вам потрібно буде вручну експортувати їх як 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;
}

Зламатися

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 за побиття Python і Mathemetica з безконтрольним C . Oooollllld школа, йо.
Роберт Фрейзер

+1 за перемогу з C, начебто, ніколи не думав, що це може статися, так
HyperNeutrino

12

Mathematica 1170 1270 1096 1059 650 528 570 551 525 498 байт

Остання версія економить 27 байт, не вимагаючи, щоб табличка була "оброблена" перед її розбором. Передостання версія зберегла 26 байт, використавши лише 10 початкових 24 зразкових пунктів.

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 байти зберегли за допомогою ідеї LegionMammal978 упакувати довгий список базових 10 чисел як єдине, базове 36 число. Він підключив ще 20 байт від остаточного коду.

Перехід від 528 до 570 байт був обумовлений додатковим кодом для того, щоб порядок повернених літер відповідав порядку букв на номерному знаку. Центроїд для кожної літери містить координату x, яка розкриває відносні положення літер вздовж x.


Кодекс без вогню

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

Огляд

Основна ідея полягає у тому, щоб перевірити, чи відповідає систематизована вибірка пікселів із вхідного зображення пікселям з того самого місця на бонафідних зображеннях. Значна частина коду складається з бітових підписів для кожного символу,

На діаграмі показані пікселі, відібрані з літер "J", "P", "Q" та "R".

jpqr

Значення пікселів можуть бути представлені у вигляді матриць. Темні, сміливі 1відповідають чорним клітинам. В 0«S відповідають білим клітинам.

jjjj

Це правила заміни дешифрування для JPQ R.

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

Потрібно зрозуміти, чому правило для "0" таке:

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

і, таким чином, відрізняється від літери "Q".


Далі показано 10 пунктів, які використовуються в остаточній версії. Цих точок достатньо для ідентифікації всіх персонажів.

зменшено


Які функції виконують

plateCrop[img]знімає рамку і лівий край з тарілки, робить фон білим. Мені вдалося усунути цю функцію з остаточної версії, вибравши компоненти зображення, можливі літери висотою від 100 до 120 пікселів.

platecrop


isolateLetters[img] видаляє окремі літери з обрізаного зображення.

Ми можемо відобразити, як це працює, показавши, куди plateCropйде обрізане зображення, вихід з якого є вхідним isolateLetters. Вихід - це список окремих символів.

букви


Coordinates- це 24 рівномірно розподілених положення для перевірки кольору пікселів. Координати відповідають тим, на першому малюнку.

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 перетворює пікселі у двійкові.

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

codesє підписом для кожного символу. Десяткові значення - це абревіатури двійкового коду для чорних (0) та білих (1) комірок. У версії для гольфу використовується база 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"}};

(* decryptRulesпризначені для заміни підписів відповідним символом *)

decryptRules=Rule@@@codes;

f це функція, яка знімає номерний знак та повертає лист.

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

тарілки

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


Гольф

Код скорочують за допомогою одиничного десяткового числа для представлення всіх 24 біт (білого або чорного) для кожного символу. Наприклад, буква «J» використовує наступні правила заміни: 1118623 -> "J".

1118623 відповідає

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}

який можна перепакувати як

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, 1}, {1, 0, 0, 1} , {1, 1, 1, 1}}

що є просто матрицею для "J", яку ми бачили вище.

%//MatrixForm

матриця

Ще одна економія - це представлення алфавіту, "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ"а не списку літер.

Нарешті, всі функції з довгої версії, за винятком h, були інтегровані у функцію, fа не визначені окремо.


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 Здається, що SE зіпсував це; спробуйте замінити {1118623, 2518818, ..., 16645599}з цим .
LegionMammal978

@ LegionMammal978, ваша пропозиція призвела до скорочення коду на понад 100 байт. Тепер я краще розумію, як Mathematica обробляє основи.
DavidC

@DavidC Крім того, здається, що якийсь пробіл прокрався до вашого коду для гольфу, і я рахую 571 байт без нього. Крім того, деякі функції можуть бути перетворені у форму інфікування. x[[All,2,1]]можна замінити на x[[;;,2,1]]. Flatten[x,1]еквівалентний Join@@xі Flatten[#,1]&/@xеквівалентний Join@@@x. Є кілька інших незначних оптимізацій, які можна зробити. 551-байтний код після цих гольфів.
LegionMammal978

Приємні поради та уважне читання. Спасибі.
DavidC

Чи намагалися ви мінімізувати кількість точок відбору проб, переміщуючи їх?
Спарр

4

C #, 1040 1027 байт

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

Безголівки:

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

В основному я знайшов деякі конкретні орієнтири, щоб перевірити жовтий / чорний, щоб визначити особу кожного символу.


Ви впевнені, що немає зображень, що надаються, і що вони розпізнають номерні знаки, де символи, наприклад, зміщені на 10 пікселів?
YetiCGN

@YetiCGN він повинен розпізнавати його до тих пір, поки розмір однаковий, і вони знаходяться в одному вертикальному положенні. Я спробував з усіма поданими прикладами, і це працює; будь ласка, повідомте мене, якщо ви знайдете його там, де його немає
Нік Мертін

Я не хочу встановлювати Visual Studio тільки для цього, але ви можете спробувати i.imgur.com/i8jkCJu.png, який має трохи менший розмір. Я думаю, що можна припустити, що всі матеріали будуть образами саме з цього веб-сайту. Спочатку мій коментар був скоріше "як, якщо це справжнє сканування пластини?" / "що робити, якщо хтось інший перемістив усіх символів вертикально на 10 пікселів, щоб зробити табличку?"
YetiCGN

@YetiCGN вам не потрібен VisualStudio для компіляції, простоcsc.exe main.cs /r:System.Drawing.dll
VisualMelon

2

PHP - 1741 1674 1143 байт

Спочатку вона була створена шляхом вивчення профілів персонажів з перших кількох прикладів, які потім узагальнили кожен символ на шість чисел. Я вибрав шість, тому що в мене було п'ять, і це не так добре, як хотілося б, але шість, здається, працюють набагато краще. Значна частина оптимізації передбачає стиснення цих профілів на менші та менші кількості байтів.

Перший і другий профіль *lhdfdnі |nnmmkkнасправді блакитна крапля з «ГБ» в нижній частині *і правої межі |, які ми ігноруємо. Безпечніше включати їх, щоб крап і права межа мали щось протилежне.

Якщо обробляти будь-який формат зображення, будь-яке розумне масштабування за умови, що співвідношення сторін не сильно змінюється, будь-який темний світлий колір і навіть шум і затінення!

Для цього потрібна межа, принаймні вгорі і внизу, це частина профілю.

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

Збережіть як ocr.php, а потім запустіть з командного рядка:

$ 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

Для тих, хто цікавиться, ось код навчання. Збережіть як learn.phpі запустіть з командного рядка, ніяких аргументів.

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

?>

У висновку слід включити пробіли
Beta Decay

3
Це не в специфікаціях у розділі Далі наведено всі символи, які ваша програма повинна розпізнати , лише символи AH, JN, PZ та 0-9. Немає згадки про пробіли.

О, гаразд, тобі все добре
Beta Decay

"Перший і другий профіль [...] - це насправді блакитна крапка з" ГБ "внизу і права межа, яку ми ігноруємо". Тоді чому ви включили їх у код, особливо якщо ключ масиву із порожнім рядком перезаписаний? Плюс: дозволено використовувати короткий відкритий синтаксис для гольфу з кодом! :-)
YetiCGN

@YetiCGN - якщо їх немає, код спробує співставити їх із чимось іншим! Я не розумів, що їх перезаписали, пощастило, що код все ще працював. Перегляд. Можливо, ви зможете адаптувати деякі мої зміни до вашої відповіді.

0

PHP, 971 970 байт

Нічиї в великій мірі на Yimin Rong «s відповідь , який може бути серйозно golfed вниз, особливо індекси масиву, і покласти в Phar зі стисненням Gzip.

Завантажте phar

Це моя покращена базова версія на 1557 1535 байт, збережена просто під назвою "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;}

Покращення:

1-й етап

  • Індекси числового масиву вилучені та упорядковані масив, рядкові індекси як неявні константи

2-й етап

  • Замінено intvalна ~~(економить 8 байт, два випадки)
  • ініціалізація для циклу видаляється там, де це не потрібно
  • file_get_contents($u)замінено на join('',file($u))(економить 5 байт)
  • та кілька інших

На жаль, усі поліпшення на другому етапі перекладаються лише на 1 байт менш gzipped код. :-D

І цей код був використаний для створення 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();');

Перевірте зображення з php ocr.phar http://i.imgur.com/i8jkCJu.pngбудь-якого іншого тесту чи будь-якого іншого.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.