2014-02-11 115 views
0

我正在嘗試創建一個能夠自動識別彩票號碼的程序。OpenCV - 樂透號碼識別

我已經認識到了平局,分球,現在我的問題是我無法識別球上的數字。

這是原來的畫面:

original

這是我的照片後,我找到了輪廓:

contours

現在對於每個輪廓我試圖確定它的數以及它是多少。這是我的應用失敗的地方。

*重要的是說,球可以在很多角度/照明可以是不同的,這一切都會影響圖片的質量。

這是發現了一個輪廓的img我PROG的例子:

enter image description here

這是我認識的數字代碼:

private void identifyNumber(Mat inFile) { 
    System.out.println("\nRunning identifyNumber"); 
    System.out.println("-------------------------"); 

    int match_method = Imgproc.TM_SQDIFF; 
    Mat img = inFile; 
    Mat bestImage = new Mat(), rotImg; 
    int bestDegree = 0, bestNumber = 0; 
    double lowerstFornumber, lowest = 1E30; 
    String templateNumber; 

    for (int k=0 ; k<=9; k++) { 
     lowerstFornumber = 1E30; 
     for(int i=-90; i<=90; i=i+5){ 
      templateNumber = "C:\\pics\\drawProcessing\\numbers\\" + k + ".png"; 
      Mat templ = Highgui.imread(templateNumber); 

      rotImg = rotateImage(img, i); 
      int result_cols = rotImg.cols() - templ.cols() + 1; 
      int result_rows = rotImg.rows() - templ.rows() + 1; 
      Mat result = new Mat(result_rows, result_cols, CvType.CV_32FC1); 

      Imgproc.matchTemplate(rotImg, templ, result, match_method); 

      MinMaxLocResult mmr = Core.minMaxLoc(result); 

      Point matchLoc; 
      if (match_method == Imgproc.TM_SQDIFF || match_method == Imgproc.TM_SQDIFF_NORMED) { 
       matchLoc = mmr.minLoc; 
      } else { 
       matchLoc = mmr.maxLoc; 
      } 

      double minValue = mmr.minVal; 

//   System.out.println(i+",maxVal:" +maxValue); 

      if(lowerstFornumber > minValue){ 
       lowerstFornumber = minValue; 
      } 

      if(lowest > minValue){ 
       lowest = minValue; 
       bestImage = rotImg; 
       bestDegree = i; 
       bestNumber = arr[k]; 
      } 
     } 
     System.out.println("lowerstFornumber " + arr[k] + " :" + lowerstFornumber); 
    } 

    System.out.println("bestDegree:" + bestDegree); 
    System.out.println("bestNumber:" + bestNumber); 
    System.out.println("_lowest:" + lowest); 
    Highgui.imwrite("C:\\pics\\drawProcessing\\out-best.jpg", bestImage);  
} 

有時它找到的號碼,有時沒有。 這是可能嗎?(我需要100%的準確性) 我是否錯誤?

+0

我會尋找更多的elabote OCR算法,而不是使用matchTemplate。您可以搜索使用MNIST數據集的作品。 – GilLevi

+0

我嘗試使用K_nearest,因爲我在這裏看到:http://blog.damiles.com/2008/11/basic-ocr-in-opencv/。他們說我應該用每個數字的許多樣品「教」編,但在我的情況下,我應該只給幾個天使的編號每個數字? – susparsy

回答

0

如果您爲您的盒子嘗試仿射不變描述符,該怎麼辦?你甚至可以從一個更簡單的描述符開始,比如篩選或衝浪,針對每個區域進行計算並與數據庫匹配。它應該很快,因爲它看起來像比例不會改變。篩選和衝浪可能會給你一些結果,但對於更穩定的事情你可以使用ASIFT。

0

沒有在Java中,但它描述的想法:

#include <iostream> 
#include <vector> 
#include <string> 
#include <fstream> 
#include <opencv2/opencv.hpp> 

using namespace cv; 
using namespace std; 


