iOS+;Tesseract Ocr+;OpenCV

iOS+;Tesseract Ocr+;OpenCV,ios,objective-c,opencv,ocr,tesseract,Ios,Objective C,Opencv,Ocr,Tesseract,我为ios写了一个数字OCR。 我有一个测试图像png,有两个数字5和4。 我找到了轮廓。如何在tesseract传输等高线 初始化tesseract: tess = new tesseract::TessBaseAPI(); tess->Init([dataPath cStringUsingEncoding:NSUTF8StringEncoding], "eng"); tess->SetPageSegMode(tesseract::PSM_SINGLE_CH

我为ios写了一个数字OCR。 我有一个测试图像png,有两个数字5和4。 我找到了轮廓。如何在tesseract传输等高线

初始化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");
tess=new-tesseract::TessBaseAPI();
tess->Init([dataPath cStringUsingEncoding:NSUTF8StringEncoding],“eng”);
tess->SetPageSegMode(tesseract::PSM_SINGLE_CHAR)//tesseract::TessBaseAPI::SetVariable(“TesseEdit_char_白名单”,“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;
}
-(std::vector)查找方形图像:(cv::Mat)\u图像{
向量平方;
cv::Mat pyr,timg,灰色0(_image.size(),cv_8U),灰色;
int thresh=50,N=11;
cv::pyrDown(_image,pyr,cv::Size(_image.cols/2,_image.rows/2));
cv::pyrUp(pyr,timg,_image.size());
矢量轮廓;
int ch[]={0,0};
混音通道(&timg,1,&0,1,ch,1);
对于(int l=0;l=(l+1)*255/N;
}
cv::findContours(灰色、轮廓、cv_RETR_外部、cv_CHAIN_近似简单);
std::向量近似;
CvRect rec1;
std::字符串str;
地图图片列表;
对于(size_t i=0;i0.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(等高线[i]),近似值,弧长(cv::Mat(等高线[i]),真值)*0.02,真值);
正方形。推回(大约);
}
}
}
返回方块;}
绘制等高线的功能:

- (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;
}
cv::Mat调试方块(std::矢量方块,cv::Mat图像){
对于(int i=0;i
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;
}
-(iAction)onMath:(id)发送方{
UIImage*image=[UIImage ImageName:@“test1.png”];
cv::Mat iMat=[self-cvMatFromUIImage:image];
std::vector sq=[自寻方尺寸:iMat];
cv::Mat hui=方形(sq,iMat);
image=[self-UIImageFromCVMat:hui];
self.imView.image=图像;
}
图像后:


链接到github上的项目:

您能检查一下这个答案吗

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

在你的例子中,有几件事正在发生

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

您看到的问题之一是,在图像的某些部分,文本非常“薄”(在阈值设置后,字母中的间隙会显示),而在其他部分,文本非常“厚”(字母开始合并)。Tesseract不会喜欢这样:)之所以会这样,是因为输入图像的亮度不均匀,所以单个阈值不能在所有地方都起作用。解决方案是执行“局部自适应阈值”,其中为图像的每个邻域计算不同的阈值。有很多方法可以做到这一点,但请查看以下示例:

带cv2的OpenCV中的自适应高斯阈值。自适应阈值(…,cv2。自适应高斯阈值(…) 局部大津法 局部自适应直方图均衡化 你还有一个问题,那就是线条不直。根据我的经验,Tesseract可以处理非常有限程度的非直线(透视失真、倾斜或歪斜的百分之几),但它不能处理波浪线。如果可以,请确保源图像具有直线:)不幸的是,没有现成的简单答案;你必须查阅研究文献,自己实现一种最先进的算法(如果可能的话,还需要开源——这确实需要开源解决方案)。Google Scholar搜索“曲线OCR提取”将帮助您开始,例如:

曲线文档图像的文本线分割 最后,我认为你将比使用C++中的OpenCV更好地使用Python生态系统(NDimige,SkVIEWS)。OpenCV python包装器对于简单的东西来说是可以的,但是对于您试图做的事情,它们不会起作用,您需要抓住许多OpenCV中没有的部分(当然,您可以混合搭配)。在C++中实现类似曲线检测的东西要比Python长一个数量级(*,即使你不知道Python,这也是正确的)。
祝你好运

您可以使用SetImage,然后使用SetRectangle和轮廓边界框;你知道如何给tesseract一个它可以读取的图像吗?你能更新你的问题吗?github用户/项目已被删除,在Internet存档中没有任何跟踪。我能找到的最好的相关链接是