我正在尝试提取文本以便我们对其进行 OCR 处理,但这些点会增加很多噪音。图片:http: //img22.imageshack.us/img22/1344/l0ap.png
提前致谢!
我正在尝试提取文本以便我们对其进行 OCR 处理,但这些点会增加很多噪音。图片:http: //img22.imageshack.us/img22/1344/l0ap.png
提前致谢!
我认为这看起来是一个有趣的问题,MSER blob 检测和修复可以解决。以下是我尝试过的一些代码;但我认为 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
编辑:混合曲率和弯曲半径。