태그 보관물: optical-char-recognition

optical-char-recognition

번호판 골프 : 인식 O (o)와 0 (0)의 문자는

참조 : 파싱

소개

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

도전

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

번호판

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

ABCDEFG

H1JKLMN0

PQRSTUVW

XYZ01234

56789

노트

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

C0D3 GLF

B3T4 DCY

M1NUS 15

YET1CGN

다른 규칙

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

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

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

채점

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

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



답변

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 % 인 4×5 그리드가 가장 좋습니다. 그런 다음 두 번째 무차별 대입법을 사용하여 최상의 비트 배열과 모듈로 값을 계산하여 문자 데이터를 짧은 문자열로 채 웁니다. 왼쪽 상단의 하위 비트는 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]
      );
    }
  }
}

답변

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”에서 샘플링 된 픽셀을 보여줍니다.

픽셀 값은 행렬로 표현 될 수 있습니다. 어둡고 대담한1 는 검은 세포에 해당합니다. 의 0백혈구에 해당합니다.

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

답변

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

기본적으로 각 문자의 신원을 확인하기 위해 노랑 / 검정을 확인하는 특정 참조 점이 있습니다.


답변

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

?>

답변

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케이스 이미지로 테스트하십시오.


답변