字符分割(二)

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;
}


效果圖:

上一篇代碼的效果圖:


可以看到,本文的代碼擦除最後面的小矩形框。原圖見上一篇博客。

發佈了44 篇原創文章 · 獲贊 29 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章