字符重build和OCR填充

我正在轮胎上进行文字识别。 为了使用OCR,我必须先得到一个清晰的二进制地图。

我已经处理了图像,文本出现了断开和停止的边缘。 我已经在MATLAB中尝试过使用圆形光盘和线条元素进行标准侵蚀/扩大,但这并没有真正的帮助。

Pr1-关于如何重build这些angular色,填补angular色间的差距?

原始Image_highres原始Image_lowRescanny边缘检测

Pr2-上面的图像是更高的分辨率和良好的照明。 但是,如果照度较差,分辨率相对较低,那么处理的可行select是什么?

在这里输入图像描述

解决scheme尝试

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

无论如何,非常感谢迄今为止的build议。 我仍然会等待,看看是否有人可以build议不同的东西,也许想出了盒子:)。

在这里输入图像描述

Matlab实现下面Spektre代码步骤的结果(没有中风扩张(按照1,2,3,4:

在这里输入图像描述

并且具有阈值tr0 = 400和tr1 = 180以及用于归一化的angular点顺序1,3,2,4 在这里输入图像描述

最好的祝福

Wajahat

我已经玩了一下你的input

照明标准化+dynamic范围标准化有助于获得更好的结果,但仍然远离需求。 我想尝试锐化部分派生,以提高从背景的字母和阈值出来的小颠簸,然后集成回去和重新着色屏蔽图像时,我会有时间(不知道什么时候可能tomorow)我会编辑这个(和评论/通知您)

规范化的照明

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

规范化的照明

边缘检测

xy的强度i的部分推导…

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

然后通过treshold=13进行treshold=13

边缘检测

[笔记]

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

经过一些分析,我发现你的图像对于锐化整合来说边缘不佳

这里是在图像的中间线上由x导出之后的强度图的例子

边缘不好

正如你所看到的,黑色的区域很好,但是白色的区域几乎不能从背景噪音中识别出来。 所以你唯一的希望就是使用@Daniel答案build议的最小最大值过滤 ,并且在黑色边缘区域占用更多的权重(白色不可靠)

最小值

最小值最大值filter强调黑色(蓝色面罩)和白色(红色面罩)区域。 如果展位面积是可靠的,那么你只是填补他们之间的空间,但这不是一个select在你的情况,而是我会扩大面积(加权更多的蓝色面具)和OCR的结果与OCR定制这种3色input。

  • 你可以为你自己定制OCR,看看OCR和字符的相似性

也可以拍摄2张不同光照位置的照片,并将其组合起来,覆盖四面八方可识别的黑色区域

最后一种方法的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); } } } //--------------------------------------------------------------------------- 

我使用我自己的图片类的图像,所以一些成员是:

  • xs,ys图像的像素大小
  • p[y][x].dd(x,y)位置处的像素,为32位整数types
  • clear(color) – 清除整个图像
  • resize(xs,ys) – 将图像大小调整为新的分辨率
  • bmp – 使用Canvas访问的VCL封装的GDI位图

我为2个相关的成员函数添加了源代码(不需要在这里复制整个类)

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 

LQ的例子

由于照明条件,红色区域不可用(closures)

您可以先应用一个最大滤波器(为新图像中的每个像素分配来自原始图像中相同像素周围的邻域的最大值),然后使用最小滤波器(从最大图像中的邻域分配最小值)。 特别是如果你把这个区域的形状做得比它高(比如,左右两个或者三个像素,上下一个像素),你应该可以得到一些你的angular色(你的图像看起来主要是在水平方向上的间隙)。

最佳邻居的大小和形状取决于你的具体问题,所以你必须尝试一些。 通过这个操作,你可能会遇到粘连在一起的字符 – 如果它们与其他的blob相比太宽,你可能必须检测到blob并将它们分开。

编辑:另外,二值化设置绝对是关键。 尝试使用几种不同的二值化algorithm(Otsu,Sauvola,…)来查看哪一个(以及哪些参数)最适合您。