2012-02-22 64 views
9

我想识别信用卡中的数字。更糟的是,源图像不能保证高质量。 OCR将通过神经网络来实现,但这不应该成为这里的主题。准备用于OCR的复杂图像

目前的问题是图像预处理。由于信用卡可以有背景和其他复杂的图形,文本不像扫描文档那样清晰。我做了边缘检测(Canny Edge,Sobel)的实验,但没有那么成功。 还计算灰度图像和模糊图像之间的差异(如Remove background color in image processing for OCR所述)并未导致OCRable结果。

我认为大多数方法都失败了,因为特定数字与其背景之间的对比度不够强。可能需要将图像分割成块,并为每个块找到最佳的预处理解决方案?

你有什么建议如何将源代码转换为可读的二进制图像? 是边缘检测的路要走还是应该坚持基本的颜色阈值?

这里是一个灰度阈值处理方法(其中我显然不愉快的结果)的一个样本:

原图:

Original image

灰度图像:

Greyscale image

阈值图像:

Thresholded image

感谢您的任何建议, 瓦伦丁

+0

由于对比度如此之小,我会尝试边缘检测,就像您提到的那样。 – 2012-02-22 23:40:50

回答

5

如果这是在所有可能的,要求更好的照明可以用来捕捉图像。低角度的光线会照亮凸起(或凹陷)字符的边缘,从而大大提高图像质量。如果图像是要通过机器进行分析,那么照明应该针对机器可读性进行优化。

这就是说,一个算法你应该看看是笔划宽度变换,这是用来提取自然图像字符。

Stroke Width Transform (SWT) implementation (Java, C#...)

一个全球性的阈值(二值化或裁剪边缘强度)可能不会削减它为这个应用程序,而是你应该看看本地化的阈值。在您的示例图像中,“31”后面的“02”特别弱,因此搜索该区域中最强的局部边缘会比使用单个阈值滤除字符串中的所有边缘更好。

如果你能识别人物的部分片段,那么你可能会使用一些方向性的形态操作,以帮助加盟段。例如,如果你有一个像以下两个几乎水平段,其中0是背景,而1是前景...

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 1 1 1 1 0 0 1 1 1 1 1 1 0 0 0 
0 0 0 1 0 0 0 1 0 1 0 0 0 0 1 0 0 0 

那么你可以沿水平方向仅执行形态学“关闭”操作加入这些部分。内核可以像

x x x x x 
1 1 1 1 1 
x x x x x 

有更复杂的方法使用贝塞尔符合甚至欧拉螺旋(又名回旋线)进行曲线完成,但预处理,以确定段被连接和后处理,以消除贫穷连接可以得到非常棘手。

5

我会有关的问题的方法是不同的卡片为不同的部分。从(MasterCard,Visa,该清单取决于您)开始,没有许多独特的信用卡,因此您可以像下拉菜单一样指定它是哪个信用卡。这样一来,就可以消除并指定像素区域:

例子:

只有面积从底部20个像素,从 30个像素留下来的10个像素从工作权从底部(创建 矩形)30个像素 - 这将涵盖所有万事达卡

当我和图像处理程序工作(有趣的项目),我打开了画面的对比度,将其转换为灰度,拿了AVERA 1个像素的每一个人的RGB值的GE,它比周围像素:

例子:

PixAvg[i,j] = (Pix.R + Pix.G + Pix.B)/3 
if ((PixAvg[i,j] - PixAvg[i,j+1])>30) 
    boolEdge == true; 

30人是你想怎样不同的图像是。差异越小,则越低。

在我的项目,以查看边缘检测,我提出布尔的单独阵列,其含有从boolEdge值,以及像素阵列。像素阵列只填充黑色和白色的点。它从布尔数组中获取值,其中boolEdge = true是白点,而boolEdge = false是黑点。所以最后,你最终会得到一个只包含白色和黑色点的像素数组(全画面)。

从那里,其中一个数字开始,其中一些完成这是很容易被发现。

1

我在执行我试图从这里使用代码:http://rnd.azoft.com/algorithm-identifying-barely-legible-embossed-text-image/ 效果也较好,但还不够...... 我发现很难找到合适的PARAMS纹理卡。

(void)processingByStrokesMethod:(cv::Mat)src dst:(cv::Mat*)dst { 
cv::Mat tmp; 
cv::GaussianBlur(src, tmp, cv::Size(3,3), 2.0);     // gaussian blur 
tmp = cv::abs(src - tmp);           // matrix of differences between source image and blur iamge 

//Binarization: 
cv::threshold(tmp, tmp, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU); 

//Using method of strokes: 
int Wout = 12; 
int Win = Wout/2; 
int startXY = Win; 
int endY = src.rows - Win; 
int endX = src.cols - Win; 

for (int j = startXY; j < endY; j++) { 
    for (int i = startXY; i < endX; i++) { 
     //Only edge pixels: 
     if (tmp.at<unsigned char="">(j,i) == 255) 
     { 
      //Calculating maxP and minP within Win-region: 
      unsigned char minP = src.at<unsigned char="">(j,i); 
      unsigned char maxP = src.at<unsigned char="">(j,i); 
      int offsetInWin = Win/2; 

      for (int m = - offsetInWin; m < offsetInWin; m++) { 
       for (int n = - offsetInWin; n < offsetInWin; n++) { 
        if (src.at<unsigned char="">(j+m,i+n) < minP) { 
         minP = src.at<unsigned char="">(j+m,i+n); 
        }else if (src.at<unsigned char="">(j+m,i+n) > maxP) { 
         maxP = src.at<unsigned char="">(j+m,i+n); 
        } 
       } 
      } 

      //Voiting: 
      unsigned char meanP = lroundf((minP+maxP)/2.0); 

      for (int l = -Win; l < Win; l++) { 
       for (int k = -Win; k < Win; k++) { 
        if (src.at<unsigned char="">(j+l,i+k) >= meanP) { 
         dst->at<unsigned char="">(j+l,i+k)++; 
        } 
       } 
      } 
     } 
    } 
} 

///// Normalization of imageOut: 
unsigned char maxValue = dst->at<unsigned char="">(0,0); 

for (int j = 0; j < dst->rows; j++) {    //finding max value of imageOut 
    for (int i = 0; i < dst->cols; i++) { 
     if (dst->at<unsigned char="">(j,i) > maxValue) 
      maxValue = dst->at<unsigned char="">(j,i); 
    } 
} 
float knorm = 255.0/maxValue; 

for (int j = 0; j < dst->rows; j++) {    //normalization of imageOut 
    for (int i = 0; i < dst->cols; i++) { 
     dst->at<unsigned char="">(j,i) = lroundf(dst->at<unsigned char="">(j,i)*knorm); 
    } 
} 
+0

好的,你提供了链接,你能不能请给OP提供一些解释。 – Yahya 2014-02-25 10:36:50