www.pudn.com > VC_ImageProcessing.rar > Motion.cpp, change:1996-11-04,size:11759b


#include "GlobalApi.h" 
#include "stdafx.h" 
#include "cdib.h" 
#include "math.h" 
/************************************************************************* 
 * 
 * \函数名称: 
 *   GetFileName() 
 * 
 * \输入参数: 
 *   CString		strFilePathName		- 图象的文件名  
 *   int		nCurFrameNum		- 当前帧的图象文件名 
 * 
 * \返回值: 
 *   CString			- 返回给定帧数的图象文件名 
 * 
 * \说明: 
 *   该函数根据指定文件路径名和当前图象序列的帧数获取图象文件名 
 *   该函数中需要注意的是,只能读取0-999帧图象,图象为bmp格式,且按照 
 *   帧数数字进行存储,例如第一帧图象文件名为×××001.bmp,第33帧图象 
 *   的文件名为×××033.bmp。如果不是bmp文件,则返回"NULL"。 
 * 
 ************************************************************************* 
 */ 
CString GetFileName(CString strFilePathName, int nCurFrameNum) 
{ 
	//文件的路径名 
	CString strTempFileName; 
 
	int nNumPos=strFilePathName.Find("."); 
	if(nNumPos==-1){ 
		AfxMessageBox("Please choose a bmp file"); 
		return "NULL"; 
	} 
 
	//表示去掉了扩展名和数字标号的路径名,在这里,限定帧数为0~999,所以采用三位来表示 
	CString strFileNameNoExtNoNum=strFilePathName.Left(nNumPos-3); 
 
	//表示标号的字符串 
	CString strTempNum; 
 
	if(nCurFrameNum<10){ 
		strTempNum.Format("00%d",nCurFrameNum); 
	} 
	else { 
		if(nCurFrameNum<100 &&nCurFrameNum>=10){ 
			strTempNum.Format("0%d",nCurFrameNum); 
		} 
		else{ 
			strTempNum.Format("%d",nCurFrameNum); 
		} 
	} 
	 
	// 得到图象文件名 
	strTempFileName=strFileNameNoExtNoNum+strTempNum+".bmp"; 
	 
	// 返回 
	return strTempFileName; 
} 
 
/************************************************************************* 
 * 
 * \函数名称: 
 *   LoadDibSeq() 
 * 
 * \输入参数: 
 *   CString		strFilePath		- 第一帧图象的文件名  
 *   int		nCurFrameNum		- 当前帧的图象文件名 
 *   int		nTotalFrameNum		- 进行检测的图象帧数 
 *   CDib*		pDib			- 指向返回CDib类的指针 
 * 
 * \返回值: 
 *   BOOL			- 成功则返回TRUE,否则返回FALSE 
 * 
 * \说明: 
 *   该函数根据指定文件路径名和当前图象序列的帧数读取图象数据道pDib中 
 *   该函数中需要注意的是,只能读取0-999帧图象,图象为bmp格式,且按照 
 *   帧数数字进行存储,例如第一帧图象文件名为×××001.bmp,第33帧图象 
 *   的文件名为×××033.bmp。 
 * 
 ************************************************************************* 
 */ 
BOOL LoadDibSeq(CString strFilePath, int nCurFrameNum, int nTotalFrameNum, CDib* pDib) 
{ 
	//一般来讲,程序在处理的过程中需要装载的帧号应该是由外界指定的 
	//当指定的帧号不合法时,就装载当前帧作为默认值 
	if (nCurFrameNum<1 || nCurFrameNum>nTotalFrameNum) 
	{ 
		AfxMessageBox("Invalidate file frame number"); 
		return FALSE; 
	} 
 
	// 获得当前帧的图象文件名 
	CString strTempFileName; 
	strTempFileName=GetFileName(strFilePath,nCurFrameNum); 
	 
	CFile fileOpen=NULL; 
 
	// 打开文件并读取 
	fileOpen.Open(strTempFileName,CFile::modeRead); 
	if(pDib->Read(&fileOpen)==FALSE){ 
		AfxMessageBox("can not open the file "+strTempFileName); 
		return FALSE; 
	} 
	return TRUE; 
} 
 
 
/************************************************************************* 
 * 
 * \函数名称: 
 *   BinaFrameDiff() 
 * 
 * \输入参数: 
 *   unsigned char*	pUnchImg1		- 图象的文件名  
 *   unsigned char*	pUnchImg2		- 当前帧的图象文件名 
 *   int		nWidth 
 *   int		nHeight 
 *   unsigned char*	pUnchResult 
 *   int		nThreshold 
 * 
 * \返回值: 
 *   CString			- 返回给定帧数的图象文件名 
 * 
 * \说明: 
 *   该函数比较pUnchImg1和pUnchImg2两个区域中的内容,如果两个区域内      
 *容的差值的绝对值比Threshold大,则将pUnchResult相应的元素设置为逻辑值1, 
 *用灰度255表示,否则为0,并用灰度0表示 
 * 
 ************************************************************************* 
 */ 
