www.pudn.com > ImageProcessing.rar > SegApi.cpp


#include "GlobalApi.h" 
#include "stdafx.h" 
#include "cdib.h" 
#include "math.h" 
#include  
#include  
using namespace std; 
 
/************************************************************************* 
 * 
 * \函数名称: 
 *   RegionSegFixThreshold() 
 * 
 * \输入参数: 
 *   CDib * pDib					- 指向CDib类的指针,含有原始图象信息 
 *   int nThreshold					- 区域分割的阈值 
 * 
 * \返回值: 
 *   无 
 * 
 * \说明: 
 *   1(逻辑)表示对应象素为前景区域,0表示背景 
 *   阈值分割的关键问题在于阈值的选取。阈值的选取一般应该视实际的应用而 
 *   灵活设定。 
 * 
 ************************************************************************* 
 */ 
void RegionSegFixThreshold(CDib * pDib, int nThreshold) 
{ 
	//遍历图象的纵坐标 
	int y; 
 
	//遍历图象的横坐标 
	int x; 
 
	//图象的长宽大小 
	CSize sizeImage		= pDib->GetDimensions(); 
	int nWidth			= sizeImage.cx		; 
	int nHeight			= sizeImage.cy		; 
 
	//图像在计算机在存储中的实际大小 
	CSize sizeImageSave	= pDib->GetDibSaveDim(); 
 
	//图像在内存中每一行象素占用的实际空间 
	int nSaveWidth = sizeImageSave.cx; 
 
	 
	//图像数据的指针 
	LPBYTE  pImageData = pDib->m_lpImage; 
 
	for(y=0; yGetDimensions(); 
	int nWidth			= sizeImage.cx		; 
	int nHeight			= sizeImage.cy		; 
 
	//图像在计算机在存储中的实际大小 
	CSize sizeImageSave	= pDib->GetDibSaveDim(); 
 
	//图像在内存中每一行象素占用的实际空间 
	int nSaveWidth = sizeImageSave.cx; 
 
	//图像数据的指针 
	LPBYTE  lpImage = pDib->m_lpImage; 
 
	// 局部阈值 
	int nThd[2][2] ; 
 
	// 子图象的平均值 
	int nLocAvg ; 
	 
	// 对左上图像逐点扫描: 
	nLocAvg = 0 ; 
	// y方向 
	for(y=0; yGetDimensions(); 
	int nWidth			= sizeImage.cx		; 
	int nHeight			= sizeImage.cy		; 
 
	// 图像在计算机在存储中的实际大小 
	CSize sizeImageSave	= pDib->GetDibSaveDim(); 
 
	// 图像在内存中每一行象素占用的实际空间 
	int nSaveWidth = sizeImageSave.cx; 
 
	// 图像数据的指针 
	LPBYTE  pImageData = pDib->m_lpImage; 
 
	// 初始化 
	for(y=0; yGetDimensions(); 
	int nWidth			= sizeImage.cx		; 
	int nHeight			= sizeImage.cy		; 
 
	// 图像在计算机在存储中的实际大小 
	CSize sizeImageSave	= pDib->GetDibSaveDim(); 
 
	// 图像在内存中每一行象素占用的实际空间 
	int nSaveWidth = sizeImageSave.cx; 
 
	// 图像数据的指针 
	LPBYTE  lpImage = pDib->m_lpImage; 
 
	// 初始化 
	for(y=0; yGetDimensions(); 
	int nWidth			= sizeImage.cx		; 
	int nHeight			= sizeImage.cy		; 
 
	// 图像在计算机在存储中的实际大小 
	CSize sizeImageSave	= pDib->GetDibSaveDim(); 
 
	// 图像在内存中每一行象素占用的实际空间 
	int nSaveWidth = sizeImageSave.cx; 
 
	// 初始化 
	memset(pUnRegion,0,sizeof(unsigned char)*nWidth*nHeight); 
 
	// 种子点 
	int nSeedX, nSeedY; 
 
	// 设置种子点为图像的中心 
	nSeedX = nWidth /2 ; 
	nSeedY = nHeight/2 ; 
 
	// 定义堆栈,存储坐标 
	int * pnGrowQueX ; 
	int * pnGrowQueY ; 
	 
	// 分配空间 
	pnGrowQueX = new int [nWidth*nHeight]; 
	pnGrowQueY = new int [nWidth*nHeight]; 
 
	// 图像数据的指针 
	unsigned char *  pUnchInput =(unsigned char * )pDib->m_lpImage; 
	 
	// 定义堆栈的起点和终点 
	// 当nStart=nEnd, 表示堆栈中只有一个点 
	int nStart ; 
	int nEnd   ; 
 
	//初始化 
	nStart = 0 ; 
	nEnd   = 0 ; 
 
	// 把种子点的坐标压入栈 
	pnGrowQueX[nEnd] = nSeedX; 
	pnGrowQueY[nEnd] = nSeedY; 
 
	// 当前正在处理的象素 
	int nCurrX ; 
	int nCurrY ; 
 
	// 循环控制变量 
	int k ; 
 
	// 图象的横纵坐标,用来对当前象素的4邻域进行遍历 
	int xx; 
	int yy; 
 
	while (nStart<=nEnd) 
	{ 
		// 当前种子点的坐标 
		nCurrX = pnGrowQueX[nStart]; 
		nCurrY = pnGrowQueY[nStart];					 
 
		// 对当前点的4邻域进行遍历 
		for (k=0; k<4; k++)	 
		{	 
			// 4邻域象素的坐标 
			xx = nCurrX+nDx[k]; 
			yy = nCurrY+nDy[k]; 
			 
			// 判断象素(xx,yy) 是否在图像内部 
			// 判断象素(xx,yy) 是否已经处理过 
			// pUnRegion[yy*nWidth+xx]==0 表示还没有处理 
 
			// 生长条件:判断象素(xx,yy)和当前象素(nCurrX,nCurrY) 象素值差的绝对值 
			if (	(xx < nWidth) && (xx>=0) && (yy=0)  
				    && (pUnRegion[yy*nWidth+xx]==0)  
					&& abs(pUnchInput[yy*nSaveWidth+xx] - pUnchInput[nCurrY*nSaveWidth+nCurrX])GetDimensions(); 
	int nWidth			= sizeImage.cx		; 
	int nHeight			= sizeImage.cy		; 
 
	//图像在计算机在存储中的实际大小 
	CSize sizeImageSave	= pDib->GetDibSaveDim(); 
 
	int nSaveWidth = sizeImageSave.cx; 
 
	//图像数据的指针 
	LPBYTE  pImageData = pDib->m_lpImage; 
 
	//初始化结果数据 
	for(n=0; nGetDimensions(); 
	int nWidth			= sizeImage.cx		; 
	int nHeight			= sizeImage.cy		; 
 
	//图像在计算机在存储中的实际大小 
	CSize sizeImageSave	= pDib->GetDibSaveDim(); 
 
	int nSaveWidth = sizeImageSave.cx; 
 
	//图像数据的指针 
	LPBYTE  pImageData = pDib->m_lpImage; 
 
	double fCosTable; 
	double fSinTable; 
	fCosTable=0 ; 
	fSinTable=0 ; 
 
	double fTmpPxValue; 
	double fRpartValue; 
	double fIpartValue; 
	fTmpPxValue=0; 
	fRpartValue=0; 
	fIpartValue=0; 
 
	for(y=0; yGetDimensions(); 
	int nWidth			= sizeImage.cx		; 
	int nHeight			= sizeImage.cy		; 
	 
	// 图像在计算机在存储中的实际大小 
	CSize sizeImageSave	= pDib->GetDibSaveDim(); 
	 
	// 图像在内存中每一行象素占用的实际空间 
	int nSaveWidth = sizeImageSave.cx; 
	 
	// 图像数据的指针 
	LPBYTE  lpImage = pDib->m_lpImage; 
	 
	// 初始化 
	for(y=0; yGetDimensions(); 
	int nWidth			= sizeImage.cx		; 
	int nHeight			= sizeImage.cy		; 
	 
	// 图像在计算机在存储中的实际大小 
	CSize sizeImageSave	= pDib->GetDibSaveDim(); 
	 
	// 图像在内存中每一行象素占用的实际空间 
	int nSaveWidth = sizeImageSave.cx; 
	 
	// 图像数据的指针 
	LPBYTE  lpImage = pDib->m_lpImage; 
	 
	// 初始化 
	for(y=0; yGetDimensions(); 
	int nWidth			= sizeImage.cx		; 
	int nHeight			= sizeImage.cy		; 
	 
	// 指向梯度数据的指针 
	double * pdGrad; 
 
	// 按照图像的大小开辟内存空间,存储梯度计算的结果 
	pdGrad=new double[nHeight*nWidth]; 
 
    // 调用Roberts算子求梯度 
	RobertsOperator(pDib, pdGrad); 
 
	// 定义当前象素梯度值 
	double dCurrGrad = 0; 
	 
	// 定义最大梯度值 
	double dMaxGrad; 
 
	// 设置初值 
	dMaxGrad = 0; 
 
	// 最大梯度值对应的象素点坐标 
	int nPx; 
	int nPy; 
 
	nPx = 0; 
	nPy = 0; 
 
	// 求梯度最大值所在的象素点坐标 
	for(y=0; y10) 
	{ 
		// 设置当前点为边界点 
		pUnEdgeTrack[nPy*nWidth + nPx] = 255 ; 
 
		dMaxGrad = 0 ; 
		for(i=0; i<8; i++) 
		{ 
			nDetX=nDx[i]; 
			nDetY=nDy[i]; 
			y = nPy + nDetY; 
			x = nPx + nDetX; 
 
			// 判断是否在图像内部 
			if(x>=0 && x=0 && y dMaxGrad)  && ( pUnEdgeTrack[y*nWidth + x] == 0) ) 
				{ 
					dMaxGrad = pdGrad[y*nWidth + x] ; 
					yy = y; 
					xx = x; 
				} 
			} 
		} 
		// 下一个边界点的梯度,横纵坐标 
		dCurrGrad = dMaxGrad ; 
		nPy = yy; 
		nPx = xx; 
	} 
 
	//释放内存 
	delete pdGrad; 
	pdGrad = NULL; 
 
} 
 
/************************************************************************* 
 * 
 * \函数名称: 
 *   MakeGauss() 
 * 
 * \输入参数: 
 *   double sigma									        - 高斯函数的标准差 
 *   double **pdKernel										- 指向高斯数据数组的指针 
 *   int *pnWindowSize										- 数据的长度 
 * 
 * \返回值: 
 *   无 
 * 
 * \说明: 
 *   这个函数可以生成一个一维的高斯函数的数字数据,理论上高斯数据的长度应 
 *   该是无限长的,但是为了计算的简单和速度,实际的高斯数据只能是有限长的 
 *   pnWindowSize就是数据长度 
 *    
 ************************************************************************* 
 */ 
void MakeGauss(double sigma, double **pdKernel, int *pnWindowSize) 
{ 
	// 循环控制变量 
	int i   ; 
	 
	// 数组的中心点 
	int nCenter; 
 
	// 数组的某一点到中心点的距离 
	double  dDis  ;  
 
	double PI = 3.14159; 
	// 中间变量 
	double  dValue;  
	double  dSum  ; 
	dSum = 0 ;  
	 
	// 数组长度,根据概率论的知识,选取[-3*sigma, 3*sigma]以内的数据。 
	// 这些数据会覆盖绝大部分的滤波系数 
	*pnWindowSize = 1 + 2 * ceil(3 * sigma); 
	 
	// 中心 
	nCenter = (*pnWindowSize) / 2; 
	 
	// 分配内存 
	*pdKernel = new double[*pnWindowSize] ; 
	 
	for(i=0; i< (*pnWindowSize); i++) 
	{ 
		dDis = (double)(i - nCenter); 
		dValue = exp(-(1/2)*dDis*dDis/(sigma*sigma)) / (sqrt(2 * PI) * sigma ); 
		(*pdKernel)[i] = dValue ; 
		dSum += dValue; 
	} 
	 
	// 归一化 
	for(i=0; i<(*pnWindowSize) ; i++) 
	{ 
		(*pdKernel)[i] /= dSum; 
	} 
} 
 
/************************************************************************* 
 * 
 * \函数名称: 
 *   GaussianSmooth() 
 * 
 * \输入参数: 
 *   unsigned char * pUnchImg				- 指向图象数据的指针 
 *   int nWidth											- 图象数据宽度 
 *   int nHeight										- 图象数据高度 
 *   double dSigma									- 高斯函数的标准差 
 *   unsigned char * pUnchSmthdImg	- 指向经过平滑之后的图象数据 
 * 
 * \返回值: 
 *   无 
 * 
 * \说明: 
 *   为了抑止噪声,采用高斯滤波对图象进行滤波,滤波先对x方向进行,然后对 
 *   y方向进行。 
 *    
 ************************************************************************* 
 */ 
void GaussianSmooth(unsigned char *pUnchImg, int nWidth, int nHeight,  
										double sigma, unsigned char * pUnchSmthdImg) 
{ 
	// 循环控制变量 
  int y; 
	int x; 
	 
	int i; 
	 
	// 高斯滤波器的数组长度 
	 
	int nWindowSize; 
	 
	//  窗口长度的1/2 
	int	nHalfLen;    
	 
	// 一维高斯数据滤波器 
	double *pdKernel ; 
	 
	// 高斯系数与图象数据的点乘 
	double  dDotMul     ; 
	 
	// 高斯滤波系数的总和 
	double  dWeightSum     ;           
   
	// 中间变量 
	double * pdTmp ; 
	 
	// 分配内存 
	pdTmp = new double[nWidth*nHeight]; 
	 
	// 产生一维高斯数据滤波器 
	// MakeGauss(sigma, &dKernel, &nWindowSize); 
	MakeGauss(sigma, &pdKernel, &nWindowSize) ; 
	 
	// MakeGauss返回窗口的长度,利用此变量计算窗口的半长 
	nHalfLen = nWindowSize / 2; 
	 
  // x方向进行滤波 
	for(y=0; y= 0  && (i+x) < nWidth ) 
				{ 
					dDotMul += (double)pUnchImg[y*nWidth + (i+x)] * pdKernel[nHalfLen+i]; 
					dWeightSum += pdKernel[nHalfLen+i]; 
				} 
			} 
			pdTmp[y*nWidth + x] = dDotMul/dWeightSum ; 
		} 
	} 
	 
	// y方向进行滤波 
	for(x=0; x= 0  && (i+y) < nHeight ) 
				{ 
					dDotMul += (double)pdTmp[(y+i)*nWidth + x] * pdKernel[nHalfLen+i]; 
					dWeightSum += pdKernel[nHalfLen+i]; 
				} 
			} 
			pUnchSmthdImg[y*nWidth + x] = (unsigned char)(int)dDotMul/dWeightSum ; 
		} 
	} 
 
	// 释放内存 
	delete []pdKernel; 
	pdKernel = NULL ; 
	 
	delete []pdTmp; 
	pdTmp = NULL; 
} 
 
 
/************************************************************************* 
 * 
 * \函数名称: 
 *   DirGrad() 
 * 
 * \输入参数: 
 *   unsigned char *pUnchSmthdImg					- 经过高斯滤波后的图象 
 *   int nWidht														- 图象宽度 
 *   int nHeight      										- 图象高度 
 *   int *pnGradX                         - x方向的方向导数 
 *   int *pnGradY                         - y方向的方向导数 
 * \返回值: 
 *   无 
 * 
 * \说明: 
 *   这个函数计算方向倒数,采用的微分算子是(-1 0 1) 和 (-1 0 1)'(转置) 
 *   计算的时候对边界象素采用了特殊处理 
 *    
 *    
 ************************************************************************* 
 */ 
void DirGrad(unsigned char *pUnchSmthdImg, int nWidth, int nHeight, 
										 int *pnGradX , int *pnGradY) 
{ 
	// 循环控制变量 
	int y ; 
	int x ; 
	 
	// 计算x方向的方向导数,在边界出进行了处理,防止要访问的象素出界 
	for(y=0; y abs(gx))  
				{ 
					// 计算插值的比例 
					weight = fabs(gx)/fabs(gy);  
 
					g2 = pnMag[nPos-nWidth] ;  
					g4 = pnMag[nPos+nWidth] ; 
					 
					// 如果x,y两个方向的方向导数的符号相同 
					// C是当前象素,与g1-g4的位置关系为: 
					//	g1 g2  
					//		 C          
					//		 g4 g3  
					if (gx*gy > 0)  
					{ 					 
						g1 = pnMag[nPos-nWidth-1] ; 
						g3 = pnMag[nPos+nWidth+1] ; 
					}  
 
					// 如果x,y两个方向的方向导数的符号相反 
					// C是当前象素,与g1-g4的位置关系为: 
					//	   g2 g1 
					//		 C          
					//	g3 g4   
					else  
					{  
						g1 = pnMag[nPos-nWidth+1] ; 
						g3 = pnMag[nPos+nWidth-1] ; 
					}  
				} 
				 
				// 如果方向导数x分量比y分量大,说明导数的方向更加“趋向”于x分量 
				// 这个判断语句包含了x分量和y分量相等的情况 
				else 
				{ 
					// 计算插值的比例 
					weight = fabs(gy)/fabs(gx);  
					 
					g2 = pnMag[nPos+1] ;  
					g4 = pnMag[nPos-1] ; 
					 
					// 如果x,y两个方向的方向导数的符号相同 
					// C是当前象素,与g1-g4的位置关系为: 
					//	g3    
					//	g4 C g2        
					//       g1 
					if (gx*gy > 0)  
					{				 
						g1 = pnMag[nPos+nWidth+1] ; 
						g3 = pnMag[nPos-nWidth-1] ; 
					}  
					// 如果x,y两个方向的方向导数的符号相反 
					// C是当前象素,与g1-g4的位置关系为: 
					//	     g1 
					//	g4 C g2        
					//  g3      
					else  
					{  
						g1 = pnMag[nPos-nWidth+1] ; 
						g3 = pnMag[nPos+nWidth-1] ; 
					} 
				} 
 
				// 下面利用g1-g4对梯度进行插值 
				{ 
					dTmp1 = weight*g1 + (1-weight)*g2 ; 
					dTmp2 = weight*g3 + (1-weight)*g4 ; 
					 
					// 当前象素的梯度是局部的最大值 
					// 该点可能是个边界点 
					if(dTmp>=dTmp1 && dTmp>=dTmp2) 
					{ 
						pUnchRst[nPos] = 128 ; 
					} 
					else 
					{ 
						// 不可能是边界点 
						pUnchRst[nPos] = 0 ; 
					} 
				} 
			} //else 
		} // for 
 
	} 
}  
 