//---------------------------------------------------------------------- 
// 
//---------------------------------------------------------------------- 
void DetectContour(Mat& img, Mat& res) 
{ 
    vector<vector<Point> > contours; 
    vector<Vec4i> hierarchy; 
    Mat edges=img.clone(); 
    //Canny(img, edges, 50, 190, 3); 
    img.copyTo(edges); 
    findContours(edges,contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE, Point()); 
    if(contours.size()>0) 
    { 
     for(int i = 0; i < contours.size(); i++) 
     { 
      vector<Point> approx; 
      approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true)*0.02, true); 
      double area = contourArea(Mat(approx)); 

      if(area>200) 
       drawContours(res, contours, i, Scalar(255,0,0), CV_FILLED, 8); 
     } 
    } 
} 
//---------------------------------------------------------------------- 
// 
//---------------------------------------------------------------------- 
int main(int argc, char **argv) 
{ 
    cv::namedWindow("result"); 
    Mat img=imread("ball.png"); 

    // Prepare mask 
    Mat mask=Mat::zeros(img.size(),CV_8UC1); 
    Mat img_gray; 
    cv::cvtColor(img,img_gray,cv::COLOR_BGR2GRAY); 
    Mat res=Mat(img.size(),CV_8UC1); 
    res=255; 

    vector<Vec3f> circles; 

    /// Apply the Hough Transform to find the circles 

    HoughCircles(img_gray, circles, cv::HOUGH_GRADIENT, 1, img_gray.rows/8, 140, 70, 0,0); 

    /// Draw the circles detected 
    for(size_t i = 0; i < circles.size(); i++) 
    { 
     Point center(cvRound(circles[i][0]), cvRound(circles[i][1])); 
     int radius = cvRound(circles[i][2]); 
     // circle outline 
     circle(mask, center, radius, Scalar(255,255,255), -1, 8, 0); 
    } 
    img.copyTo(res,mask); 

    cv::cvtColor(res,res,cv::COLOR_BGR2GRAY); 

    threshold(res,res,80,255,cv::THRESH_BINARY_INV); 
    mask=0; 
    DetectContour(res,mask); 

    mask.copyTo(res); 

    int element_size=10; 
    Mat element = getStructuringElement(cv::MORPH_ELLIPSE,Size(2*element_size + 1, 2*element_size+1),Point(element_size, element_size)); 

    int element_size2=5; 
    Mat element2 = getStructuringElement(cv::MORPH_ELLIPSE,Size(2*element_size + 1, 2*element_size+1),Point(element_size, element_size)); 

    cv::dilate(res,res,element2); 
    cv::erode(res,res,element); 

    vector<vector<Point> > contours; 
    vector<Vec4i> hierarchy; 

    findContours(res,contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE, Point()); 
    for (int i=0;i<contours.size();++i) 
    { 
     RotatedRect box = minAreaRect(contours[i]); 
     Point2f center, vtx[4]; 
     box.points(vtx); 

     float w=100; 
     float h=100; 
     // Create a column vector with the coordinates of each point (on the field plane) 
     cv::Mat xField; 
     xField.create(4, 1, CV_32FC2); 
     xField.at<Point2f>(0) = (vtx[0]); 
     xField.at<Point2f>(1) = (vtx[1]); 
     xField.at<Point2f>(2) = (vtx[2]); 
     xField.at<Point2f>(3) = (vtx[3]); 

     // same thing for xImage but with the pixel coordinates instead of the field coordinates, same order as in xField 
     cv::Mat xImage; 
     xImage.create(4, 1, CV_32FC2); 
     xImage.at<Point2f>(0) = (cv::Point2f(0, 0)); 
     xImage.at<Point2f>(1) = (cv::Point2f(w, 0)); 
     xImage.at<Point2f>(2) = (cv::Point2f(w, h)); 
     xImage.at<Point2f>(3) = (cv::Point2f(0, h)); 

     // Compute the homography matrix 
     cv::Mat H = cv::findHomography(xField,xImage); 
     xField.release(); 
     xImage.release(); 

     Mat warped; 
     warpPerspective(img,warped,H,Size(w,h)); 
     H.release(); 
     char win_name[255]; 
     sprintf(win_name,"number_image %d",i); 
     namedWindow(win_name); 
     imshow(win_name,warped); 
//  cv::waitKey(0); 

     for(int j = 0; j < 4; j++) 
     { 
      line(img, vtx[j], vtx[(j+1)%4], Scalar(0, 255, 0), 1, LINE_AA); 
     } 

    } 




    imshow("result",img); 
    cv::waitKey(0); 

    cv::destroyAllWindows(); 
} 

enter image description here

+0

我不明白minAreaRect如何幫助我?第一張和第二張圖片是我的輸出,我確定了輪廓並將'contourArea'限制爲我所需要的。輸出是第二個圖像,我如何從第二個圖像獲得,minAreaRect如何幫助從那裏識別'4'? – susparsy

+0

如果在二值化之後應用cv :: dilate,則會得到包含兩個數字的輪廓。之後你可以找到輪廓,然後最小邊界矩形。它會讓你旋轉的矩形區域,其中包括整個數字。之後你可以不旋轉這個區域(得到非擴張的數字圖像),並讓數字以自然的方式站立。然後使用標準的OCR。如果你提供清晰的圖像(沒有紅框),我會測試我的想法並將代碼放在這裏。 –

+0

已編輯,請檢查 – susparsy