1

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

提前致谢!

4

2 回答 2

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

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

于 2013-09-08T21:13:56.017 回答
1

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

1- 具有非常低值的二进制阈值

2-找到所有轮廓并列出它们的区域。用白色填充小轮廓。

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

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

形态学操作将帮助您从斑点(数字)中切出小肢体(点)。

于 2013-09-08T23:54:22.877 回答