字符紧缩对齐

     经过归一化处理的字符在图像图像中排列没有规律,这给后续的字符样本提取增加了工作量。因此,这里还要对字符进行缩紧和对齐。

      代码如下:

/****************************************************************
功能:      对目标(如字符)进行缩紧和对齐,归一化之后使用
参数:      img:输入图像
                dst:输出对齐后的图像
				vector<RECT> vecRT:归一化之后所得的 字符分割的矩形框
注    :      只能处理二值图像
返回值:   返回分割字符的矩形框
***************************************************************/
vector<RECT> Ctry::ObjectAlign(IplImage* src, IplImage* dst, vector<RECT> vecRT)
{
	int nNormW, nNormH;      //归一化之后统一矩形的宽、高
	if (vecRT.size() > 0)
	{
		nNormW = vecRT[0].right - vecRT[0].left + 1;
		nNormH = vecRT[0].bottom - vecRT[0].top + 1;
	}

	int nSrc_i, nSrc_j;   //映射源坐标
	RECT  rt;
	vector<RECT> rtNEW;
	rtNEW.clear();
	rtNEW.resize(vecRT.size());
	int nMargin = 2;   //紧缩对齐后,图像留出左、上边界距离

	//依次调整每个轮廓矩形区域的位置
	for (int nObj = 0; nObj < vecRT.size(); nObj++)
	{
		rt = vecRT[nObj];
		//紧缩对齐后的轮廓矩形
		rtNEW[nObj].left = nObj*nNormW + nMargin;    //左边界
		rtNEW[nObj].right = (nObj + 1)*nNormW + nMargin;
		rtNEW[nObj].top = 0 + nMargin;    //上边界
		rtNEW[nObj].bottom = nNormH + nMargin;

		vecRT[nObj] = rtNEW[nObj];
        //将原矩形框内的像素映射到新的矩形框中
		for (int i = 0; i < nNormH; i++)
		{
			for (int j = nObj*nNormW; j < (nObj + 1)*nNormW; j++)
			{
				//计算映射源坐标
				nSrc_i = rt.top + i;
				nSrc_j = rt.left + j - nObj*nNormW;

				double		pixel = cvGetReal2D(src, nSrc_i, nSrc_j);
				cvSetReal2D(dst, i+nMargin, j+nMargin, pixel);
			}
		}
	}
	return  rtNEW;
}

