2

我为 ios 写了一个数字 OCR。我有一个带有两位数字 5 和 4 的测试图像 png。我找到了轮廓。如何在 tesseract 转移轮廓一?

初始化正方体:

    tess = new tesseract::TessBaseAPI();
    tess->Init([dataPath cStringUsingEncoding:NSUTF8StringEncoding], "eng");
    tess->SetPageSegMode(tesseract::PSM_SINGLE_CHAR); //<-- !!!!
    tess->tesseract::TessBaseAPI::SetVariable("tessedit_char_whitelist", "0123456789");

检测轮廓功能:

- (std::vector<std::vector<cv::Point> >)findSquaresInImage:(cv::Mat)_image {
std::vector<std::vector<cv::Point> > squares;
cv::Mat pyr, timg, gray0(_image.size(), CV_8U), gray;
int thresh = 50, N = 11;
cv::pyrDown(_image, pyr, cv::Size(_image.cols/2, _image.rows/2));
cv::pyrUp(pyr, timg, _image.size());
std::vector<std::vector<cv::Point> > contours;
    int ch[] = {0, 0};
    mixChannels(&timg, 1, &gray0, 1, ch, 1);
    for( int l = 0; l < N; l++ ) {
        if( l == 0 ) {
            cv::Canny(gray0, gray, 0, thresh, 5);
            cv::dilate(gray, gray, cv::Mat(), cv::Point(-1,-1));
        }
        else {
            gray = gray0 >= (l+1)*255/N;
        }
        cv::findContours(gray, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
        std::vector<cv::Point> approx;

        CvRect rec1;
        std::string str;
        std::map<int,IplImage*> pic_list;

        for( size_t i = 0; i < contours.size(); i++ )
        {

            rec1 = cv::boundingRect(contours[i]);

            if (rec1.height > 0.5*gray.rows && rec1.width < 0.756*gray.cols) {
                NSLog(@"%d %d %d %d", rec1.width, rec1.height, rec1.x, rec1.y);
                cv::approxPolyDP(cv::Mat(contours[i]), approx, arcLength(cv::Mat(contours[i]), true)*0.02, true);
                squares.push_back(approx);
            }
        }
    }

return squares;  }

绘制轮廓功能:

cv::Mat debugSquares( std::vector<std::vector<cv::Point> > squares, cv::Mat image ) {
for ( int i = 0; i< squares.size(); i++ ) {
    // draw contour
    cv::drawContours(image, squares, i, cv::Scalar(255,0,0), 1, 8, std::vector<cv::Vec4i>(), 0, cv::Point());

    // draw bounding rect
    cv::Rect rect = boundingRect(cv::Mat(squares[i]));
    cv::rectangle(image, rect.tl(), rect.br(), cv::Scalar(0,255,0), 2, 8, 0);

    // draw rotated rect
    cv::RotatedRect minRect = minAreaRect(cv::Mat(squares[i]));
    cv::Point2f rect_points[4];
    minRect.points( rect_points );
    for ( int j = 0; j < 4; j++ ) {
        cv::line( image, rect_points[j], rect_points[(j+1)%4], cv::Scalar(0,0,255), 1, 8 ); // blue
    }
}

return image;
}

btn 点击方法:

- (IBAction)onMath:(id)sender {
    UIImage *image = [UIImage imageNamed:@"test1.png"];

    cv::Mat iMat = [self cvMatFromUIImage:image];
    std::vector<std::vector<cv::Point> > sq = [self findSquaresInImage:iMat];
    cv::Mat hui = debugSquares(sq, iMat);

    image = [self UIImageFromCVMat:hui];
    self.imView.image = image;
}

之后的图像:

github上的项目链接:https ://github.com/MaxPatsy/iORC

4

1 回答 1

0

你能在这里检查这个答案

我在这里描述了一些为 Tesseract 准备图像的技巧:使用 tesseract 识别车牌

在您的示例中,发生了几件事...

您需要使文本为黑色,图像的其余部分为白色(而不是相反)。这就是字符识别的重点。灰度是可以的,只要背景多为全白,文字多为全黑即可;文本的边缘可能是灰色的(抗锯齿),这可能有助于识别(但不一定 - 你必须尝试)

您看到的问题之一是,在图像的某些部分,文本真的很“薄”(并且在阈值处理后字母中的间隙出现),而在其他部分它真的很“厚”(字母开始合并)。Tesseract 不会喜欢这样 :) 发生这种情况是因为输入图像的光照不均匀,因此单个阈值并不适用于任何地方。解决方案是进行“局部自适应阈值”,其中为图像的每个邻域计算不同的阈值。有很多方法可以做到这一点,但请查看以下示例:

使用 cv2.adaptiveThreshold(...,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,...) 在 OpenCV 中进行自适应高斯阈值处理 局部 Otsu 的方法 局部自适应直方图均衡 您遇到的另一个问题是线条不直。根据我的经验,Tesseract 可以处理非常有限程度的非直线(百分之几的透视失真、倾斜或歪斜),但它实际上不适用于波浪线。如果可以,请确保源图像具有直线 :) 不幸的是,对此没有简单的现成答案;您必须查看研究文献并自己实现一种最先进的算法(如果可能的话将其开源 - 确实需要一个开源解决方案)。谷歌学术搜索“曲线 OCR 提取”会让你开始,

弯曲文档图像的文本行分割最后:我认为使用 python 生态系统(ndimage、skimage)比使用 C++ 中的 OpenCV 会做得更好。OpenCV python 包装器适用于简单的东西,但对于你想要做的事情,它们不会完成这项工作,你需要抓取许多不在 OpenCV 中的部分(当然你可以混合和匹配)。在 C++ 中实现曲线检测之类的东西将比在 python 中长一个数量级(*即使你不了解 python,也是如此)。

祝你好运!

于 2015-12-24T08:59:29.527 回答