번호판 골프 : 인식


20

참조 : 파싱

소개

스피드 카메라를 프로그래밍하고있는 정부 프로그래밍 팀에서 일하고 있습니다. 그러나 속도 계산기를 프로그래밍 한 사람들이 너무 많은 공간을 차지했기 때문에 번호판 인식 소프트웨어를 가능한 작게 만들어야합니다.

도전

번호판 이미지가 표시되면 번호판의 텍스트를 반환하십시오.

번호판

다음은 프로그램이 인식해야하는 모든 문자입니다.

ABCDEFG

H1JKLMN0

PQRSTUVW

XYZ01234

56789

노트

영국 번호판에서 I (i)와 1 (1)의 문자는 동일하고 O (o)와 0 (0)의 문자는 동일합니다. 따라서 항상 문자를 숫자라고 가정하십시오. 즉, 다음 번호판은 10 (0)입니다.

C0D3 GLF

B3T4 DCY

M1NUS 15

YET1CGN

다른 규칙

인터넷 액세스 및 OCR 라이브러리 및 기능이 허용되지 않습니다.

번호판은 항상 위에 표시된 것과 동일하게 보입니다. 모든 번호판의 크기는 대략 동일합니다 (자르기 방법으로 인해 일부 부정확 한 점이있을 수 있습니다).

숫자판의 무손실 PNG 버전이 필요한 경우 해당 번호판을 제공합니다.

채점

바이트 단위의 최단 프로그램이 이깁니다.

모든 번호판은 이 사이트 의 검색 창 스크린 샷입니다.


8
스피드 트랩을 통과하도록 상기시켜주세요. (내 번호판에는 문자 O가 포함되어 있습니다.)
Neil

3
예,이 질문의 제목은 매우 정확하지 않습니다. 방법에 대해 "OCR 영국 번호판" ?
Lynn

3
@Neil My UK 번호판은 O와 0을 모두 가지며 동일하게 보입니다. 물론 어떤 것이 올바른 해석인지를 결정하는 규칙이 있지만, 그것은 완전히 다른 도전이 될 것입니다.
Level River 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]);}}}

이미지 의 너비 ( w)와 높이 ( h) 를 입력 한 다음 압축 된 RGB 데이터를 chars ( d) 배열로 입력합니다 . 다른 모든 함수 매개 변수는 변장 된 변수 선언입니다. 녹색 채널을 제외한 모든 것을 무시하고 임계 값 32를 초기 패스로 적용합니다.

각 샘플 상자의 35 % 이상이 채워져 있는지 확인한다는 점을 제외하면 @DavidC의 방법과 거의 동일합니다. 잘하면 변경을 확장하는 것이 더 강력 해 지지만 누가 알겠습니까?

나는 최고의 신뢰성을 위해 어떤 리샘플링 크기와 커버리지 퍼센트를 찾기 위해 무차별 대입법을 사용했습니다 (즉, 여러 해석을 가진 한 문자의 경우는 가장 적음). 커버리지가 35 % 인 4x5 그리드가 가장 좋습니다. 그런 다음 두 번째 무차별 대입법을 사용하여 최상의 비트 배열과 모듈로 값을 계산하여 문자 데이터를 짧은 문자열로 채 웁니다. 왼쪽 상단의 하위 비트는 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 개를 추가 작업없이 제거 할 수 있습니다. 이 제거는 특정 유효하지 않은 입력으로 인해 유효하지 않은 메모리 읽기가 발생할 수 있으므로 특정 이미지에서 segfault가 발생할 수 있습니다.

용법:

이미지를 가져 오기 위해 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]
      );
    }
  }
}

괴물과 파이썬과 Mathemetica을 치고 +1 C를 . Oooollllld 학교, 요.
Robert Fraser

C와 함께 WINNING에 +1, 그런 일은 일어날 수 있다고 생각하지 않았습니다.
HyperNeutrino

12

Mathematica 1170 1270 1096 1059650528570551525 498 바이트

최신 버전은 플레이트를 구문 분석하기 전에 "트림"하지 않아도되므로 27 바이트를 절약합니다. 두 번째 버전은 원래 24 개의 샘플 포인트 중 10 개만 사용하여 26 바이트를 절약했습니다.

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

LegionMammal978의 기본 10 개 숫자 목록을 단일 기본 36 개 숫자로 묶는 아이디어를 통해 122 바이트가 절약되었습니다. 그는 최종 코드에서 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 픽셀 사이의 가능한 문자를 선택하여 최종 버전에서이 기능을 제거 할 수있었습니다.

판금


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각 캐릭터의 서명입니다. 10 진수 값은 검정 (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 비트 (흰색 또는 검은 색)를 모두 나타내는 단일 10 진수를 사용하여 코드를 줄입니다. 예를 들어, 문자 "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, 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

샘플링 포인트 수를 최소화하여 샘플링 포인트 수를 최소화하려고 했습니까?
Sparr

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 크기가 동일하고 수직 위치에있는 한 인식해야합니다. 제공된 모든 예제를 시도했지만 작동합니다. 당신이 그것이없는 곳을 찾으면 알려주십시오
Nick Mertin

나는 이것을 위해 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 바이트

처음 몇 가지 예에서 캐릭터의 프로파일을 학습하여 처음으로 설정 한 후 각 캐릭터를 6 개의 숫자로 요약했습니다. 원래 5 개를 사용했기 때문에 6 개를 선택했는데 원하는만큼 작동하지 않았지만 6 개가 훨씬 잘 작동하는 것 같습니다. 대부분의 최적화에는 이러한 프로파일을 더 작은 바이트 수로 압축하는 것이 포함됩니다.

첫 번째 및 두 번째 프로필 *lhdfdn이며 |nnmmkk실제로 하단에 "GB"가 *있고 오른쪽 테두리 가있는 파란색 얼룩입니다.| 우리가 무시하고 있습니다. 얼룩과 오른쪽 테두리가 일치하도록 포함하는 것이 더 안전합니다.

이미지 형식을 처리해야하는 경우, 가로 세로 비율이 너무 많이 변하지 않고 밝은 색상의 어두운 부분과 약간의 노이즈 및 음영 처리가 제공되는 경우 적절한 배율 조정이 가능합니다!

프로파일의 일부인 적어도 상단과 하단에 테두리가 필요합니다.

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

"첫 번째와 두 번째 프로필 [...]은 실제로 아래쪽에"GB "가있는 파란색 얼룩이며 오른쪽 경계이며 무시하고 있습니다." 그렇다면 왜 빈 문자열을 가진 배열 키를 덮어 쓰는 경우에 왜 코드에 포함 시켰습니까? 플러스 : 코드 골프에 짧은 오픈 구문을 사용할 수 있습니다! :-)
YetiCGN

@YetiCGN-그렇지 않은 경우 코드는 다른 코드와 일치 시키려고 시도합니다! 코드가 덮어 쓰인 것을 알지 못했지만 코드가 여전히 작동했습니다. 수정. 내 변경 사항 중 일부를 답변에 적용 할 수 있습니다.

0

PHP, 971 바이트

크게 무승부 Yimin Rong답변에 인덱스는 특히 인덱스를 심각하게 골라 내고 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 바이트, 두 회 발생)
  • 불필요한 경우 for 루프 초기화 제거
  • file_get_contents($u) 로 교체 join('',file($u)) (5 바이트 절약)
  • 그리고 다른 몇

불행히도, 모든 2 단계 개선은 1 바이트 적은 gzipped 코드로만 변환됩니다. :-디

그리고이 코드는 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케이스 이미지로 테스트하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.