//版权所有:yuanjingwei
//完成日期:20150415
//文章内容:关于数学公式中箭头与字母的拆分
//利益相关: 如果借鉴,麻烦请回复告知。谢谢!
//注: 如果能够帮到大家,真心很高兴!
//编译环境:vs2013+opencv2.4.10
hello 大家好! 我们接着讨论关于OCR字符预处理的相关事情。 本文主要想讲一下,数学公式中箭头与字母拆分的问题。
其实对于OCR预处理的一些情况,我们大部分都是根据图片中的字符规则来剔除坏的badcase,得到我们想要的字符。在实际情况中,我们会遇到如下情况:
我们可以看到二值化后 F与箭头相连,这样会影响后面字符识别的结果。所以我们在前期处理的过程中要把这样类型的badcase去掉。
做法其实挺简单,根据适当的字符规则,进行剔除。 通过对比多个badcase 我们发现这是一类问题,于是具体步骤如下:
0: 原图二值化,bounding后如下:
1:将图像二值化后,找到符合规则的箭头粘连框, 这里我利用了bounding框的宽高比,设定了一个上下阈值 up=2.0 down=0.6 即:
double upT = 2.0;
double downT = 0.6;
double rateWToH = (double)(*itb).width / (double)(*itb).height;
if (rateWToH > downT&&rateWToH < upT&&(*itb).height>20)
当然这个规则不是强规则,我们只能剔除一部分。木有关系,我们慢慢来嘛! 第二个规则来啦,不过理想情况下,我们会得到:
2: 找到箭头bounding , 通过观察看出,箭头在bounding框上面三分之一处(这是一个规律),所以我们只取上部分 这样会得到:
这样我们就得到箭头啦。可是 稍安勿躁,这可是理想的情况嘛,不理想的会咋样呢,嘿嘿,我确信的告诉你 啥都有。有一个规则就是,我们在拆掉箭头与字符的同时不能对其他文字产生影响,所以我们必须接着利用规则剔除掉噪声。讲到这里,是不是很罗嗦呀,那就忍着点吧。
3:对2中的图像进行行统计,统计0像素的个数 ,找出最大值,且要求值大于 整列的四分之三同时索引行不能大于整行的二分之一。紧接着我们会在最大值索引行到最后一行上求出最小值,判断最小值与最大值的关系以及最大值索引行与最小值索引行之间的关系,这部分应该是一个强规则,会去掉很多噪声。
4:我们接着罗列规则 根据箭头的特点,我们将2图按列分为4个部分,这样最后一部分会包含箭头的头部,其他三部分都会是箭头的尾巴。计算四部分0像素的个数,根据0的数量就能排除最后一部分。 那么剩下的就是我们要的箭头啦。
5: 找到箭头,我们就找到了分割线,分割粘连部分就可以搞定啦。以下是结果图:
哦了,我们任务就完成啦!
具体代码实现如下:(仅供参考哦)
void CAdhension::ArrowSegmentation(cv::Mat src, cv::vector > &contours)
{
CV_Assert(src.data!=0);
//bounding
cv::vector >::iterator itc = contours.begin();
cv::vector bounding;
cv::Mat dst;
cv::cvtColor(src,dst,CV_GRAY2BGR);
cv::vector > segContours; //存贮分割后的contours
for (; itc != contours.end(); itc++)
{
cv::Rect r = cv::boundingRect((*itc));
bounding.push_back(r);
cv::rectangle(dst,r,cv::Scalar(0,0,255));
}
cv::vector::iterator itb = bounding.begin();
double upT = 2.0;
double downT = 0.6;
for (; itb != bounding.end(); itb++)
{
double rateWToH = (double)(*itb).width / (double)(*itb).height;
if (rateWToH > downT&&rateWToH < upT&&(*itb).height>20)//高度大于20个像素
{
//符合条件进行箭头分离
cv::Rect r;
r.x = 0;
r.y = 0;
r.width = (*itb).width;
r.height = (int)ceil((*itb).height*0.35);
cv::Mat imgArrow(cv::Size(r.width, r.height), CV_8UC1);
src((*itb))(r).copyTo(imgArrow);
//先找每行黑点极值点
cv::vector countPoint;
cv::vector countValue;
for (int i = 0; i < imgArrow.rows; i++)
{
int count = cv::countNonZero(imgArrow(cv::Range(i,i+1),cv::Range::all()));
countValue.push_back(imgArrow.cols - count);
}
int indexMax = 0;
int max = 0;
maxVector(countValue, indexMax, max);
if (max < 0.80 * imgArrow.cols || indexMax>0.6 * imgArrow.rows)
{
continue; //很明显不符合箭头条件
}
else//在排除其他不符合条件的
{
cv::vector cx;
int indexMin = 0;
int min = 0;
for (int k = indexMax; k < imgArrow.rows; k++)
{
cx.push_back(countValue[k]);
}
minVector(cx,indexMin,min);//最小值也是我们的分割点
if (indexMax + indexMin<0.5 * imgArrow.rows || min>max/5.0) //排除条件
{
continue;
}
else
{
//接着排除利用像素间关系
//分为四块
cv::Rect r;
r.width = imgArrow.cols / 4;
r.height = imgArrow.rows;
r.y = 0;
cv::vector cb;
for (int k = 0; k < 4; k++)
{
r.x = k*imgArrow.cols / 4;
int count = cv::countNonZero(imgArrow(r));
cb.push_back((imgArrow(r).rows*imgArrow(r).cols) - count);
}
if (cb[3]>cb[2]*1.5 && cb[3] > cb[1]*1.5 && cb[3] > cb[0]*1.5)
{
int cndex = std::max(cb[0], std::max(cb[2], cb[1]));
if (abs(cb[2] - cb[1]) < cndex / 2.0 && abs(cb[2] - cb[0]) < cndex / 2.0 && abs(cb[1] - cb[0]) < cndex / 2.0)
{
//符合最后条件的为箭头然后分开
cv::Rect r0, r1;
cv::vector cont1;
int segLine = indexMax + indexMin;
r0.x = (*itb).x;
r0.y = (*itb).y;
r0.width = (*itb).width;
r0.height = segLine;
r1.x = (*itb).x ;
r1.y = (*itb).y + segLine;
r1.height = (*itb).height - segLine;
r1.width = (*itb).width;
cv::imshow("0", src(r0));
cv::imshow("1", src(r1));
cv::waitKey();
}
}
}
}
}
}
}