Number Plate Golf: Pengakuan

20

Lihat juga: Parsing

pengantar

Anda sedang bekerja di tim pemrograman pemerintah, yang telah memprogram kamera kecepatan. Namun, sekelompok orang yang telah memprogram kalkulator kecepatan telah mengambil terlalu banyak ruang, jadi Anda harus membuat perangkat lunak pengenalan plat nomor sekecil mungkin.

Tantangan

Diberi gambar pelat nomor, kembalikan teks di piring.

Plat nomor

Berikut ini adalah semua karakter yang harus dikenali oleh program Anda:

ABCDEFG

H1JKLMN0

PQRSTUVW

XYZ01234

56789

Catatan

Pada pelat nomor Inggris, karakter untuk I (i) dan 1 (satu) adalah sama dan karakter untuk O (o) dan 0 (nol) adalah sama. Karena itu, selalu anggap karakter adalah angka. Yaitu plat nomor berikut adalah 10 (satu nol):

Contohnya

C0D3 GLF

B3T4 DCY

M1NUS 15

YET1CGN

Aturan lainnya

Akses Internet dan pustaka dan fungsi OCR tidak diizinkan.

Pelat nomor akan selalu terlihat identik dengan yang ditunjukkan di atas. Semua pelat nomor akan berukuran hampir sama (akan ada beberapa ketidakakuratan karena metode tanam).

Jika Anda membutuhkan versi PNG lossless pelat nomor apa pun, saya akan menyediakannya untuk Anda.

Mencetak gol

Program terpendek dalam byte menang.

Semua pelat nomor adalah tangkapan layar bilah pencarian di situs ini

Peluruhan Beta
sumber
8
Ingatkan saya untuk mengemudi melalui perangkap kecepatan Anda. (Plat nomor saya berisi huruf O.)
Neil
3
Ya, judul pertanyaan ini cukup tidak akurat. Bagaimana dengan "OCR plat Inggris" ?
Lynn
3
@Neil Plat nomor UK saya memiliki O dan 0 dan terlihat identik. Tentu saja ada aturan untuk menentukan interpretasi mana yang benar, tetapi itu akan menjadi tantangan lain.
Level River St
2
Sayang sekali karakternya tidak lebar tetap. Itu bisa membuat beberapa kemungkinan kode sangat pendek.
GuitarPicker
1
@YetiCGN Keinginan Anda adalah perintah saya;)
Beta Decay

Jawaban:

11

C, 409 byte (dan saya sama terkejutnya dengan siapa pun)

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

Diambil sebagai input: lebar ( w) dan tinggi ( h) dari gambar, diikuti oleh data RGB yang dikemas sebagai larik chars ( d). Semua parameter fungsi lainnya adalah deklarasi variabel yang menyamar. Abaikan semuanya kecuali saluran hijau, dan terapkan ambang batas 32 sebagai lintasan awal.

Sebagian besar sama dengan metode @ DavidC, kecuali ini memeriksa bahwa setidaknya 35% dari setiap kotak sampel diisi. Semoga itu membuatnya lebih kuat untuk mengukur perubahan, tetapi siapa yang tahu.

Saya menggunakan metode brute-force untuk mengetahui ukuran resampling dan persen cakupan yang digunakan untuk keandalan terbaik (yaitu kasus paling sedikit dari satu karakter yang memiliki beberapa interpretasi). Ternyata kisi 4x5 dengan cakupan 35% adalah yang terbaik. Saya kemudian menggunakan metode brute-force kedua untuk menghitung pengaturan bit terbaik dan nilai modulo untuk mengemas data karakter menjadi string pendek - bit rendah di kiri atas, meningkat dalam x lalu y, dengan nilai akhir% 101 berubah yang terbaik, berikan tabel pencarian ini:

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

Mengurangi 7 berarti inisial dapat dihapus, dan 2 yang terakhir dapat dihapus tanpa kerja ekstra. Penghapusan ini berarti bahwa input tertentu yang tidak valid dapat menyebabkan memori yang tidak valid terbaca, sehingga dapat memisahkan gambar tertentu.

Pemakaian:

Untuk mendapatkan gambar ke dalamnya, saya menulis pembungkus menggunakan libpng. Juga ternyata meskipun memiliki nama file, gambar dalam pertanyaan sebenarnya adalah jpegs (!), Jadi Anda harus mengekspornya secara manual sebagai pngs terlebih dahulu.

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

Kerusakan

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]
      );
    }
  }
}
Dave
sumber
1 untuk mengalahkan Python dan Mathemetica dengan panik C . Sekolah yang bagus, yo.
Robert Fraser
+1 untuk WINNING dengan C, seperti, tidak pernah berpikir itu bisa terjadi, ya
HyperNeutrino
12

Mathematica 1170 1270 1096 1059 650 528 570 551 525 498 byte

