2012-02-22 32 views
9

Tôi muốn nhận dạng chữ số từ thẻ tín dụng. Để làm cho mọi thứ tồi tệ hơn, hình ảnh nguồn không được đảm bảo có chất lượng cao. OCR phải được thực hiện thông qua mạng thần kinh, nhưng đó không phải là chủ đề ở đây.Chuẩn bị hình ảnh phức tạp cho OCR

Vấn đề hiện tại là xử lý trước hình ảnh. Vì thẻ tín dụng có thể có hình nền và đồ họa phức tạp khác, văn bản không rõ ràng như khi quét tài liệu. Tôi đã thực hiện các thí nghiệm với phát hiện cạnh (Canny Edge, Sobel), nhưng nó không thành công. Đồng thời tính toán sự khác biệt giữa hình thang màu xám và hình mờ (như đã nêu tại Remove background color in image processing for OCR) không dẫn đến kết quả OCRable.

Tôi nghĩ hầu hết các cách tiếp cận đều thất bại vì độ tương phản giữa một chữ số cụ thể và nền của nó không đủ mạnh. Có lẽ cần phải phân đoạn hình ảnh thành các khối và tìm giải pháp tiền xử lý tốt nhất cho mỗi khối?

Bạn có bất kỳ đề xuất nào về cách chuyển đổi nguồn thành hình ảnh nhị phân có thể đọc được không? Phát hiện cạnh có phải là đường đi hay tôi nên gắn bó với viền màu cơ bản?

Dưới đây là một mẫu của một cách tiếp cận greyscale-ngưỡng (nơi tôi rõ ràng là không hài lòng với kết quả):

Original image:

Original image

ảnh Greyscale:

Greyscale image

Hình ảnh bị chặn :

Thresholded image

Cảm ơn lời khuyên, Valentin

+0

Vì có quá ít tương phản, tôi sẽ thử phát hiện cạnh, như bạn đã đề cập. –

Trả lời

5

Nếu có thể, hãy yêu cầu sử dụng ánh sáng tốt hơn để chụp ảnh. Một ánh sáng góc thấp sẽ chiếu sáng các cạnh của các ký tự được nâng lên (hoặc bị chìm), do đó cải thiện đáng kể chất lượng hình ảnh. Nếu hình ảnh có nghĩa là để được phân tích bởi một máy, thì ánh sáng phải được tối ưu hóa cho khả năng đọc máy.

Điều đó nói rằng, một thuật toán bạn nên xem xét là Chuyển đổi chiều rộng nét, được sử dụng để trích xuất các ký tự từ hình ảnh tự nhiên.

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

Ngưỡng toàn cầu (đối với binarization hoặc cắt mạnh cạnh) có thể sẽ không cắt nó cho ứng dụng này, và thay vào đó bạn nên xem xét các ngưỡng cục bộ. Trong ví dụ của bạn, "02" theo "31" đặc biệt yếu, vì vậy việc tìm kiếm các cạnh cục bộ mạnh nhất trong vùng đó sẽ tốt hơn lọc tất cả các cạnh trong chuỗi ký tự bằng cách sử dụng một ngưỡng đơn.

Nếu bạn có thể xác định một phần phân đoạn của các ký tự, thì bạn có thể sử dụng một số thao tác hình thái định hướng để giúp nối các phân đoạn. Ví dụ: nếu bạn có hai phân đoạn gần như ngang như sau, trong đó 0 là nền và 1 là tiền cảnh ...

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 

thì bạn có thể thực hiện thao tác "đóng" hình thái theo hướng ngang chỉ đến tham gia các phân đoạn đó. Các hạt nhân có thể là một cái gì đó giống như

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

Có nhiều phương pháp tinh vi hơn để thực hiện hoàn thành đường cong bằng Bezier phù hợp hoặc thậm chí Euler xoắn ốc (aka clothoids), nhưng tiền xử lý để xác định các phân đoạn để được tham gia và xử lý sau để loại bỏ nghèo tham gia có thể nhận được Rất khôn lanh.

5

Cách làm thế nào tôi sẽ đi về vấn đề tách biệt các thẻ thành phần khác nhau. Không có nhiều thẻ tín dụng duy nhất để bắt đầu với (MasterCard, Visa, danh sách tùy thuộc vào bạn), vì vậy bạn có thể thực hiện như một menu thả xuống để chỉ định thẻ tín dụng nào. Bằng cách đó, bạn có thể loại bỏ và xác định vùng pixel:

Ví dụ:

Chỉ làm việc với diện tích 20 pixel từ phía dưới, 30 pixel từ trái sang 10 pixel từ phải sang 30 pixel từ đáy (tạo một hình chữ nhật ) - Đây sẽ bao gồm tất cả MasterCards

Khi tôi làm việc với các chương trình xử lý ảnh (dự án thú vị) tôi bật lên sự tương phản của hình ảnh, chuyển đổi nó sang màu xám quy mô, lấy avera ge của mỗi giá trị RGB cá nhân của 1 pixel, và so sánh nó với tất cả xung quanh điểm ảnh:

Ví dụ:

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

30 sẽ là thế nào riêng biệt mà bạn muốn hình ảnh của bạn được. Sự khác biệt càng thấp thì mức thấp hơn sẽ là sự khoan dung.

Trong dự án của tôi, để xem phát hiện cạnh, tôi đã tạo một mảng riêng biệt các boolean, chứa các giá trị từ boolEdge và mảng pixel. Mảng pixel được lấp đầy chỉ với các chấm đen và trắng. Nó nhận các giá trị từ mảng boolean, trong đó boolEdge = true là một chấm trắng, và boolEdge = false là một chấm đen. Vì vậy, cuối cùng, bạn kết thúc với một mảng pixel (hình ảnh đầy đủ) mà chỉ chứa các chấm trắng và đen.

Từ đó, sẽ dễ dàng hơn để phát hiện vị trí số bắt đầu và số kết thúc.

1

trong việc thực hiện của tôi, tôi đã cố gắng để sử dụng mã từ đây: http://rnd.azoft.com/algorithm-identifying-barely-legible-embossed-text-image/ kết quả tốt hơn nhưng không đủ ... tôi tìm thấy nó khó khăn để tìm thấy những params phù hợp với card kết cấu.

(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

Tốt, bạn đã cung cấp liên kết, bạn có thể vui lòng cung cấp một số giải thích cho OP không. – Yahya

Các vấn đề liên quan