// 版权所有:yuansanwan
// 完成日期:20150417
// 如有借鉴,请告知,谢谢!
// 编译环境: vs2013+opencv2.4.10
大家好,今天我们来讨论OCR识别中针对连通域提取的boundingRect相交的问题。 首先我们要明确两个rect相交合并的必要性。在我们做二值化的过程中,阈值的选取很难避免针对某一个图像过大或者过小。阈值过大,就会出现文字被拆的很碎的情况;阈值过小,噪声也会增加。所以为了改善bounding分割比较碎的情况,我们将相交的bounding进行合并。举个例子:
![](https://img-blog.csdn.net/20150417153451342?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2Fud2FuZG91amlhbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
我们看到 “次”字由于是左右结构,有两个连通域;“与”字上下结构,也有两个连通域。如果我们将这样的rect去识别,效果肯定会很差。如果我们对他们进行了bounding合并,效果就会立竿见影。好吧。我们来看结果:
大家看到结果了吧。我们将两个bounding连在了一起,这样效果就会很好啦。
下面讲讲实现过程。
1 首先我们要判断两个rect的相交程度,这是判断两个rect是否合并的条件。这里我写死了程序 当两个rect相交比例大于小rect面积的0.2时,就合并。 当然这是一个经验值,可以修改
2 接着我们先写一个rect合并的函数:这个比较简单
cv::Rect RectMerge(const cv::Rect rect1, const cv::Rect rect2)
{
cv::Rect rectM;
rectM.x = std::min(rect1.x, rect2.x);
rectM.y = std::min(rect1.y, rect2.y);
rectM.width = std::max(rect1.x + rect1.width, rect2.x + rect2.width) - rectM.x;
rectM.height = std::max(rect1.y + rect1.height, rect2.y + rect2.height) - rectM.y;
return rectM;
}
3 我们需要一个判断两个rect相交程度的函数,注意 我们返回值为void 相交程度写在参数里面 用得是引用
<pre name="code" class="cpp">void RectOverLapCoefficient(cv::Rect rect1, cv::Rect rect2, double & rate)
{
CV_Assert(rect1.area() != 0 && rect2.area() != 0);
rate = 0.0;
int flag = RectOverLap(rect1, rect2);
if (flag == false)
{
rate = 0.0;
return;
}
cv::Rect rectMerge;
rectMerge = RectMerge(rect1, rect2);
cv::Mat dst(cv::Size(rectMerge.width + rectMerge.x, rectMerge.height + rectMerge.y), CV_8UC1, cv::Scalar(0));
int maxArea = rect1.area() > rect2.area() ? rect1.area() : rect2.area();
if (maxArea == rect1.area())
{
dst(rect2) = cv::Scalar::all(0);
dst(rect1) = cv::Scalar::all(127);
int count = countNonZero(dst(rect2));
rate = (double)count / (double)rect2.area();
}
else
{
dst(rect1) = cv::Scalar::all(0);
dst(rect2) = cv::Scalar::all(127);
int count = countNonZero(dst(rect1));
rate = (double)count / (double)rect1.area();
}
}
int RectOverLap(const cv::Rect rect1, const cv::Rect rect2)
{
//先算包含情况
int x1 = rect1.x;
int x2 = rect2.x;
int y1 = rect1.y;
int y2 = rect2.y;
int width1 = rect1.width;
int width2 = rect2.width;
int height1 = rect1.height;
int height2 = rect2.height;
if (x2 > x1 + width1 || x2 + width2 < x1 || y2 + height2 < y1 || y2 > y1 + height1)
{
return false;
}
if (x1 > x2 + width2 || x1 + width1 < x2 || y1 + height1 < y2 || y1 > y2 + height2)
{
return false;
}
return true;
}
4 接着 大餐来啦! rect相与函数:这里我们将俩个相与的rect保留了下来,没有删掉。在编写函数过程中,遇到一个问题,就是在遍历数组过程中,删掉没用的,这可是一个危险的行为。大家尽量不要去做。 将删掉的东西存为数组, 单独遍历进行删除。
void BoundingMerge(cv::vector<cv::vector<cv::Point> > &contours)
{
CV_Assert(contours.size()!=0);
//画连通域bounding
cv::vector<cv::Rect> bounding;
cv::vector<cv::vector<cv::Point> >::iterator itCon = contours.begin();
for (; itCon != contours.end(); itCon++)
{
cv::Rect r = cv::boundingRect((*itCon));
bounding.push_back(r);
}
double rate=0.0;
//bounding指针 三层循环
cv::vector<cv::Rect>::iterator itc = bounding.begin();
cv::vector<cv::Rect>::iterator itd;
cv::vector<cv::Rect>::iterator ite = bounding.begin();
//连通域指针
cv::vector<cv::vector<cv::Point> >::iterator itcC = contours.begin();
cv::vector<cv::vector<cv::Point> >::iterator itdC;
cv::vector<cv::vector<cv::Point> >::iterator iteC;
cv::vector<cv::vector<cv::Point> > cont;
double threshold = 0.2;
//记录第三层循环的rect
cv::vector<cv::Rect> rectIte;//记录ite的rect
int flagItc = 0;
int flagItd = 0;
while (itc != bounding.end()&&itcC!=contours.end())
{
flagItc = 0;
itd = bounding.begin();
itdC = contours.begin();
while (itd != bounding.end()&&itdC!=contours.end())
{
flagItd = 0;
if (itc == itd)
{
itd++;
itdC++;
continue;
}
for (int k = 0; k<rectIte.size(); k++)
{
if ((*itc) == rectIte[k])
{
flagItc = 1;
break;
}
if ((*itd) == rectIte[k])
{
flagItd = 1;
break;
}
}
if (flagItc == 1)
{
break;
}
else if (flagItd ==1)
{
itd++;
itdC++;
continue;
}
//计算重叠率
RectOverLapCoefficient((*itc), (*itd), rate);
if (rate>threshold)
{
cv::Rect r;
cv::vector<cv::Point> c;
//合并rect
r= RectMerge((*itc), (*itd));
int sizeItcC = (*itcC).size();
int sizeItdC = (*itdC).size();
for (int k = 0; k < sizeItcC + sizeItdC; k++)
{
if (k < (*itcC).size())
{
c.push_back((*itcC)[k]);
}
else
{
c.push_back((*itdC)[k - sizeItcC]);
}
}
//判断其他点是否与合并后的rect相交
ite = bounding.begin();
iteC = contours.begin();
while (ite != bounding.end()&&iteC!=contours.end())
{
if (ite == itc || ite == itd)
{
ite++;
iteC++;
continue;
}
rate = 0.0;
RectOverLapCoefficient(r, (*ite), rate);
if (rate > threshold)
{
//记录ite
rectIte.push_back((*ite));
r = RectMerge(r, (*ite));
for (int k = 0; k<(*iteC).size(); k++)
{
c.push_back((*iteC)[k]);
}
}
ite++;
iteC++;
}
cont.push_back(c);
}
itd++;
itdC++;
}
itc++;
itcC++;
}
//添加合并后的rect,不删,添加在后面
for (int k = 0; k < cont.size(); k++)
{
contours.push_back(cont[k]);
}
}
哦啦!本讲就到这里吧!最后来张励志的图!激励下自己!
季后赛来啦! go spurs! go!