OCR 的字符重建和填充

Waj*_*hat 5 ocr matlab image-processing edge-detection

我正在研究轮胎上的文本识别。为了使用 OCR,我必须首先获得清晰的二值图。

我已经处理了图像,文本出现边缘破损和不连续的情况。我已经在 MATLAB 中尝试过使用圆盘和线元素进行标准腐蚀/膨胀,但它并没有真正帮助。

Pr1-关于如何重建这些字符并填补字符笔划之间的空白的任何想法?

原图_highres 原始图像_lowRes 检测到精明的边缘

Pr2- 上面的图像分辨率更高,光照良好。但是,如果如下图所示,照明较差且分辨率相对较低,那么可行的处理选项是什么?

在此处输入图片说明

尝试的解决方案:

S1:这是对Spektre共享的处理过的图像应用中值滤波器的结果。为了去除噪声,我应用了中值滤波器 (5x5),随后使用线元素 (5,11) 进行图像膨胀。即使现在 OCR (Matlab 2014b) 也只能识别一些字符

无论如何,非常感谢到目前为止的建议。我仍然会等着看是否有人可以提出不同的建议,也许是开箱即用的:)。

在此处输入图片说明

Matlab 实现以下 Spektre 代码中的步骤的结果(没有笔画扩张(按 1、2、3、4 的顺序对角进行标准化:

在此处输入图片说明

并且阈值 tr0=400 和 tr1=180 以及归一化的角顺序 1,3,2,4 在此处输入图片说明

此致

瓦贾哈特

Spe*_*tre 5

我玩过你的输入

光照归一化 + 动态范围归一化有助于获得更好的结果,但距离需要的还很远。我想尝试锐化部分派生,以增强背景中的字母并在整合回来之前调整小凹凸并重新着色以掩盖图像,当我有时间时(不确定何时可能明天)我将编辑此(并评论/通知你)

标准化照明

计算平均角强度并双线性重新调整强度以匹配平均颜色

标准化照明

如果您需要更复杂的东西,请参阅:

边缘检测

强度i的部分推导xy...

  • i=|i(x,y)/dx|+|i(x,y)/dy|

然后由 treshold=13

边缘检测

[笔记]

为了消除大部分噪声,我在边缘检测之前应用了平滑滤波

[edit1] 经过一些分析,我发现您的图像边缘较差,无法进行锐化集成

这里是图像中间线 x 第一次推导后的强度图示例

边缘不好

如您所见,黑色区域很好,但白色区域几乎无法从背景噪音中识别出来。所以你唯一的希望是按照@Daniel 的回答建议使用最小最大过滤,并在黑边区域增加权重(白色不可靠)

最小最大

min max filter 强调黑色(蓝色遮罩)和白色(红色遮罩)区域。如果展位区域是可靠的,那么您只需填充它们之间的空间,但在您的情况下这不是一个选项,而是我会扩大区域(在蓝色蒙版上加权更多)和 OCR 使用针对此类 3 种颜色输入定制的 OCR 的结果。

您还可以使用不同的光线位置和固定的相机拍摄 2 张​​图像,然后将它们组合起来以从各个方面覆盖可识别的黑色区域

[edit2] 最后一个方法的 C++ 源代码

//---------------------------------------------------------------------------
typedef union { int dd; short int dw[2]; byte db[4]; } color;
picture pic0,pic1,pic2; // pic0 source image,pic1 normalized+min/max,pic2 enlarge filter
//---------------------------------------------------------------------------
void filter()
    {
    int sz=16;          // [pixels] square size for corner avg color computation (c00..c11)
    int fs0=5;          // blue [pixels] font thickness
    int fs1=2;          // red  [pixels] font thickness
    int tr0=320;        // blue min treshold
    int tr1=125;        // red  max treshold

    int x,y,c,cavg,cmin,cmax;
    pic1=pic0;          // copy source image
    pic1.rgb2i();       // convert to grayscale intensity

    for (x=0;x<5;x++) pic1.ui_smooth();
    cavg=pic1.ui_normalize();

    // min max filter
    cmin=pic1.p[0][0].dd; cmax=cmin;
    for (y=0;y<pic1.ys;y++)
     for (x=0;x<pic1.xs;x++)
        {
        c=pic1.p[y][x].dd;
        if (cmin>c) cmin=c;
        if (cmax<c) cmax=c;
        }
    // treshold min/max
    for (y=0;y<pic1.ys;y++)
     for (x=0;x<pic1.xs;x++)
        {
        c=pic1.p[y][x].dd;
             if (cmax-c<tr1) c=0x00FF0000; // red
        else if (c-cmin<tr0) c=0x000000FF; // blue
        else                 c=0x00000000; // black
        pic1.p[y][x].dd=c;
        }
    pic1.rgb_smooth();  // remove single dots

    // recolor image
    pic2=pic1; pic2.clear(0);
    pic2.bmp->Canvas->Pen  ->Color=clWhite;
    pic2.bmp->Canvas->Brush->Color=clWhite;
    for (y=0;y<pic1.ys;y++)
     for (x=0;x<pic1.xs;x++)
        {
        c=pic1.p[y][x].dd;
        if (c==0x00FF0000)
            {
            pic2.bmp->Canvas->Pen  ->Color=clRed;
            pic2.bmp->Canvas->Brush->Color=clRed;
            pic2.bmp->Canvas->Ellipse(x-fs1,y-fs1,x+fs1,y+fs1); // red
            }
        if (c==0x000000FF)
            {
            pic2.bmp->Canvas->Pen  ->Color=clBlue;
            pic2.bmp->Canvas->Brush->Color=clBlue;
            pic2.bmp->Canvas->Ellipse(x-fs0,y-fs0,x+fs0,y+fs0); // blue
            }
        }
    }
//---------------------------------------------------------------------------
int  picture::ui_normalize(int sz=32)
    {
    if (xs<sz) return 0;
    if (ys<sz) return 0;
    int x,y,c,c0,c1,c00,c01,c10,c11,cavg;

    // compute average intensity in corners
    for (c00=0,y=         0;y<     sz;y++) for (x=         0;x<     sz;x++) c00+=p[y][x].dd; c00/=sz*sz;
    for (c01=0,y=         0;y<     sz;y++) for (x=xs-sz;x<xs;x++) c01+=p[y][x].dd; c01/=sz*sz;
    for (c10=0,y=ys-sz;y<ys;y++) for (x=         0;x<     sz;x++) c10+=p[y][x].dd; c10/=sz*sz;
    for (c11=0,y=ys-sz;y<ys;y++) for (x=xs-sz;x<xs;x++) c11+=p[y][x].dd; c11/=sz*sz;
    cavg=(c00+c01+c10+c11)/4;

    // normalize lighting conditions
    for (y=0;y<ys;y++)
     for (x=0;x<xs;x++)
        {
        // avg color = bilinear interpolation of corners colors
        c0=c00+(((c01-c00)*x)/xs);
        c1=c10+(((c11-c10)*x)/xs);
        c =c0 +(((c1 -c0 )*y)/ys);
        // scale to avg color
        if (c) p[y][x].dd=(p[y][x].dd*cavg)/c;
        }
    // compute min max intensities
    for (c0=0,c1=0,y=0;y<ys;y++)
     for (x=0;x<xs;x++)
        {
        c=p[y][x].dd;
        if (c0>c) c0=c;
        if (c1<c) c1=c;
        }
    // maximize dynamic range <0,765>
    for (y=0;y<ys;y++)
     for (x=0;x<xs;x++)
      c=((p[y][x].dd-c0)*765)/(c1-c0);
    return cavg;
    }
//---------------------------------------------------------------------------
void picture::rgb_smooth()
    {
    color   *q0,*q1;
    int     x,y,i;
    color   c0,c1,c2;
    if ((xs<2)||(ys<2)) return;
    for (y=0;y<ys-1;y++)
        {
        q0=p[y  ];
        q1=p[y+1];
        for (x=0;x<xs-1;x++)
            {
            c0=q0[x];
            c1=q0[x+1];
            c2=q1[x];
            for (i=0;i<4;i++) q0[x].db[i]=WORD((WORD(c0.db[i])+WORD(c0.db[i])+WORD(c1.db[i])+WORD(c2.db[i]))>>2);
            }
        }
    }
//---------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

我对图像使用我自己的图片类,因此一些成员是:

  • xs,ys 以像素为单位的图像大小
  • p[y][x].dd(x,y)位置处的像素为 32 位整数类型
  • clear(color) - 清除整个图像
  • resize(xs,ys) - 将图像调整为新的分辨率
  • bmp - VCL 封装 GDI 位图,具有 Canvas 访问权限

我只为 2 个相关成员函数添加了源代码(无需在此处复制整个类)

[edit3] LQ 图像

我找到的最佳设置(代码相同):

int sz=32;          // [pixels] square size for corner avg color computation (c00..c11)
int fs0=2;          // blue [pixels] font thickness
int fs1=2;          // red  [pixels] font thickness
int tr0=52;         // blue min treshold
int tr1=0;          // red  max treshold
Run Code Online (Sandbox Code Playgroud)

LQ示例

由于光照条件,红色区域不可用(关闭)