2012-02-22 59 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