【问题标题】:OpenCV - Lotto number recognitionOpenCV - 乐透号码识别
【发布时间】:2014-02-17 17:14:59
【问题描述】:

我正在尝试创建一个可以自动识别彩票号码的程序。

我已经识别出平局的时刻,将球分开,现在我的问题是我无法识别球上的数字。

这是原图:

这是我找到轮廓后的照片:

现在对于每个轮廓,我尝试确定它是否是一个数字以及它是什么数字。这就是我的应用失败的地方。

*重要的是,球可以有多个角度/灯光可以不同,这都会影响图片的质量。

这是我的 prog 找到的轮廓 img 的示例:

这是我识别号码的代码:

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% 的准确率) 我做错了吗?

【问题讨论】:

  • 我会搜索更多的 elabote OCR 算法,而不是使用 matchTemplate。您可以搜索使用 MNIST 数据集的作品。
  • 我正在尝试使用 K_nearest,就像我在这里看到的那样:blog.damiles.com/2008/11/basic-ocr-in-opencv。他们说我应该用每个数字的许多样本来“教”prog,但在我的情况下,我应该只给 prog 几个天使的每个数字吗?

标签: java opencv image-processing image-recognition


【解决方案1】:

如果您尝试为您的盒子使用仿射不变描述符会怎样?您甚至可以从更简单的描述符开始,例如 sift 或 surf,针对每个区域计算并匹配到数据库。它应该很快,因为看起来规模不会改变。 Sift 和 surf 可能会给你一些结果,但为了更稳定,你可以使用 ASIFT。

【讨论】:

    【解决方案2】:

    不是在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();
    }
    

    【讨论】:

    • 我不明白 minAreaRect 将如何帮助我?第一个和第二个图像是我的输出,我确定了轮廓并将“contourArea”限制在我需要的范围内。输出是第二张图片,我如何从第二张图片中获取,minAreaRect 将如何帮助识别“4”?
    • 如果在二值化后应用 cv::dilate,您将得到包含两个数字的轮廓。在它之后,您可以找到轮廓,然后找到最小边界矩形。它将为您提供包含整数的旋转矩形区域。之后,您可以取消旋转该区域(获取非扩张数字图像)并使数字以自然方式站立。然后使用标准 OCR。如果您提供清晰的图像(没有红色框),我将测试我的想法并将代码放在这里。
    • 我已经编辑了我的答案。如您所见,数字以旋转的矩形为界。你可以提取这些矩形的内容,你会得到自然旋转角度的数字。该算法可能需要一些调整,但我认为这个概念很清楚。
    • 感谢您的帮助,嗯,但这并不是我真正要求的。找到轮廓是我现在最少的问题。正如我写的那样,我正在尝试识别数字(44 为'4','4')。我尝试使用blog.damiles.com/2008/11/basic-ocr-in-opencv,但我不知道我的“教学图像”应该是什么,可能有百万天使,你将如何解决它(只是逻辑),数百个样本?
    • 我觉得这篇文章应该会有所帮助:codeproject.com/Articles/196168/…
    猜你喜欢
    • 2021-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-15
    • 2011-04-14
    • 2013-08-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多