字符分割(二)

ANN在训练和识别时都只能将一个单独的数字作为样本,因此对于扫描图像中的多个连续数字需要进行分割。具体算法如下:

      1,确定图像中字符的大致高度范围:先自下而上对图像进行逐行扫描,直到遇到第一个黑素像素,记下行号,然后自上而下对图像进行逐行扫描,直到遇到第一个黑素像素,记下行号。这两个行号就标志出了字符大致的高度范围。

       2,确定每个字符的左起始和右终止位置:在第一步得到的高度范围内进行自左向右逐列扫描,遇到第一个黑色像素时,认为是字符分割的起始位,然后继续扫描,直到遇到有一列中没有黑色像素,认为是这个字符的右终止位置,准备开始进行下一个字符的分割。按照上述方法继续扫描,直到扫描到图像的最右端。这样就得到了每个字符的比较精确的快读范围。

      3,在已知的每个字符比较精确的宽度范围内,再按照第一步的方法,分别自下而上和自上而下,逐行扫描,来获取每个字符精确的高度范围。

     本文较前一篇字符分割的区别在于,增加了对矩形框的筛选功能,并对不需要的矩形框中的字符可以擦除。代码如下:

/****************************************************************
功能:      对前景目标(如字符)进行划分,将各个字符的轮廓矩形返回 
参数:      img:输入等待分割的图像
            int areaThreshold:面积阈值,当分割后的字符小于这个值时,会擦除该字符,用于去除噪音等小区域杂点干扰
注    :   只能处理二值图像
返回值:   返回分割字符的矩形框
***************************************************************/
vector<RECT> Ctry::ObjectSegment(IplImage* img, int areaThreshold)
{
	vector<RECT> vecRoughRECT;    //粗略对象轮廓的矩形向量数组    
	vector<RECT> vecRECT;         //精化后对象轮廓的矩形向量数组    
	vector<RECT> vecRECTBig;     //存放精化对象区域中大的的矩形框    
	//清空用来表示每个对象的vector    
	vecRoughRECT.clear();
	vecRECT.clear();
	vecRECTBig.clear();

	int nTop, nBttom;   //整体前景区域的上下边界    
	int nObjCnt = 0;      //对象数目    

	//从上向下扫描,找到整体区域的前景的上边界    
	for (int i = 0; i < img->height; i++)
	{
		for (int j = 0; j < img->width; j++)
		{
			double pixel = cvGetReal2D(img, i, j);
			if (int(pixel) == 0)
			{
				nTop = i;
				i = img->height;   //对i赋大值,使得在break跳出内存循环后,直接在跳出外层循环    
				break;
			}
		}
	}

	//从下向上扫描,找到整体区域的前景的下边界    
	for (int i = img->height - 1; i >= 0; i--)
	{
		for (int j = 0; j < img->width; j++)
		{
			double pixel = cvGetReal2D(img, i, j);
			if (int(pixel) == 0)
			{
				nBttom = i;
				i = -1;   //对i赋小值,使得在break跳出内存循环后,直接在跳出外层循环    
				break;
			}
		}
	}

	bool bStartSeg = false;      //是否已经开始某一个对象的分割    
	bool bBlackInCol;             //某一列中是否包含黑色像素    

	RECT rt;

	//按列扫描,找到每一个目标的左右边界    
	for (int j = 0; j < img->width; j++)
	{
		bBlackInCol = false;
		for (int i = 0; i < img->height; i++)
		{
			double pixel = cvGetReal2D(img, i, j);
			if (int(pixel) == 0)
			{
				bBlackInCol = true;     //该列中发现黑点    
				if (!bStartSeg)  //还没有进入一个对象的分割    
				{
					bStartSeg = true;//进入一个对象的分割    
					rt.left = j;
				}
				else
					break;
			}
		}
		if (j == (img->width - 1))   //扫描到最后一列了,说明整个图像扫描完毕    
		{
			break;
		}
		//正处在分割状态,且扫描完一列都没有发现黑像素,表明当前对象分割完毕    
		if (bStartSeg && !bBlackInCol)
		{
			rt.right = j;     //对象右边界确定    

			//对象的粗略上下边界(有待精化)    
			rt.top = nTop;
			rt.bottom = nBttom;
			::InflateRect(&rt, 1, 1);  //矩形框膨胀一个像素,以免绘制时压到字符    
			vecRoughRECT.push_back(rt);   //插入vector    
			bStartSeg = false;   //当前分割结束    
			nObjCnt++;   //对象数目加1    
		}
	}

	RECT rtNEW;     //存放精化对象区域的矩形框    
	//由于得到了精确的左右边界,现在可以精化矩形框的上下边界    
	int nSize = vecRoughRECT.size();
	for (int nObj = 0; nObj < nSize; nObj++)
	{
		rt = vecRoughRECT[nObj];
		rtNEW.left = rt.left - 1;
		rtNEW.right = rt.right + 1;
		//从上向下逐行扫描边界    
		for (int i = rt.top; i < rt.bottom; i++)
		{
			for (int j = rt.left; j < rt.right; j++)
			{
				double pixel = cvGetReal2D(img, i, j);
				if (int(pixel) == 0)
				{
					rtNEW.top = i - 1;
					//对i赋大值,使得在break跳出内存循环后,直接在跳出外层循环    
					i = rt.bottom;
					break;
				}
			}
		}

		//从下向上逐行扫描边界    
		for (int i = rt.bottom - 1; i > rt.top; i--)
		{
			for (int j = rt.left; j < rt.right; j++)
			{
				double pixel = cvGetReal2D(img, i, j);
				if (int(pixel) == 0)
				{
					rtNEW.bottom = i + 1;
					//对i赋小值,使得在break跳出内存循环后,直接在跳出外层循环    
					i = rt.top - 1;
					break;
				}
			}
		}
		vecRECT.push_back(rtNEW);
	}

	//提取较大的框,擦除小的矩形框  
	for (int i = 0; i < vecRECT.size(); i++)
	{
		int x = vecRECT[i].left;
		int y = vecRECT[i].top;
		int x1 = vecRECT[i].right;
		int y1 = vecRECT[i].bottom;
		int area = (x1 - x)*(y1 - y);   //矩形的面积  
		//当面积小于阈值时,擦除当前矩形框中的前景,否则将当前矩形框压入新的vector  
		if (area < areaThreshold)
		{
			for (int i = y; i < y1 + 1; i++)
			{
				for (int j = x; j < x1 + 1; j++)
				{
					cvSetReal2D(img, i, j, 255);
				}
			}
		}
		else
		{
			vecRECTBig.push_back(vecRECT[i]);
		}
	}

	//画矩形框,显示分割字符    
	for (int i = 0; i < vecRECTBig.size(); i++)
	{
		int x = vecRECTBig[i].left - 1;
		int y = vecRECTBig[i].top - 1;
		int x1 = vecRECTBig[i].right + 1;
		int y1 = vecRECTBig[i].bottom + 1;
		CvPoint pt1(x, y);
		CvPoint pt2(x1, y1);
		cvRectangle(img, pt1, pt2, CV_RGB(255, 0, 0), 1);
	}
	cvSaveImage("C:\\Users\\Administrator\\Desktop\\rect0.jpg", img);	
	return vecRECTBig;
}


效果图:

上一篇代码的效果图:


可以看到,本文的代码擦除最后面的小矩形框。原图见上一篇博客。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值