ナンバープレートゴルフ:表彰


20

参照:解析

前書き

あなたは、スピードカメラをプログラミングしている政府のプログラミングチームに取り組んでいます。ただし、速度計算機をプログラムした人々のグループはスペースを取りすぎているため、ナンバープレート認識ソフトウェアをできるだけ小さくする必要があります。

チャレンジ

ナンバープレートの画像を指定すると、プレート上のテキストを返します。

ナンバープレート

以下は、プログラムが認識しなければならないすべての文字です。

ABCDEFG

H1JKLMN0

PQRSTUVW

XYZ01234

56789

注意

イギリスのナンバープレートでは、I(i)と1(1)の文字は同じであり、O(o)と0(ゼロ)の文字は同じです。そのため、常に文字が数字であると想定してください。すなわち、次のナンバープレートは10(1つのゼロ)です。

C0D3 GLF

B3T4 DCY

M1NUS 15

YET1CGN

その他の規則

インターネットアクセスおよびOCRライブラリと機能は許可されていません。

ナンバープレートは常に上に示したものと同じに見えます。すべてのナンバープレートはほぼ同じサイズになります(切り取り方法により多少の誤差があります)。

ナンバープレートのロスレスPNGバージョンが必要な場合は、それらを提供します。

得点

バイト単位の最短プログラムが優先されます。

すべてのナンバープレートは、このサイトの検索バーのスクリーンショットです


8
スピードトラップを通過するように通知します。(私のナンバープレートには文字Oが含まれています。)
ニール

3
はい、この質問のタイトルはかなり不正確です。どの程度「OCR英国のナンバープレート」
リン

3
@Neil My UKナンバープレートにはOと0の両方があり、同じように見えます。どちらが正しい解釈であるかを決定するルールはもちろんありますが、それはまったく別の課題です。
レベルリバーセント

2
文字が固定幅ではないのは残念です。これにより、非常に短いコードの可能性が生まれます。
GuitarPicker

1
@YetiCGNあなたの願いは私の命令です;)
ベータ崩壊

回答:


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)に続いて、charsの配列としてパックされたRGBデータ()が続きdます。他のすべての関数パラメーターは、変装した変数宣言です。緑のチャネルを除くすべてを無視し、初期パスとして32のしきい値を適用します。

@DavidCのメソッドとほぼ同じですが、これは各サンプルボックスの少なくとも35%が満たされていることを確認します。願わくば、それによって変更のスケーリングがより堅牢になることを願っていますが、誰が知っているでしょう。

ブルートフォース法を使用して、どのリサンプリングサイズとカバレッジパーセントを使用して最高の信頼性(つまり、1つの文字が複数の解釈を持つ最も少ないケース)を使用するかを見つけました。カバレッジが35%の4x5グリッドが最適であることが判明しました。次に、2番目のブルートフォース法を使用して最適なビット配置とモジュロ値を計算し、文字データを短い文字列にパックしました。このルックアップテーブルを指定すると、最高の出力が得られます。

-------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を使用してラッパーを作成しました。また、ファイル名にもかかわらず、問題の画像は実際にはjpeg(!)であるため、最初に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]
      );
    }
  }
}

PythonとMathemeticaをCで打ち負かした+1 。ウーオールドスクール、よ。
ロバートフレイザー

WINNING for Cの+1、それが起こるとは思わなかった、
ハァー-HyperNeutrino

12

Mathematica 1170 1270 1096 1059 650 528 570 551 525 498バイト

最新バージョンでは、解析前にプレートを「トリミング」する必要がないため、27バイトが節約されます。最後から2番目のバージョンでは、元の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、1、9、15}-> "J"、
{15、9、15、14、8、8}-> "P"、
{15、9、9、9、9、15、15 }-> "Q"、
{15、9、15、14、10、11}-> "R"

「0」のルールが次の理由であることが理解できるはずです。

{15、9、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」}


ゴルフ

単一の10進数を使用して各文字の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、0、1}、{1、0、0、1} 、{1、1、1、1}}

これは、上で見た「J」の単純なマトリックスです。

%//MatrixForm

マトリックス

もう1つの節約は、アルファベット"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また、あたかもゴルフスペースのコードに空白が潜り込んでいるかのように見えます。さらに、一部の関数は中置形式に変換できます。x[[All,2,1]]に置き換えることができますx[[;;,2,1]]Flatten[x,1]はと同等でJoin@@xFlatten[#,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は、サイズが同じで、同じ垂直位置にある限り認識します。提供されたすべての例を試してみましたが、動作します。見つからない場合はお知らせください
Nick Mertin

このためだけにVisual Studioをインストールしたくありませんが、サイズが少し小さいi.imgur.com/i8jkCJu.pngを試すことができます。すべての提出物がその特定のウェブサイトからの画像であると仮定するのは安全だと思います。当初、私のコメントは「もしそれが本当のプレートスキャンだとしたら?」という行に沿っていました。/「他の誰かがプレートを作るためにすべての文字を10ピクセルだけ垂直にシフトしたらどうなるでしょうか?」
YetiCGN

@YetiCGN Visual Studioをコンパイルする必要はないはずです 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;
}

?>

あなたは出力にスペースを含める必要があります
ベータ崩壊

3
これは、以下の仕様には含まれていません。以下は、プログラムが認識しなければならないすべての文字であり、文字AH、JN、PZ、および0-9のみです。スペースの言及はありません。

ああ、大丈夫、あなたはその後、大丈夫です
ベータ崩壊

「最初と2番目のプロファイル[...]は、実際には下部に「GB」が付いた青い塊で、右の境界線は無視しています。」次に、特に空の文字列を持つ配列キーが上書きされる場合、なぜコードにそれらを含めたのですか?プラス:コードゴルフに短いオープン構文を使用することができます!:-)
YetiCGN

@YetiCGN-そうでない場合、コードはそれらを他の何かに一致させようとします!それらが上書きされていることに気付いていませんでしたが、幸運なことにコードはまだ機能していました。改訂。あなたは私の変更のいくつかをあなたの答えに適応させることができるかもしれません。

0

PHP、971 970バイト

Yimin Rong答えを大いに利用します。これは、特に配列インデックスを真剣に調べ、gzip圧縮でPharに入れることができます。

pharをダウンロードする

これは、ファイル名「o」の下に保存された1557 1535バイトの改良された基本バージョンです。

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

改善点:

第一段階

  • 数値配列インデックスが削除され、配列が再配列されました。暗黙的な定数としての文字列インデックス

第2段階

  • 交換しintval~~(セーブ8バイト二出現箇所)
  • 不要な場合にforループの初期化を削除
  • file_get_contents($u) 交換された join('',file($u))(5バイトを保存)
  • その他いくつか

残念ながら、すべての第2段階の改善は、1バイト少ないgzip圧縮されたコードに変換されます。:-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またはテストケース画像の他。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.