OCR字符识别(三)--- bounding框相交合并

//  版权所有:yuansanwan
// 完成日期:20150417
// 如有借鉴,请告知,谢谢!
// 编译环境: vs2013+opencv2.4.10
         大家好,今天我们来讨论OCR识别中针对连通域提取的boundingRect相交的问题。 首先我们要明确两个rect相交合并的必要性。在我们做二值化的过程中,阈值的选取很难避免针对某一个图像过大或者过小。阈值过大,就会出现文字被拆的很碎的情况;阈值过小,噪声也会增加。所以为了改善bounding分割比较碎的情况,我们将相交的bounding进行合并。举个例子:
                                            
我们看到 “次”字由于是左右结构,有两个连通域;“与”字上下结构,也有两个连通域。如果我们将这样的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!


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值