void BinaFrameDiff(unsigned char *pUnchImg1, unsigned char *pUnchImg2,  
		  int nWidth, int nHeight, unsigned char * pUnchResult, 
		  int nThreshold) 
{ 
	int nTemp=0; 
	for (int i=0;i<nHeight*nWidth;i++) { 
			nTemp = abs(pUnchImg1[i] - pUnchImg2[i]);			 
			pUnchResult[i] = nTemp > nThreshold ? 255:0; 
	}  
	return ; 
} 
 
/************************************************************************* 
 * 
 * \函数名称: 
 *   ErodeFrameDiff() 
 * 
 * \输入参数: 
 *   unsigned char*	pUnchImg1		- 图象数据指针 
 *   int		nWidth			- 图象宽度 
 *   int		nHeight			- 图象高度 
 *   int		nErodeHalfWin		- 腐蚀窗口大小的一半 
 *   unsigned char*	pUnchResult		- 结果数据制止 
 *   int		nThreshold		- 阈值 
 * 
 * \返回值: 
 *   无 
 * 
 * \说明: 
 *   该函数进行腐蚀操作,形态学操作对pUnchImg中的每一点,计算这一点对应的 
 *窗口内的一些参数,然后根据参数结果给这个点设置相应的值. 功能上相当于广义滤波	 
 * 
 ************************************************************************* 
 */ 
void ErodeFrameDiff(unsigned char *pUnchImg, int nWidth, int nHeight, int nErodeHalfWin,  
	  int nErodeThreshold, unsigned char *pUnchResult) 
{ 
	// 搜索整个图象,对图象进行腐蚀处理 
	for (int i=nErodeHalfWin;i<nHeight-nErodeHalfWin;i++) { 
		for (int j=nErodeHalfWin;j<nWidth-nErodeHalfWin;j++) { 
			// 如果帧间的差不为0才进行处理 
			if (pUnchImg[i*nWidth+j] != 0)  
			{ 
				int iPointCount = 0; 
 
				// 根据此点的邻域判断此点是否需要删除 
				for (int r=-nErodeHalfWin;r<=nErodeHalfWin;r++) { 
					for (int c=-nErodeHalfWin;c<=nErodeHalfWin;c++) { 
						if (pUnchImg[(i+r)*nWidth+j+c] != 0) { 
							iPointCount++; 
						} 
					} 
				} 
 
				// 如果邻域中不为0的个数小于设定的阈值,则强行设置为0 
				if (iPointCount < nErodeThreshold) { 
					pUnchResult[i*nWidth+j] = 0; 
				} 
				else { 
					pUnchResult[i*nWidth+j] = 255; 
				} 
			} 
			else  
			{ 
				pUnchResult[i*nWidth+j] = 0; 
			} 
		} 
	} 
	return ; 
} 
 
/************************************************************************* 
 * 
 * \函数名称: 
 *   GetBackground() 
 * 
 * \输入参数: 
 *   CString		strFilePath		- 第一帧图象的文件名  
 *   int		nTotalFrameNum		- 进行检测的图象帧数 
 *   int		nImageWidth		- 图象宽度 
 *   int		nImageHeight		- 图象高度 
 *   unsigned char *	pUnchBackGround		- 指向返回背景数据的指针 
 * 
 * \返回值: 
 *   BOOL			- 成功则返回TRUE,否则返回FALSE 
 * 
 * \说明: 
 *   该函数根据指定文件名的图象序列求取静止背景 
 * 
 ************************************************************************* 
 */ 