Versi terbaru menghemat 27 byte dengan tidak mengharuskan plat "dipangkas" sebelum diuraikan. Versi kedua terakhir menyimpan 26 byte dengan hanya menggunakan 10 dari 24 titik sampel asli.

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 byte disimpan melalui ide LegionMammal978 tentang pengemasan daftar panjang nomor basis 10 sebagai nomor tunggal, nomor 36. Dia mengupas 20 byte lagi dari kode akhir.

Lompatan dari 528 ke 570 byte adalah karena kode tambahan untuk memastikan bahwa urutan surat-surat yang dikembalikan sesuai dengan urutan surat-surat di plat nomor. Centroid untuk setiap huruf berisi koordinat x, yang mengungkapkan posisi relatif huruf sepanjang x.


Kode Tidak Terkunci

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

Ikhtisar

Ide dasarnya adalah untuk memeriksa apakah pengambilan sampel piksel secara sistematis dari gambar input cocok dengan piksel dari lokasi yang sama pada gambar bonafide. Sebagian besar kode terdiri dari tanda tangan bit untuk setiap karakter,

Diagram menunjukkan piksel yang diambil dari huruf "J", "P", "Q", dan "R".

jpqr

Nilai piksel dapat direpresentasikan sebagai matriks. Gelap, tebal 1berhubungan dengan sel hitam. Ini 0sesuai dengan sel putih.

jjjj

Ini adalah aturan penggantian dekripsi untuk JPQ R.

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

Seharusnya dimungkinkan untuk memahami mengapa aturan untuk "0" adalah:

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

dan dengan demikian dapat dibedakan dari huruf "Q".


Berikut ini menunjukkan 10 poin yang digunakan dalam versi final. Poin-poin ini cukup untuk mengidentifikasi semua karakter.

berkurang


Apa fungsinya

plateCrop[img]menghilangkan bingkai dan tepi kiri dari piring, membuat latar belakang putih. Saya dapat menghilangkan fungsi ini dari versi final dengan memilih komponen gambar, kemungkinan huruf yang tingginya antara 100 dan 120 piksel.

platecrop


isolateLetters[img] menghapus setiap huruf dari gambar yang dipangkas.

Kita dapat menampilkan cara kerjanya dengan menunjukkan di mana gambar yang dipangkas, output dari plateCropmasuk sebagai input untuk isolateLetters. Outputnya adalah daftar karakter individu.

surat


Coordinatesadalah 24 posisi yang didistribusikan secara merata untuk memeriksa warna piksel. Koordinat sesuai dengan yang ada di gambar pertama.

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 mengubah piksel menjadi biner.

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

codesadalah tanda tangan untuk setiap karakter. Nilai desimal adalah singkatan dari kode biner untuk sel hitam (0) dan Putih (1). Dalam versi golf, base 36 digunakan.

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

(* decryptRulesuntuk mengganti tanda tangan dengan karakter masing-masing *)

decryptRules=Rule@@@codes;

f adalah fungsi yang mengambil gambar plat dan mengembalikan surat.

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

piring

{"A", "B", "C", "D", "E", "F", "G"}
{"H", "1", "J", "K", "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", "8", "9"}


Golf

Kode ini disingkat dengan menggunakan angka desimal tunggal untuk mewakili semua 24 bit (putih atau hitam) untuk setiap karakter. Misalnya, huruf "J" menggunakan aturan pengganti berikut: 1118623 -> "J".

1118623 berkorespondensi dengan

IntegerDigits[1118623 , 2, 24]

{0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1}

yang dapat dikemas ulang sebagai

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

yang hanya merupakan matriks untuk "J" yang kita lihat di atas.

%//MatrixForm

matriks

Penghematan lain datang dari mewakili alfabet "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ"bukan sebagai daftar huruf.

