字符緊縮對齊

     經過歸一化處理的字符在圖像圖像中排列沒有規律,這給後續的字符樣本提取增加了工作量。因此,這裏還要對字符進行縮緊和對齊。

      代碼如下:

/****************************************************************
功能:      對目標(如字符)進行縮緊和對齊,歸一化之後使用
參數:      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;
}

原圖:效果圖:(歸一化效果圖)

緊縮對齊效果圖:



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章