Номерной знак Гольф: Признание

20

Смотри также: Разбор

Вступление

Вы работаете в правительственной команде по программированию, которая программирует камеры контроля скорости. Тем не менее, группа людей, которые запрограммировали калькулятор скорости, заняли слишком много места, поэтому вы должны сделать программное обеспечение для распознавания номерных знаков как можно меньше.

Вызов

Учитывая изображение номерного знака, верните текст на табличке.

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

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

АБВГДЕЖ

H1JKLMN0

PQRSTUVW

XYZ01234

56789

Заметка

На британских номерных знаках символы для I (i) и 1 (один) одинаковы, а символы для O (o) и 0 (ноль) одинаковы. По этой причине всегда предполагайте, что символы - это числа. Т.е. следующий номерной знак равен 10 (один ноль):

Примеры

C0D3 GLF

B3T4 DCY

M1NUS 15

YET1CGN

Другие правила

Доступ к Интернету и библиотеки OCR запрещены.

Таблички с номерами всегда будут выглядеть так, как показано выше. Все номерные знаки будут примерно одинакового размера (будут некоторые неточности из-за метода обрезки).

Если вам требуются версии PNG без каких-либо номерных знаков, я предоставлю их вам.

счет

Самая короткая программа в байтах побеждает.

Все номерные знаки являются скриншотами панели поиска на этом сайте.

Бета распад
источник
8
Напомни мне проехать через твою скоростную ловушку. (Мой номерной знак содержит букву О.)
Нейл
3
Да, название этого вопроса довольно неточно. Как насчет "OCR британский номерной знак" ?
Линн
3
@Neil У моего британского номерного знака есть и O, и 0, и они выглядят одинаково. Конечно, существуют правила, которые определяют, какая интерпретация является правильной, но это будет совсем другая задача.
Уровень Река St
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]);}}}

Принимает в качестве входных данных: width ( w) и height ( 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 (!), Поэтому вам нужно будет сначала вручную экспортировать их как 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;
}

Сломать

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 . Ооооооооочень школа, йо
Роберт Фрейзер
+1 за ПОБЕДУ с C, вроде бы, никогда не думал, что это может произойти,
ага
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соответствует белым клеткам.

JJJJ

Это правила замены расшифровки для 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»

Должно быть возможно понять, почему правило для «0» таково:

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

и, следовательно, отличается от буквы «Q».


Ниже показаны 10 баллов, использованных в финальной версии. Этих точек достаточно для идентификации всех персонажей.

уменьшенный


Что делают функции

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

platecrop


isolateLetters[img] удаляет отдельные буквы из обрезанного изображения.

Мы можем показать, как это работает, показывая, куда обрезанное изображение выводится в plateCropкачестве входных данных isolateLetters. Вывод представляет собой список отдельных символов.

письма


Coordinates24 равномерно распределенных положения для проверки цвета пикселя. Координаты соответствуют указанным на первом рисунке.

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


Golfed

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

1118623 соответствует

IntegerDigits[1118623 , 2, 24]

{0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 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, 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
источник
@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;}}}

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

В основном я нашел некоторые конкретные контрольные точки, чтобы проверить желтый / черный, чтобы определить личность каждого персонажа.

Ник мертин
источник
Вы уверены, что не существует наложения на предоставленные изображения и что он распознает номерные знаки, где символы, например, смещены на 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. Нет упоминания о пробелах.
О, хорошо, тогда у тебя все в порядке
бета-распад
«Первый и второй профиль [...] на самом деле представляют собой синий шарик с« ГБ »внизу и правой границей, который мы игнорируем». Тогда почему вы включили их в код, особенно если ключ массива с пустой строкой перезаписан? Плюс: в коде гольф разрешено использовать короткий открытый синтаксис! :-)
YetiCGN
@YetiCGN - если нет, код попытается сопоставить их с чем-то другим! Я не знал, что они были перезаписаны, к счастью, код все еще работал. Пересмотр. Возможно, вы сможете адаптировать некоторые из моих изменений к своему ответу.
0

PHP, 971 970 байт

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

Скачать фар

Это моя улучшенная базовая версия на 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 байтов, два раза)
  • инициализация цикла for удалена там, где она не нужна
  • file_get_contents($u) заменено на join('',file($u)) (сохраняет 5 байтов)
  • и несколько других

К сожалению, все улучшения второго этапа переводятся только в 1 байт сжатого кода. :-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или любого другого изображения тестового примера.

YetiCGN
источник