BOOL GetBackground(CString strFilePath, int nTotalFrameNum, int nImageWidth,  
		   int nImageHeight, unsigned char* pUnchBackGround) 
{ 
	// 如果此时背景已经生成,函数返回,不需要再一次计算 
	/*if (pUnchBackGround!=NULL){		 
		return TRUE; 
	}*/ 
 
	 
	// pUnchTemp1和pUnchTemp2用来计算相邻两帧之间的帧差 
	// 每次只要读入一帧即可,即:假设刚刚比较k-1和k帧,那么现在比较k和 
	// k+1帧,那么k帧是不需要重新读入的 
	unsigned char* pUnchTemp1; 
	unsigned char* pUnchTemp2; 
 
	pUnchTemp1 = new unsigned char[nImageWidth * nImageHeight * sizeof(unsigned char)]; 
	pUnchTemp2 = new unsigned char[nImageWidth * nImageHeight * sizeof(unsigned char)]; 
	 
	// 临时存放图象数据的CDib指针 
	CDib* pDibTemp; 
	pDibTemp = new CDib; 
 
	// 读出第一帧数据并放入pDibTemp 
	pDibTemp->Empty(); 
	if(!LoadDibSeq(strFilePath,1,nTotalFrameNum,pDibTemp)){ 
		return FALSE; 
	} 
 
	// 然后将数据取出,存放在pUnchTemp1中 
	memcpy(pUnchTemp2,pDibTemp->m_lpImage,nImageWidth*nImageHeight*sizeof(unsigned char));	 
 
	// pChResultAfterMor 是用来记录帧间变化的内存区域 
	unsigned char * pUnchTrackBox = new unsigned char[(nTotalFrameNum)* 
		nImageWidth*nImageHeight*sizeof(unsigned char)]; 
 
	unsigned int index = 0; 
 
	// 帧间差的区域,二进制 
	unsigned char *pUnchTemp3=new unsigned char[nImageWidth*nImageHeight*sizeof(unsigned char)]; 
 
	// 腐蚀之后的区域,二进制 
	unsigned char * pUnchResultAfterMor = new unsigned char[nImageWidth*nImageHeight*sizeof(unsigned char)]; 
 
	// 对每一帧进行比较 
	for (int i = 2; i<nTotalFrameNum-1; i++)	{	 
		// 打开第i帧图象文件,并将图象存放在CDib对象pDibTemp中 
		pDibTemp->Empty(); 
		if(!LoadDibSeq(strFilePath , i , nTotalFrameNum , pDibTemp)){ 
			return FALSE; 
		} 
 
		// 然后将数据取出,存放在pUnchTemp2中 
		memcpy(pUnchTemp2,pDibTemp->m_lpImage,nImageWidth*nImageHeight);	 
 
		// 对图象帧差进行二值化处理,并将二值化后的图象存放在pUnchTemp3中 
		BinaFrameDiff(pUnchTemp1,pUnchTemp2 ,nImageWidth,nImageHeight,pUnchTemp3,10); 
 
		// 对二值化后的图象进行腐蚀处理,在这里对腐蚀窗口的大小设置为2,阈值为7 
		ErodeFrameDiff(pUnchTemp3,nImageWidth,nImageHeight,2,7,pUnchResultAfterMor); 
 
		// 将此二值化后的程序放入pUnchTrackBox的相应位置		 
		memcpy(pUnchTrackBox+index,pUnchResultAfterMor,sizeof(unsigned char)*nImageWidth*nImageHeight); 
 
		// 计算图象数据在pUnchTrackBox中的偏移量 
		index = index + nImageWidth*nImageHeight*sizeof(unsigned char); 
		 
		// 每做完两帧之间的比较,就使帧号下移一个,pUnchTemp1中是存k帧内容,pUnchTemp2帧是存k+1 
		// 帧内容,所以,每次只要把pUnchTemp2中的内容给pTemp1,而pTemp2重新读入既可以了. 
		unsigned char* pUnchTag = NULL; 
 
		pUnchTag   = pUnchTemp1; 
		pUnchTemp1 = pUnchTemp2; 
		pUnchTemp2 = pUnchTag; 
	} 
	 
	// 释放已分配内存 
	delete []pUnchTemp1;	 
	pUnchTemp1 = NULL; 
	delete []pUnchTemp2;	 
	pUnchTemp2 = NULL;	 
	delete []pUnchTemp3;	 
	pUnchTemp3 = NULL; 
	delete []pUnchResultAfterMor;	 
	pUnchResultAfterMor=NULL; 
	 
	// 每一帧的大小 
	int nFrameSize = nImageWidth * nImageHeight * sizeof(unsigned char); 
	 
	// 记录最大长度	 
	int * pnTrackSegLen = new int [nImageWidth*nImageHeight]; 
 
	// 记录最大长度中的中间帧标号 
	int * pnTrackSegFrame = new int [nImageWidth*nImageHeight]; 
 
	// 对每一个象素点跟踪最大为0的长度,并将最大长度中的中间帧标号记录下来 
	for (int y = 0; y<nImageHeight; y++)	{ 
		for (int x = 0; x<nImageWidth; x++)	{ 
			// 此象素在一帧那相对于该帧第一个元素的偏移量 
			int offset = y * nImageWidth + x; 
 
			// 初始化最大长度 
			int largeLen = 0; 
			int t = 1; 
 
			// 连续为0的段的开始和结束帧标号 
			int segStart,segEnd; 
 
			// 跟踪长度 
			int segLen; 
 
			// 当前帧数 
			int frameNum; 
			 
			segLen = 0; 
			frameNum = 1; 
 
			// 遍历整个序列,跟踪并记录此点连续为0的最大长度 
			while (t < nTotalFrameNum - 1)	{ 
				// 如果还没有到达序列结束并且此点的不为0,则继续到下一帧搜索 
				while ((t < nTotalFrameNum -1) && (pUnchTrackBox[t*nFrameSize+offset] != 0))	 
					t++; 
 
					//如果此时的t>= nTotalFrameNum - 1,则说明,已经遍历到最后一帧了 
					if (t >= nTotalFrameNum - 1)	 
						break; 
 
					// 此时应为此长度的开始 
					segStart = t; 
 
					while ((t < nTotalFrameNum - 1) && (pUnchTrackBox[t*nFrameSize+offset] == 0))	 
						t++;						 
 
					// 此长度的结束帧标号 
					segEnd = t - 1; 
 
					// 获得此连续为0的帧的长度 
					segLen = segEnd +1 -segStart;	 
 
					// 判断是否为最大长度,是则进行替换 
					if (segLen > largeLen)	{ 
						largeLen = segLen; 
						frameNum = (segEnd + segStart)/2;				 
					} 
				}  
				 
				pnTrackSegLen[offset] = largeLen; 
				pnTrackSegFrame[offset] = frameNum; 
		} 
	} 
 
	delete []pUnchTrackBox;	pUnchTrackBox=NULL; 
	 
	// 因为对每个象素而言,背景可能出现在不同帧里,此时需要把所有帧调入内存 
	unsigned char* pBuffer = new unsigned char[nTotalFrameNum*(nImageWidth*nImageHeight)]; 
 
	for (int k=1; k<nTotalFrameNum; k++)	{ 
		pDibTemp->Empty(); 
		LoadDibSeq(strFilePath , k , nTotalFrameNum , pDibTemp); 
 
		// 然后将数据取出,存放在pBuffer的相应位置中 
		memcpy(pBuffer+k*nFrameSize,pDibTemp->m_lpImage,nImageWidth*nImageHeight);	 
	} 
 
	// 遍历整个图象,设置背景数据 
	for (y=0; y<nImageHeight; y++)	{ 
		for (int x=0; x<nImageWidth; x++)	{ 
			// 获得此象素点在一帧数据中的偏移量 
			int k = y * nImageWidth + x;	 
 
			// 获得此象素点连续为0的最大长度的中间帧标号 
			int nFrameMax = pnTrackSegFrame [k]; 
 
			// 设置具有最大长度的中间帧的数据为背景数据 
			pUnchBackGround[k] = pBuffer[(nFrameMax*nFrameSize)+k]; 
		} 
	}		 
 
	delete []pnTrackSegLen;  
	pnTrackSegLen=NULL; 
	delete []pnTrackSegFrame;	 
	pnTrackSegFrame=NULL; 
	delete []pBuffer;  
	pBuffer=NULL; 
	 
	::SetCursor(::LoadCursor(NULL, IDC_ARROW)); 
	 
	return TRUE; 
}