/************************************************************************* 
 * 
 * \函数名称: 
 *   TraceEdge() 
 * 
 * \输入参数: 
 *   int    x									- 跟踪起点的x坐标  
 *   int    y									- 跟踪起点的y坐标 
 *   int nLowThd							- 判断一个点是否为边界点的低阈值 
 *   unsigned char *pUnchEdge - 记录边界点的缓冲区 
 *   int *pnMag               - 梯度幅度图 
 *   int nWidth               - 图象数据宽度 
 * 
 * \返回值: 
 *   无 
 * 
 * \说明: 
 *   递归调用   
 *   从(x,y)坐标出发,进行边界点的跟踪,跟踪只考虑pUnchEdge中没有处理并且 
 *   可能是边界点的那些象素(=128),象素值为0表明该点不可能是边界点,象素值 
 *   为255表明该点已经被设置为边界点,不必再考虑 
 *    
 *    
 ************************************************************************* 
 */ 
void TraceEdge (int y, int x, int nLowThd, unsigned char *pUnchEdge, int *pnMag, int nWidth)  
{  
	// 对8邻域象素进行查询 
	int xNb[8] = {1, 1, 0,-1,-1,-1, 0, 1} ; 
	int yNb[8] = {0, 1, 1, 1,0 ,-1,-1,-1} ; 
 
	int yy ; 
	int xx ; 
	 
	int k  ; 
	 
	for(k=0; k<8; k++) 
	{ 
		yy = y + yNb[k] ; 
		xx = x + xNb[k] ; 
		// 如果该象素为可能的边界点,又没有处理过 
		// 并且梯度大于阈值 
		if(pUnchEdge[yy*nWidth+xx] == 128  && pnMag[yy*nWidth+xx]>=nLowThd) 
		{ 
			// 把该点设置成为边界点 
			pUnchEdge[yy*nWidth+xx] = 255 ; 
 
			// 以该点为中心进行跟踪 
			TraceEdge(yy, xx, nLowThd, pUnchEdge, pnMag, nWidth); 
		} 
	} 
}  
 
