【问题标题】:huge dot pattern on text removal文本删除上的大点图案
【发布时间】:2013-09-08 15:41:37
【问题描述】:

我正在尝试提取文本以便我们对其进行 OCR 处理,但这些点会增加很多噪音。 图片:http://img22.imageshack.us/img22/1344/l0ap.png

提前致谢!

【问题讨论】:

    标签: opencv filter noise-reduction


    【解决方案1】:

    我认为这看起来是一个有趣的问题,MSER blob 检测和修复可以解决。以下是我尝试过的一些代码;但我认为 OCR 输入的结果不可接受;但是,无论如何我都将它包括在内,以防它可能被证明是有用的。修复并没有以我希望的方式将字符的轮廓线延伸到蒙版区域。我认为一个更有前途的方法如下

    • 计算图像梯度幅度
    • 阈值梯度
    • 将高于阈值的点视为曲线点的候选点
    • 上曲率界限低于点的最小曲率(比如点的平均曲率以下 3-6 个标准偏差)的跟踪曲线,这样就不会在点的边界上生成候选曲线,只有字母的暴露部分。这是基于观察到点具有高曲率而字符边界段在拐角之间具有低曲率。
    • 用低阶多项式外推和相交候选曲线。
    • 检测和连接曲线段的循环
    • 在白色背景上用实心黑色对任何闭合曲线进行光栅化
    • 传递给 OCR。

    MSER+修复尝试:

    //Find blobs, assuming the image to repair is in a cv::Mat text
    cv::Mat grey;
    cv::cvtColor(text, grey, CV_RGB2GRAY);
    //MSER has an easier time finding these dots if there are more pixels to work with
    cv::resize(grey, grey, cv::Size(text.cols*2, text.rows*2), cv::INTER_CUBIC);
    cv::blur(grey, grey, cv::Size(3, 3));
    int delta = 1;
    int minPixels = 5;
    int maxPixels = 400;
    float maxVariation = 0.4;
    float minDiversity = 0.1;
    cv::MSER detector(delta, minPixels, maxPixels, maxVariation, minDiversity);
    std::vector<std::vector<cv::Point> > blobs;
    detector(grey, blobs);
    
    //Find the radius of each blob
    cv::Mat radii((int)blobs.size(), 1, CV_64F);
    for (int i = 0; i < blobs.size(); i++)
    {
        cv::Point2f center; //not used
        float rad;
        cv::minEnclosingCircle(blobs[i], center, rad);
        radii.at<double>(i, 0) = (double)rad;
    }
    
    //Build a Gaussian mixture histogram
    cv::TermCriteria criteria;
    criteria.maxCount = 500;
    criteria.type = cv::TermCriteria::COUNT;
    cv::EM model = cv::EM(4, cv::EM::COV_MAT_DIAGONAL, criteria);
    model.train(radii);
    
    //Get the stats for each Gauss peak in the Gaussian mixture model
    cv::Mat weights = model.get<cv::Mat>("weights");
    cv::Mat means = model.get<cv::Mat>("means");
    vector<cv::Mat> covs = model.get< vector<cv::Mat> >("covs");
    
    
    //Identify the heaviest peak to use as the classifier for dots
    float heaviestPeakWeight = 0;
    int heaviestPeakId = -1;
    for (int i = 0; i < weights.size().width; i++)
    {
        if (weights.at<double>(0, i) > heaviestPeakWeight)
        {
            heaviestPeakWeight = weights.at<double>(0, i);
            heaviestPeakId = i;
        }
    }
    
    //Classify the blobs by their radius, we make the assumption
    //that because the dots are more numerous than other features
    //and that their size is uniform, they should cause a sharp
    //peak in the histogram
    const double Sqrt2Pi = sqrt(2*M_PI);
    std::vector<int> blobsInHeaviest;
    blobsInHeaviest.reserve(blobs.size());
    for (int i = 0; i < radii.rows; i++)
    {
        //For each radius find the strongest Gauss peak it lies under
        double maxClassVal = 0;
        int maxClassId = -1;
        double x = radii.at<double>(i, 0);
        for (int j = 0; j < weights.size().width; j++)
        {
            double mean = means.at<double>(0, j);
            double variance = covs[j].at<double>(0, 0);
            double weight = weights.at<double>(0, j);
    
            double classVal =   weight*exp(-pow((x - mean), 2)/(2*variance))/
                                (Sqrt2Pi*variance);
            if (classVal >= maxClassVal)
            {
                maxClassVal = classVal;
                maxClassId = j;
            }
        }
        if (maxClassId == heaviestPeakId)
        {
            blobsInHeaviest.push_back(i);
        }
    }
    
    //Rasterize the blobs to create an inpaint mask to remove the dots
    cv::Mat mask(grey.size(), CV_8UC1, cv::Scalar(0));
    for(int i = 0; i < blobsInHeaviest.size(); i++)
    {
        for (int j = 0; j < blobs[blobsInHeaviest[i]].size(); j++)
        {
            mask.at<uchar>(blobs[blobsInHeaviest[i]][j]) = 255;
        }
    }
    
    //Prior to inpainting we need to ensure the mask components cover
    //the white lines between the dots and the letters. MSER makes them
    //a bit undersized so we can use morphological dilate to make them
    //a little larger.
    double scale = means.at<double>(0, heaviestPeakId);
    cv::dilate(mask, mask,
               cv::getStructuringElement(cv::MORPH_RECT,
                                         cv::Size(rint(3*scale),rint(3*scale))));
    cv::inpaint(grey, mask, grey, rint(1.7*scale), cv::INPAINT_NS);
    
    //Dynamic thresholding could be accomplished by exploiting the assumption
    //that the histogram for an image of text will have a step like histogram
    //use a step detection algorithm and threshold at the step location. I
    //did not do this because thresholding is not the problem with this approach.
    cv::threshold(grey, grey, 130, 255, cv::THRESH_BINARY);
    cv::resize(grey, grey, cv::Size(text.cols/2, text.rows/2));
    //Finished result is in grey
    

    编辑:混合曲率和弯曲半径。

    【讨论】:

      【解决方案2】:

      您在这里已经有了很好的解决方案,但我仍然想添加另一种方法。

      1- 二进制阈值非常低

      2- 找到所有轮廓并列出它们的areasFill白色小轮廓。

      3-尝试OCR,如果它没有给你一个数字答案,添加更多的预处理:

      while(!ocr)    
      {    
      do morphological closing,
      do morphological opening,
      fill small blobs with white,
      try ocr.
      }
      

      morphological operations 将帮助你从你的斑点(数字)中剪下小肢体(点)。

      【讨论】:

        猜你喜欢
        • 2021-12-15
        • 2018-08-24
        • 1970-01-01
        • 1970-01-01
        • 2016-12-03
        • 2013-10-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多