车牌号高尔夫:识别


20

另请参阅:解析

介绍

您正在与一个政府编程团队合作,他们一直在为测速相机编程。但是,为速度计算器编程的人占用了太多空间,因此您必须使车牌识别软件尽可能小。

挑战

给定车牌图像,返回车牌上的文字。

车牌号

以下是程序必须识别的所有字符:

ABCDEFG

H1JKLMN0

PQRSTUVW

XYZ01234

56789

注意

在英国车牌上,I(i)和1(一个)的字符相同,而O(o)和0(零)的字符相同。因此,始终假设字符是数字。即以下车牌号是10(一个零):

例子

C0D3 GLF

B3T4 DCY

M1NUS 15

YET1CGN

其他规定

禁止访问Internet和OCR库和功能。

车牌始终看起来与上面显示的相同。所有号牌的尺寸都大致相同(由于裁剪方法而有些不正确)。

如果您需要任何车牌的无损PNG版本,我们将为您提供。

计分

以字节为单位的最短程序获胜。

所有号码牌均为该站点搜索栏的屏幕截图


8
提醒我开车穿过您的速度陷阱。(我的车牌上包含字母O。)
Neil

3
是的,这个问题的标题很不准确。如何“OCR英国车牌”
林恩

3
@Neil我的英国号码牌同时具有O和0,它们看起来相同。当然,有规则来确定哪个是正确的解释,但这将是另一个挑战。
水平河圣

2
字符不是固定宽度太糟糕了。这可能会导致一些非常短的代码可能性。
GuitarPicker

1
@YetiCGN您的愿望就是我的命令;)
Beta Decay

Answers:


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用作初始通过。

基本上与@DavidC的方法相同,不同之处在于此方法检查是否至少填充了每个样本框的35%。希望这可以使扩展变更更可靠,但谁知道。

我使用了蛮力方法来找出要获得最佳可靠性(即,一个字符具有多种解释的最少情况)所要使用的重采样大小和覆盖百分比。事实证明,覆盖率为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个字母可以删除而无需任何额外的工作。删除意味着某些无效输入可能导致无效的内存读取,因此可能会对特定图像进行段错误。

用法:

为了获取图像,我使用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]
      );
    }
  }
}

+1,用C击败Python和Mathemetica 。Oooollldd学校,哟。
罗伯特·弗雷泽

+1,用C
赢得

12

Mathematica 1170 1270 1096 1059 650528570551525525字节

最新版本节省了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的对应于白色的细胞。

j

这些是JPQ R的解密替换规则。

{1,1,1,1,9,9,15}->“ J”,
{15,9,15,14,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是每个字符的签名。十进制值是黑色(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位(白色或黑色),可以缩短代码的长度。例如,字母“ 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另外,似乎有些空白隐藏在您的golf代码中,如果没有它,我计数为571个字节。此外,某些功能可以转换为中缀形式。x[[All,2,1]]可以替换为x[[;;,2,1]]Flatten[x,1]等同于Join@@x,并且Flatten[#,1]&/@x等同于Join@@@x。还有其他一些次要的优化可以完成。打完高尔夫球之后的551字节代码。
LegionMammal978

不错的提示和仔细阅读。谢谢。
DavidC,2016年

您是否试图通过移动采样点来减少采样点的数量?
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字节

它是通过从前几个示例中学习字符的轮廓来设置的,然后将每个字符概括为六个数字。我之所以选择六个,是因为我最初有五个,但它并没有达到我想要的效果,但是六个似乎要好得多。大多数优化涉及将这些配置文件压缩为越来越小的字节数。

第一和第二轮廓*lhdfdn|nnmmkk实际上是蓝色的斑点与“国标”在底部*,和右边界|,这是我们正在忽略。包含它们是比较安全的,以便Blob和右边框可以匹配。

应该可以处理任何图像格式,任何合理的缩放比例,前提是纵横比的变化不会太大,任何浅色的阴影,甚至一点噪点和阴影!

它确实需要轮廓(至少在顶部和底部),这是轮廓的一部分。

<?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,2016年

3
这不是规范中的内容。以下是程序必须识别的所有字符,仅是AH,JN,PZ和0-9。没有提及空格。

哦,好的,那您还好吗
Beta Decay

“第一个和第二个配置文件实际上是蓝色斑点,底部带有“ GB”,右侧是我们忽略的边框。” 那么,为什么要将它们包括在代码中,特别是如果覆盖了带有空字符串的数组键时呢?加:允许对代码高尔夫使用短开放语法!:-)
YetiCGN '16

@YetiCGN-如果不是,那么代码将尝试将它们与其他对象匹配!我没意识到它们被覆盖了,幸运的是代码仍然有效。正在修改。您也许可以将我的某些更改适应您的答案。

0

PHP,971970字节

大量使用Yimin Rong答案,可以将其严重击倒,尤其是数组索引,然后将其放入带有gzip压缩的Phar中。

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

改进之处:

第一阶段

  • 删除数字数组索引并将数组重新排序,字符串索引作为隐式常量

第二阶段

  • 替换intval~~(节省8个字节,两次出现)
  • 无需进行for循环初始化
  • file_get_contents($u)替换为join('',file($u))(节省5个字节)
  • 和其他一些

不幸的是,所有第二阶段的改进仅转换为少了1个字节的压缩代码。:-D

此代码用于创建Phar:

<?php
$phar = new Phar('o.phar');
$phar->addFile('o');
$phar['o']->compress(Phar::GZ);
$phar->setStub('<?Phar::mapPhar(o.phar);include"phar://o.phar/o";__HALT_COMPILER();');

php ocr.phar http://i.imgur.com/i8jkCJu.png测试用例图像或任何其他测试用例图像进行测试。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.