OpenCV+Tesseract进行OCR学习(一)文字提取

OpenCV+Tesseract进行OCR学习(一)文字提取

OCR简介

熟悉OCR的人都了解,OCR大致分为两个部分:

-文字提取text extractor
-文字识别text recognition

其中,第一部分是属于图像处理部分,涉及到图像分割的知识,而第二部分则大多数利用谷歌的Tesseract来进行字符的识别,设计到的东西不多,当然也不难,难的是要能够做到非常准确的识别率,以及它的识别速率。

文字提取

这一部分工作是很关键的,因为文字提取的好坏,直接影响到最后的识别结果,相当于预处理部分,是非常重要的,其主要目的是为了分割出文字字符。
主要涉及工作有:

: -灰度化
-直方图均衡
-分块处理
-锐化
-Otsu
-处理0和1边界值
-如果有必要,还需要进行噪声去除,这里要涉及到找连通分量的相关计算;

code

你看,是不是很简单呀。。好辣,下面附上代码:

void TextDetector::segmentText(cv::Mat &spineImage, cv::Mat &segSpine, bool removeNoise){

    cv::Mat spineGray;
    cvtColor(spineImage, spineGray, CV_BGR2GRAY);
    imshow("gray source" , spineGray);
    spineGray = spineGray - 0.5;
//    WriteData("/Users/eternity/Desktop/未命名文件夹/gray1.txt", spineGray);
//    waitKey();
    cv::Mat spineAhe;
    adaptiveHistEqual(spineGray, spineAhe, 0.01);
    imshow("ahe", spineAhe);
//    WriteData("/Users/eternity/Desktop/未命名文件夹/gray2.txt", spineAhe);

    int window_num = 40;

    double window_h = (spineImage.rows / (double)window_num + 1e-3);

    int window_w = spineImage.cols;

    cv::Mat spine_th = cv::Mat::zeros(spineGray.size(), CV_8U);

    for (int i = 0; i < window_num; i ++) {
        double cut_from_r = window_h * i;
        double cut_to_r = window_h * (i+1);
        cv::Mat window_img = cv::Mat::zeros(Size(cut_to_r-cut_from_r + 1, window_w), CV_8U);
        cv::Rect rect = cv::Rect(0, cut_from_r, window_w-1, cut_to_r - cut_from_r + 1);
        window_img = cv::Mat(spineGray, rect);
        imshow("window section", window_img);

        sharpenImage(window_img, window_img);
        imshow("sharpen", window_img);
//        waitKey();
//        WriteData("/Users/eternity/Desktop/未命名文件夹/gray4.txt", window_img);
        double max_local,min_local;
        minMaxLoc(window_img, &min_local, &max_local);
        double color_diff = max_local - min_local;
        double thresh;
        cv::Mat window_tmp;
        if (color_diff > 50)
            thresh = threshold(window_img, window_tmp, 1, 255, THRESH_OTSU);
        else
            thresh = 0;
//        cout<<thresh<<endl;
        cv::Mat seg_window(window_img.size(), CV_64F);
        imgQuantize(window_img, seg_window, thresh);
//        WriteData("/Users/eternity/Desktop/未命名文件夹/quantize2.txt", seg_window);
        seg_window = seg_window == 1;
//        seg_window = seg_window / 255;
        //处理0边界值
        vector<int> cols1,cols2,rows1,rows2;
        findKEdgeFirst(seg_window, 0, 5, rows1, cols1);
        findKEdgeLast (seg_window, 0, 5, rows2, cols2);
        float max_zero_dist, max_one_dist;
        if(cols1.empty() || cols2.empty())
            max_zero_dist = 0.0;
        else{
            float avg_right = (rows2[0]+rows2[1]+rows2[2]+rows2[3]+rows2[4]) / (float)sizeof(rows2);
            float avg_left  = (rows1[0]+rows1[1]+rows1[2]+rows1[3]+rows1[4]) / (float)sizeof(rows1);
            max_zero_dist = avg_right - avg_left;
        }
        cols1.clear();
        cols2.clear();
        rows1.clear();
        rows2.clear();

        //处理1边界值
        findKEdgeFirst(seg_window, 255, 5, rows1, cols1);
        findKEdgeLast (seg_window, 255, 5, rows2, cols2);
        if(cols1.empty() || cols2.empty())
            max_one_dist = 0;
        else{
            float avg_right = (rows2[0]+rows2[1]+rows2[2]+rows2[3]+rows2[4]) / (float)sizeof(rows2);
            float avg_left  = (rows1[0]+rows1[1]+rows1[2]+rows1[3]+rows1[4]) / (float)sizeof(rows1);
            max_one_dist = avg_right - avg_left;
        }
        cols1.clear();
        cols2.clear();
        rows1.clear();
        rows2.clear();

        cv::Mat idx;
        findNonZero(seg_window, idx);
        int one_count = (int)idx.total();
        int zero_count = (int)seg_window.total() - one_count;

        float one_zero_diff = max_one_dist - max_zero_dist;
        float  dist_limit = 5;

        if(one_zero_diff > dist_limit)
            seg_window = ~ seg_window;
        else{
            if(one_zero_diff > -dist_limit && one_count > zero_count)
                seg_window = ~ seg_window;
        }

        seg_window.copyTo(cv::Mat( spine_th, rect));
//        imshow("spine_th", spine_th);
//        waitKey();


    }
    //去除噪声
    if (removeNoise) {
        vector<vector<cv::Point>> contours;
        imshow("spine_th", spine_th);
//        WriteData("/Users/eternity/Desktop/未命名文件夹/quantize1.txt", spine_th);
//        waitKey();
        findContours(spine_th, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);

        for (int i = 0; i < contours.size(); i ++) {
            //compute bounding rect
            cv::Rect rect = boundingRect(contours[i]);
            double bbox_aspect = rect.width / (double)rect.height;
            int bbox_area = rect.width * rect.height;
            //compute solidity
            vector<vector<Point>> hull(1);
            convexHull( contours[i], hull[0] );
            double convex_area = contourArea(hull[0]);
            double solidity = bbox_area / convex_area;

            for (int j = 0; j < contours[i].size(); j ++) {
                if ( (rect.width > spineImage.cols / 1.001)
                    || (rect.width > spineImage.cols / 1.4 && bbox_aspect > 5.0)
                    || (rect.height > spineImage.cols / 1.1)
                    || (bbox_area < pow(spineImage.cols/30, 2))
                    || (bbox_aspect > 0.5 && bbox_aspect < 1.7 && solidity > 0.9) )

                    spine_th.at<int>(contours[i][j].x, contours[i][j].y) = 0;
//                WriteData("/Users/eternity/Desktop/未命名文件夹/quantize2.txt", spine_th);
            }


        }

    }
    segSpine = spine_th;
//    transpose(segSpine, segSpine);
//    flip(segSpine, segSpine, 0);
    imshow("segspine", segSpine);
//    waitKey();
    spine_th.release();



}
//对图片进行level量化
void TextDetector::imgQuantize(cv::Mat &src, cv::Mat &dst, double level){
    dst = cv::Mat::zeros(src.rows, src.cols, CV_8U);
    for (int i = 0; i < src.rows; i ++) {
        uchar *data = src.ptr<uchar>(i);
        uchar *data2 = dst.ptr<uchar>(i);
        for (int j = 0; j < src.cols; j ++) {
            if(data[j] <= level)
                data2[j] = 1;
            else
                data2[j] = 2;

        }
    }

}
//找出最左边界处,前edgeValue个值为k的边界值
void TextDetector::findKEdgeFirst(cv::Mat &data, int edgeValue,int k,vector<int> &rows,vector<int> &cols){
    int count = 0;
    for (int i = 0; i < data.cols; i ++) {
        uchar *u = data.ptr<uchar>(i);
        for (int j = 0; j < data.rows; j ++) {
            if(edgeValue == (int)u[j]){
                if(count < k){
                    count ++;
                    cols.push_back(i);
                    rows.push_back(j);
                }

            }

        }
    }

}
//找出最右边界处,倒数edgeValue个值为k的边界值
void TextDetector::findKEdgeLast(cv::Mat &data, int edgeValue,int k,vector<int> &rows, vector<int> &cols){
    int count = 0;
    for (int i = data.cols - 1; i >= 0; i --) {
        uchar *u = data.ptr<uchar>(i);
        for (int j = data.rows - 1; j >= 0; j --) {
            if(edgeValue == (int)u[j]){
                if(count < k){
                    count ++;
                    cols.push_back(i);
                    rows.push_back(j);
                }

            }
        }

    }

}
//直方图均衡
void TextDetector::adaptiveHistEqual(cv::Mat &src,cv::Mat &dst,double clipLimit)
{
    Ptr<cv::CLAHE> clahe = createCLAHE();
    clahe->setClipLimit(clipLimit);
    clahe->apply(src, dst);
}
  • 18
    点赞
  • 92
    收藏
    觉得还不错? 一键收藏
  • 24
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值