文字识别

基于Opencv & Tesseract

这里记录一下如何找出文字轮廓

1、首先获取到图片的灰度图

    Mat img = [MMOpenCVHelper cvMatFromUIImage:self.receiveImage];
    //灰度化
    Mat gray;
    cvtColor(img, gray, COLOR_BGR2GRAY);

2、画出文字轮廓 这里面操作相对较多 

- (void)findTextRegionWithGrayMat:(Mat )gray{
    Mat img = [MMOpenCVHelper cvMatFromUIImage:self.showView.image];
    //为了切割图片时不会带有绿线 拷贝一份原图
    Mat img_copy = [MMOpenCVHelper cvMatFromUIImage:[self.showView.image copy]];
    //形态学变换的预处理,得到可以查找矩形的轮廓
    Mat dilation  = [self preprocess:gray];
    //查找和筛选文字区域
    std::vector<RotatedRect> rects = findTextRegion(dilation);
    queue_count = rects.size();
    begin_Queue_Count = 0;
    //创建画轮廓的队列
    [self.opQueue addOperationWithBlock:^{
        for (RotatedRect rect :  rects){
            if (rect.size.height <=0 || rect.size.width <=0) {
                continue;
            }
            //画线
            Point2f P[4];
            rect.points(P);
            for (int j = 0; j <= 3; j++){
                line(img, P[j], P[(j + 1) % 4], Scalar(0,255,0), 4);
            }
            //主线程中更新UI
            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                self.showView.image = [MMOpenCVHelper UIImageFromCVMat:img];
                begin_Queue_Count ++;
                if (begin_Queue_Count == queue_count) {
                    [self beginTesseract:gray rects:rects withImageCopy:img_copy];
                }
            }];
        }
    }];
    
}

   图片的预处理,这里应该要做降噪处理,暂时没有研究

//预处理
- (Mat )preprocess:(Mat )gray{
    //形态梯度
    Mat grad;
    Mat morphKernel = getStructuringElement(MORPH_ELLIPSE, cv::Size(3, 3));
    morphologyEx(gray, grad, MORPH_GRADIENT, morphKernel);
    //二值化
    Mat bw;
    threshold(grad, bw, 0.0, 255.0, THRESH_BINARY | THRESH_OTSU);
    //闭运算
    Mat connected;
    morphKernel = getStructuringElement(MORPH_RECT, cv::Size(14, 2));
    //iterations 默认值为1
    morphologyEx(bw, connected, MORPH_CLOSE, morphKernel,cv::Point(-1,-1), 3);
    return connected;
}
//取出字体区域
std::vector<RotatedRect> findTextRegion(Mat img){
    std::vector<RotatedRect> rects;
    //1.查找轮廓
    std::vector<std::vector<cv::Point>> contours;
    std::vector<Vec4i> hierarchy;
    findContours(img, contours, hierarchy, CV_RETR_CCOMP, CHAIN_APPROX_SIMPLE,cv::Point(0, 0));
    //2.筛选那些面积小的
    for (int i = contours.size() - 1; i > -1 ; i--){
        
        //计算当前轮廓的面积
        double area = contourArea(contours[i]);
        
        //面积小于1000的全部筛选掉
        if (area < 100)
            continue;
        
        //轮廓近似,作用较小,approxPolyDP函数有待研究
        double epsilon = 0.001*arcLength(contours[i], true);
        Mat approx;
        approxPolyDP(contours[i], approx, epsilon, true);

        //找到最小矩形,该矩形可能有方向
        RotatedRect rect = minAreaRect(contours[i]);
        //计算高和宽
        int m_width = rect.boundingRect().width;
        int m_height = rect.boundingRect().height;
        
        if (m_height > m_width * 1.2){
            continue;
        }
        //获取ROI
        Mat maskROI;
        //判断ROI内容是否超过0.45
        Point2f center = rect.center;//外接矩形中心点坐标
        //提取
        float rect_width ;
        float rect_height;
        if(rect.size.width < rect.size.height){
            rect_width = rect.size.height;
            rect_height = rect.size.width;
        } else {
            rect_width = rect.size.width;
            rect_height = rect.size.height;
        }
        
        if (center.x - rect_width/2  < 0|| rect_width  < 0 || center.x - rect_width/2  + rect_width  > img.cols || center.y - rect_height/2  < 0 || rect_height  <0 || center.y - rect_height/2  + rect_height  > img.rows) {
            NSLog(@"超出范围了");
            continue;
            
        }
        maskROI = img(cv::Rect(center.x - (rect_width/ 2) , center.y - (rect_height/2)   , rect_width , rect_height ));//提取ROi
        double content = (double)countNonZero(maskROI);
        double r = content/(m_width*m_height);
        if (r < 0.4 || (rect_width < 8 && rect_width < 8)) {
            NSLog(@"内容少于0.4占比");
            continue;
        }
        rects.push_back(rect);
    }
    return rects;
}

文字轮廓基本到这里就可以画出来了,接下来取出每个小块图片丢给tesseract去识别。

因为这个速度没有很快所以要充分利用CPU,我后面识别用的

可以设置

猜你喜欢

转载自www.cnblogs.com/wycstudy/p/9084354.html