Akhirnya, semua fungsi dari versi panjang, kecuali h, diintegrasikan ke dalam fungsi fdaripada ditentukan secara terpisah.


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
sumber
@ DavidC Sepertinya SE mengacaukannya; coba ganti {1118623, 2518818, ..., 16645599}dengan ini .
LegionMammal978
@ LegionMammal978, saran Anda menyebabkan pemendekan kode lebih dari 100 byte. Saya sekarang mengerti lebih baik bagaimana Mathematica menangani basis.
DavidC
@ DavidvidC Juga, tampak seolah-olah beberapa spasi kosong menyelinap ke kode golf Anda, dan saya menghitung 571 byte tanpa itu. Selain itu, beberapa fungsi dapat dikonversi ke bentuk infix. x[[All,2,1]]dapat diganti dengan x[[;;,2,1]]. Flatten[x,1]sama dengan Join@@x, dan Flatten[#,1]&/@xsetara dengan Join@@@x. Ada beberapa optimasi kecil lainnya yang dapat dilakukan. Kode 551-byte setelah golf ini.
LegionMammal978
Kiat yang bagus dan bacaan yang cermat. Terima kasih.
DavidC
Apakah Anda mencoba meminimalkan jumlah titik pengambilan sampel dengan memindahkannya?
Sparr
4

C #, 1040 1027 byte

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

Tidak Terkumpul:

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

Pada dasarnya saya menemukan beberapa titik referensi khusus untuk memeriksa kuning / hitam untuk menentukan identitas masing-masing karakter.

Nick Mertin
sumber
Apakah Anda yakin tidak ada overfit pada gambar yang disediakan dan itu akan mengenali plat nomor di mana karakter misalnya digeser 10 piksel?
YetiCGN
@ YetiCGN harus mengenalinya asalkan ukurannya sama, dan mereka berada di posisi vertikal yang sama. Saya telah mencoba dengan semua contoh yang diberikan, dan itu berhasil; tolong beri tahu saya jika Anda menemukannya di tempat yang tidak tepat
Nick Mertin
Saya tidak ingin menginstal Visual Studio hanya untuk ini, tetapi Anda dapat mencoba i.imgur.com/i8jkCJu.png yang ukurannya sedikit lebih kecil. Saya pikir aman untuk mengasumsikan bahwa semua kiriman akan berupa gambar dari situs web tertentu. Awalnya komentar saya lebih mirip "bagaimana kalau itu benar-benar scan piring?" / "Bagaimana jika orang lain menggeser semua karakter secara vertikal dengan 10 piksel untuk membuat piring?"
YetiCGN
@YetiCGN Anda tidak perlu VisualStudio untuk dikompilasi, cukupcsc.exe main.cs /r:System.Drawing.dll
VisualMelon
2

PHP - 1741 1674 1143 byte

Pertama kali diatur dengan mempelajari profil karakter dari beberapa contoh pertama, yang kemudian merangkum setiap karakter menjadi enam angka. Saya memilih enam karena pada awalnya saya punya lima, dan itu tidak bekerja sebaik yang saya inginkan, tetapi enam tampaknya bekerja lebih baik. Sebagian besar optimasi melibatkan memeras profil-profil ini menjadi jumlah byte yang lebih kecil dan lebih kecil.

Profil pertama dan kedua *lhdfdndan |nnmmkksebenarnya adalah gumpalan biru dengan "GB" di bagian bawah *, dan batas kanan |, yang kita abaikan. Lebih aman untuk memasukkan mereka sehingga gumpalan dan perbatasan kanan memiliki sesuatu yang cocok.

Harus menangani format gambar apa pun, penskalaan beralasan apa pun asalkan rasio aspek tidak berubah terlalu banyak, warna gelap pada warna terang, dan bahkan sedikit noise dan bayangan!

Memang perlu perbatasan, setidaknya di bagian atas dan bawah, itu bagian dari profil.

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

Simpan sebagai ocr.php, lalu jalankan dari baris perintah:

$ 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

Bagi mereka yang tertarik, berikut adalah kode pembelajarannya. Simpan sebagai learn.phpdan jalankan dari baris perintah, tanpa argumen.

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

?>

sumber
Anda harus memasukkan spasi di output
Beta Decay
3
Itu tidak ada dalam spesifikasi di bawah . Berikut ini adalah semua karakter yang harus dikenali oleh program Anda , hanya karakter AH, JN, PZ, dan 0-9. Tidak disebutkan spasi.
Oh, baiklah, milikmu baik-baik saja
Beta Decay
"Profil pertama dan kedua [...] sebenarnya adalah gumpalan biru dengan" GB "di bagian bawah, dan perbatasan kanan, yang kita abaikan." Lalu mengapa Anda memasukkan mereka dalam kode, terutama jika kunci array dengan string kosong ditimpa? Plus: Diizinkan menggunakan sintaks terbuka pendek untuk kode golf! :-)
YetiCGN
@YetiCGN - jika tidak maka kode akan mencoba mencocokkannya dengan sesuatu yang lain! Saya tidak menyadari mereka ditimpa, beruntung kodenya masih berfungsi. Merevisi. Anda mungkin dapat menyesuaikan beberapa perubahan saya dengan jawaban Anda.
0

PHP, 971 970 byte

Menarik berat pada Yimin Rong 's jawaban , yang dapat serius golfed bawah, terutama indeks array, dan dimasukkan ke dalam Phar dengan kompresi gzip.

Unduh phar

Ini adalah versi dasar saya yang ditingkatkan pada 1557 1535 byte, disimpan hanya dengan nama file "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;}

Perbaikan:

Tahap 1

  • Indeks array numerik dihapus dan disusun ulang array, indeks string sebagai konstanta implisit

Tahap 2

  • Diganti intvaldengan ~~(menyimpan 8 byte, dua kejadian)
  • untuk inisialisasi loop dihapus jika tidak perlu
  • file_get_contents($u)diganti dengan join('',file($u))(menghemat 5 byte)
  • dan beberapa lainnya

Sayangnya, semua peningkatan tahap kedua hanya diterjemahkan menjadi 1 byte lebih sedikit kode gzip. :-D

Dan kode ini digunakan untuk membuat 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();');

Uji dengan php ocr.phar http://i.imgur.com/i8jkCJu.pngatau gambar kasus uji lainnya.

YetiCGN
sumber