/************************************************************************* 
 * 
 * \函数名称: 
 *   EstimateThreshold() 
 * 
 * \输入参数: 
 *   int *pnMag               - 梯度幅度图 
 *	 int nWidth               - 图象数据宽度 
 *	 int nHeight              - 图象数据高度 
 *   int *pnThdHigh           - 高阈值 
 *   int *pnThdLow            - 低阈值 
 *	 double dRatioLow         - 低阈值和高阈值之间的比例 
 *	 double dRatioHigh        - 高阈值占图象象素总数的比例 
 *   unsigned char *pUnchEdge - 经过non-maximum处理后的数据 
 * 
 * \返回值: 
 *   无 
 * 
 * \说明: 
 *   经过non-maximum处理后的数据pUnchEdge,统计pnMag的直方图,确定阈值。 
 *   本函数中只是统计pUnchEdge中可能为边界点的那些象素。然后利用直方图, 
 *   根据dRatioHigh设置高阈值,存储到pnThdHigh。利用dRationLow和高阈值, 
 *   设置低阈值,存储到*pnThdLow。dRatioHigh是一种比例:表明梯度小于 
 *   *pnThdHigh的象素数目占象素总数目的比例。dRationLow表明*pnThdHigh 
 *   和*pnThdLow的比例,这个比例在canny算法的原文里,作者给出了一个区间。 
 * 
 ************************************************************************* 
 */ 
void EstimateThreshold(int *pnMag, int nWidth, int nHeight, int *pnThdHigh,int *pnThdLow,  
											 unsigned char * pUnchEdge, double dRatioHigh, double dRationLow)  
{  
	// 循环控制变量 
	int y; 
	int x; 
	int k; 
	 
	// 该数组的大小和梯度值的范围有关,如果采用本程序的算法,那么梯度的范围不会超过pow(2,10) 
	int nHist[1024] ; 
 
	// 可能的边界数目 
	int nEdgeNb     ; 
 
	// 最大梯度值 
	int nMaxMag     ; 
 
	int nHighCount  ; 
 
	nMaxMag = 0     ;  
	 
	// 初始化 
	for(k=0; k<1024; k++)  
	{ 
		nHist[k] = 0;  
	} 
 
	// 统计直方图,然后利用直方图计算阈值 
	for(y=0; y= nThdHigh)) 
				{ 
					// 设置该点为边界点 
					pUnchEdge[nPos] = 255; 
					TraceEdge(y, x, nThdLow, pUnchEdge, pnMag, nWidth); 
				} 
      } 
   } 
 
	 // 那些还没有被设置为边界点的象素已经不可能成为边界点 
   for(y=0; y