文本删除上的巨大圆点图案

时间:2013-09-08 15:41:38

标签: opencv filter noise-reduction

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

提前致谢!

2 个答案:

答案 0 :(得分: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

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

答案 1 :(得分:1)

你已经有了一个很好的解决方案,但我还想添加另一种方法。

1-二进制阈值,值非常低

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

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

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

morphological operations将帮助你从斑点(数字)中切出小四肢(点)。