完整的调用改程序的代码:( void Ctry::OnTryPath() //消息响应函数,函数从这里开始)


/****************************************************************
功能:      对前景目标(如字符)进行划分,将各个字符的轮廓矩形返回 
参数:      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;
}


void Ctry::OnTryPath()   //消息响应函数,函数从这里开始
{
	// TODO:  在此添加命令处理程序代码    
	IplImage* img = cvLoadImage("C:\\Users\\Administrator\\Desktop\\dst.jpg", -1);

	vector<RECT>  vecRECTBig, vecNew;
	vecRECTBig = ObjectSegment(img, 260);

	IplImage *norm = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
	IplImage *dst1 = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
	for (int i = 0; i < norm->height; i++)
	{
		for (int j = 0; j < norm->width; j++)
		{
			cvSetReal2D(norm, i, j, 255);
			cvSetReal2D(dst1, i, j, 255);
		}
	}

	for (int i = 0; i < vecRECTBig.size(); i++)
	{
		RECT rt = vecRECTBig[i];    //取得一个矩形轮廓
		vecRECTBig[i] = RgnZoom(img, norm, 60, 100, rt);   //将norm图对应的点设为黑色,返回缩放后的矩形框
	}

	vecNew = ObjectAlign(norm, dst1, vecRECTBig);

	for (int i = 0; i < vecRECTBig.size(); i++)
	{
		int x = vecNew[i].left - 1;
		int y = vecNew[i].top - 1;
		int x1 = vecNew[i].right + 1;
		int y1 = vecNew[i].bottom + 1;
		CvPoint pt1(x, y);
		CvPoint pt2(x1, y1);
		cvRectangle(dst1, pt1, pt2, CV_RGB(255, 0, 0), 1);
	}
	cvSaveImage("C:\\Users\\Administrator\\Desktop\\tar.jpg", dst1);
}


/****************************************************************
功能:       字符归一化
参数:        img:有待归一化的图像
                  dst:归一化后目标图像
				  int nTargWidth:归一化的目标宽度
				  int nTargHeight:归一化的目标高度
				  RECT lpRect:等待缩放的矩形框
注    :       只能处理二值图像
返回值:   缩放后的矩形框
***************************************************************/
RECT  Ctry::RgnZoom(IplImage* img, IplImage* dst, int nTargWidth, int nTargHeight, RECT lpRect)
{
	RECT retRT;   //缩放后的区域矩形
	double dXScale;   //水平方向缩放因子
	double dYScale;   //竖直方法缩放因子

    //确定缩放系数
	dXScale = (double)nTargWidth / (lpRect.right - lpRect.left + 1);
	dYScale = (double)nTargHeight / (lpRect.bottom - lpRect.top + 1);

	int nSrc_i, nSrc_j;   //映射源坐标

	retRT.top = lpRect.top;
	retRT.bottom = retRT.top + nTargHeight;
	retRT.left = lpRect.left;
	retRT.right = retRT.left + nTargWidth;

   //对图像的区域矩形进行逐行扫描,通过像素映射完成缩放
	for (int  i = retRT.top; i < retRT.bottom; i++)
	{
		for (int j = retRT.left; j < retRT.right; j++)
		{
			//计算映射的源坐标(最邻近插值)
			nSrc_i = retRT.top + int((i - retRT.top) / dYScale);
			nSrc_j = retRT.left + int((j - retRT.left) / dXScale);

			//对应像素赋值
			double pixel = cvGetReal2D(img, nSrc_i, nSrc_j);
			cvSetReal2D(dst, i, j, pixel);
		}
	}
	return retRT;
}




/****************************************************************
功能:      对目标(如字符)进行缩紧和对齐,归一化之后使用
参数:      img:输入图像
                dst:输出对齐后的图像
				vector<RECT> vecRT:归一化之后所得的 字符分割的矩形框
注    :      只能处理二值图像
返回值:   返回分割字符的矩形框
***************************************************************/
vector<RECT> Ctry::ObjectAlign(IplImage* src, IplImage* dst, vector<RECT> vecRT)
{
	int nNormW, nNormH;      //归一化之后统一矩形的宽、高
	if (vecRT.size() > 0)
	{
		nNormW = vecRT[0].right - vecRT[0].left + 1;
		nNormH = vecRT[0].bottom - vecRT[0].top + 1;
	}

	int nSrc_i, nSrc_j;   //映射源坐标
	RECT  rt;
	vector<RECT> rtNEW;
	rtNEW.clear();
	rtNEW.resize(vecRT.size());
	int nMargin = 2;   //紧缩对齐后,图像留出左、上边界距离

	//依次调整每个轮廓矩形区域的位置
	for (int nObj = 0; nObj < vecRT.size(); nObj++)
	{
		rt = vecRT[nObj];
		//紧缩对齐后的轮廓矩形
		rtNEW[nObj].left = nObj*nNormW + nMargin;    //左边界
		rtNEW[nObj].right = (nObj + 1)*nNormW + nMargin;
		rtNEW[nObj].top = 0 + nMargin;    //上边界
		rtNEW[nObj].bottom = nNormH + nMargin;

		vecRT[nObj] = rtNEW[nObj];
        //将原矩形框内的像素映射到新的矩形框中
		for (int i = 0; i < nNormH; i++)
		{
			for (int j = nObj*nNormW; j < (nObj + 1)*nNormW; j++)
			{
				//计算映射源坐标
				nSrc_i = rt.top + i;
				nSrc_j = rt.left + j - nObj*nNormW;

				double		pixel = cvGetReal2D(src, nSrc_i, nSrc_j);
				cvSetReal2D(dst, i+nMargin, j+nMargin, pixel);
			}
		}
	}
	return  rtNEW;
}

原图:效果图:(归一化效果图)

紧缩对齐效果图:



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值