www.pudn.com > hmmPlatform.rar > VQ.cpp


#include "StdAfx.h" 
#include ".\vq.h" 
#include  
 
////////////////////////////////////////////////////////////////////////// 
// 构造函数 
//  
// 创建人:		陈文凯 
// 创建日期:	2005-06-07 
// 修改人: 
// 修改日期: 
CVQ::CVQ(void) 
{ 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 析造函数 
//  
// 创建人:		陈文凯 
// 创建日期:	2005-06-07 
// 修改人: 
// 修改日期: 
CVQ::~CVQ(void) 
{ 
} 
 
//////////////////////////////////////////////////////////////////////////// 
//// 采用LGB算法进行VQ 
////  
//// 创建人:		陈文凯 
//// 创建日期:	2005-06-07 
//// 修改人: 
//// 修改日期: 
//void CVQ::LGB(  
//			  const double* pInVector,		// 输入样本序列 
//			  unsigned int nInLen,			// 输入样本序列长度 
//			  const double* pInCodeBook,	// 输入码本,可为空 
//			  double* pOutCodeBook,			// 输出码本 
//			  unsigned int nCodeNums,		// 码本长度 
//			  unsigned int nMaxReapt,		// 最大迭代次数 
//			  double fMinChange,			// 畸变改进阈值 
//			  double fInitDistortion		// 初始畸变 
//			  ) 
//{ 
//	// 当前总畸变 
//	double fCurDistortion = fInitDistortion; 
//	// 调用LGBRepeat返回的总畸变 
//	double fRetDistortion = 0; 
//	// 当前迭代次数 
//	unsigned int nCurRepeat = 0; 
//	// 当前畸变改进量 
//	double fCurChange = fInitDistortion; 
// 
//	// 若初始码本为空,则用输入序列的前nCodeNums个样本为初始码本 
//	if (pInCodeBook == NULL) 
//	{ 
//		memcpy(pOutCodeBook, pInVector, sizeof(double) * nCodeNums); 
//	} 
// 
//	// 调用LGBRepeat进行迭代 
//	while (nCurRepeat < nMaxReapt && fMinChange < fCurChange) 
//	{ 
//		fRetDistortion = CVQ::LGBRepeat(pInVector, nInLen, pOutCodeBook, nCodeNums); 
// 
//		// 计算当前畸变改进量 
//		fCurChange = abs(fCurDistortion - fRetDistortion) / fCurDistortion; 
//		fCurDistortion = fRetDistortion; 
//	} 
//} 
// 
//////////////////////////////////////////////////////////////////////////// 
//// 进行LGB算法迭代,输出总畸变 
////  
//// 创建人:		陈文凯 
//// 创建日期:	2005-06-07 
//// 修改人: 
//// 修改日期: 
//double CVQ::LGBRepeat(  
//					const double* pInVector,		// 输入样本序列 
//					unsigned int nInLen,			// 输入样本序列长度 
//					double* pCodeBook,				// 输入/出码本 
//					unsigned int nCodeNums			// 码本长度 
//					) 
//{ 
//	// 聚类时每个码矢代表的样本数 
//	unsigned int pnSampleNums[nCodeNums]; 
//	// 聚类时进行样本叠加值 
//	double pfSampleSums[nCodeNums]; 
//	// 保存总畸变 
//	double fDistortion = 0; 
//	// 保存相邻的码矢下标 
//	unsigned int nNearCode = 0; 
//	// 保存样本与码矢距离 
//	double fDistance = 0; 
// 
//	// 初始化聚类时每个码矢代表的样本数 
//	memset(pnSampleNums, 0, sizeof(unsigned int) * nCodeNums); 
//	// 初始化聚类时进行样本叠加值 
//	memset(pfSampleSums, 0, sizeof(double) * nCodeNums); 
// 
//	// 按照输入码本对输入样本序列进行分段 
//	for (int i = 0; i < nInLen; i++) 
//	{ 
//		fDistance = CVQ_MAXIMUN; 
// 
//		for(int j = 0; j < nCodeNums; j++) 
//		{ 
//			if (abs(pInVector[i] - pCodeBook[j]) < fDistance) 
//			{ 
//				fDistance = abs(pInVector[i] - pCodeBook[j]); 
// 
//				nNearCode = j; 
//			} 
//		} 
// 
//		pfSampleSums[nNearCode] += pInVector[i]; 
//		pnSampleNums[nNearCode] ++; 
//	} 
// 
//	// 计算总畸变 
//	for (int i = 0; i < nCodeNums; i++) 
//	{ 
//		for (int j = 0; j < pnSamplesNums) 
//	} 
// 
//	// 计算码本 
// 
//	return fDistortion; 
//} 
 
////////////////////////////////////////////////////////////////////////// 
// 对输入样本进行简单聚类 
// 先对输入样本进行分段,然后简单的求每个分段的均值作为码本 
// 产生的码本与时间t相关 
// 
// 创建人:		陈文凯 
// 创建日期:	2005-06-07 
// 修改人: 
// 修改日期: 
void CVQ::EasyCluster( 
					  const double* pInVector,		// 输入样本序列 
					  unsigned int nInLen,			// 输入样本序列长度 
					  double* pCodeBook,			// 输入/出码本 
					  unsigned int nCodeNums		// 码本长度 
					  ) 
{ 
	unsigned int nSegLen = nInLen / nCodeNums; 
	unsigned int nOffSet = 0; 
 
	// 初始化码本 
	memset(pCodeBook, 0, sizeof(double) * nCodeNums); 
 
	for (unsigned int i = 0; i < nCodeNums; i++) 
	{ 
		nOffSet = i * nSegLen; 
 
		for (unsigned int j = 0; j < nSegLen; j++) 
		{ 
			pCodeBook[i] += pInVector[nOffSet + j]; 
		} 
 
		// 对码矢求均值 
		pCodeBook[i] /= nSegLen; 
	} 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 实现K均值聚类 
// 
// 创建人:		陈文凯 
// 创建日期:	2005-06-07 
// 修改人: 
// 修改日期: 
void CVQ::KMeansCluster( 
						const double* pInVector,		// 输入样本序列 
						unsigned int nInLen,			// 输入样本序列长度 
						double* pCodeBook,				// 输出码本 
						unsigned int nCodeNums			// 码本长度 
						) 
{ 
	// pInVector(i)是否属于pCodeBook(j) 
	unsigned int* pFlag = NULL; 
	// 迭代中使用到的码本 
	double* pNewCodeBook = NULL; 
 
	// 循环变量 
	unsigned int i = 0; 
	unsigned int j = 0; 
	// 迭代返回的畸变 
	double fDistortion = 0; 
 
	// 对码本进行升序排序 
	double fMinCode = 0; 
	unsigned int nMinIndex = 0; 
 
	if (pInVector != NULL && pCodeBook != NULL) 
	{ 
		// 分配化标志矩阵 
		pFlag = new unsigned int[nCodeNums * nInLen]; 
		// 分配迭代码本 
		pNewCodeBook = new double[nCodeNums]; 
		 
		// 使用EasyCluster设定初始码本 
		CVQ::EasyCluster(pInVector, nInLen, pCodeBook, nCodeNums); 
		for (i = 0; i < nCodeNums; i++) 
		{ 
			TRACE("%f\n", pCodeBook[i]); 
		} 
 
		// 迭代聚类 
		fDistortion = 1; 
		while (fDistortion > 0) 
		{ 
			fDistortion = CVQ::KMeansClusterRepeat( 
				pInVector, nInLen, pCodeBook, nCodeNums, pFlag, pNewCodeBook); 
 
			// 拷贝新码本 
			memcpy(pCodeBook, pNewCodeBook, sizeof(double) * nCodeNums); 
 
			for (i = 0; i < nCodeNums; i++) 
			{ 
				TRACE("%f\n", pCodeBook[i]); 
			} 
			TRACE("#####################\n"); 
		} 
 
		// 对码本进行升序排序 
		for (i = 0; i < nCodeNums; i++) 
		{ 
			fMinCode = pCodeBook[i]; 
			nMinIndex = i; 
 
			for (j = i + 1; j < nCodeNums; j++) 
			{ 
				if (pCodeBook[j] < fMinCode) 
				{ 
					fMinCode =  pCodeBook[j]; 
					nMinIndex = j; 
				} 
			} 
 
			pCodeBook[nMinIndex] = pCodeBook[i]; 
			pCodeBook[i] = fMinCode; 
		} 
 
		// 释放所占资源 
		delete[] pNewCodeBook; 
		delete[] pFlag; 
	} 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 实现K均值聚类的迭代过程, 输出码书的畸变程度 
// 
// 创建人:		陈文凯 
// 创建日期:	2005-06-12 
// 修改人: 
// 修改日期: 
double CVQ::KMeansClusterRepeat( 
								const double* pInVector,		// 输入样本序列 
								unsigned int nInLen,			// 输入样本序列长度 
								double* pCodeBook,				// 输入码本 
								unsigned int nCodeNums,			// 码本长度 
								unsigned int* pFlag,			// 用于分类的数组,再KMeansCluster中分配 
								double* pNewCodeBook			// 输出新码本 
								) 
{ 
	// 畸变 
	double fDistortion = 0; 
	// 样本和码本的最小距离 
	double fMinDis = 0; 
	// 计算样本和码本的距离 
	double fDis = 0; 
	// 最小距离对应的码本下标 
	unsigned int nIndex = 0; 
 
	// 码本包含样本数 
	unsigned int nSamplesCount = 0; 
	// 码本中样本累积 
	double fSamplesSum = 0; 
 
	// 循环变量 
	unsigned int i = 0; 
	unsigned int j = 0; 
 
	if (pInVector != NULL) 
	{ 
		// 初始化输出新码本 
		memset(pNewCodeBook, 0, sizeof(double) * nCodeNums); 
		// 初始化标志矩阵 
		memset(pFlag, 0, sizeof(unsigned int) * nCodeNums * nInLen); 
 
		// 聚类 
		for (i = 0; i < nInLen; i++) 
		{ 
			// 初始最小值 
			fMinDis = abs(pInVector[i] - pCodeBook[0]); 
			nIndex = 0; 
 
			for (j = 0; j < nCodeNums; j++) 
			{ 
				fDis = abs(pInVector[i] - pCodeBook[j]); 
 
				if (fDis < fMinDis) 
				{ 
					fMinDis = fDis; 
					nIndex = j; 
				} 
			} 
 
			// 设定pInVector[i]属于中心pCodeBook[nIndex]; 
			pFlag[nIndex * nInLen + i] = 1; 
		} 
 
		// 计算新码本 
		for (i = 0; i < nCodeNums; i++) 
		{ 
			fSamplesSum = 0; 
			nSamplesCount = 0; 
 
			for (j = 0; j < nInLen; j++) 
			{ 
				if (pFlag[i * nInLen + j] == 1) 
				{ 
					nSamplesCount++; 
					fSamplesSum += pInVector[j]; 
				} 
			} 
 
			// 当堆大小为0时,对应码矢为0 
			pNewCodeBook[i] =  
				(nSamplesCount == 0 ? 0 : (fSamplesSum / nSamplesCount)); 
			fDistortion += abs(pCodeBook[i] - pNewCodeBook[i]); 
		} 
 
		// 计算畸变 
		fDistortion /= nCodeNums; 
	} 
 
	return fDistortion; 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 对输入码本,按照标准码本进行分类 
// 
// 创建人:		陈文凯 
// 创建日期:	2005-06-12 
// 修改人: 
// 修改日期: 
void CVQ::Classify( 
				   const double* pInCodeBook,	// 输入码本 
				   unsigned int nInNums,		// 输入码本中码字数量 
				   const double* pCodeBook,		// 模板码本 
				   unsigned int nCodeNums,		// 模板码本中码字数量 
				   unsigned int* pKinds			// 输入码本中的码字对应的模板码本中码字的下标 
				   ) 
{ 
	// 码本的最小距离 
	double fMinDis = 0; 
	// 最小距离对应的模板码字的下标 
	unsigned int nMinIndex = 0; 
	// 计算码本距离 
	double fDis = 0; 
 
	if (pInCodeBook != NULL && pCodeBook != NULL) 
	{ 
		// 初始化码字下标 
		memset(pKinds, 0, sizeof(unsigned int) * nInNums); 
 
		for (unsigned int i = 0; i < nInNums; i++) 
		{ 
			// 初始化最小距离 
			fMinDis = abs(pInCodeBook[i] - pCodeBook[0]); 
			nMinIndex = 0; 
 
			// 计算最小距离 
			for (unsigned int j = 0; j < nCodeNums; j++) 
			{ 
				fDis = abs(pInCodeBook[i] - pCodeBook[j]); 
 
				if (fDis < fMinDis) 
				{ 
					fMinDis = fDis; 
					nMinIndex = j; 
				} 
			} 
 
			// 保存码字下标 
			pKinds[i] = nMinIndex; 
			TRACE("pKinds[%d]: %d\n", i, nMinIndex); 
		} 
	} 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 计算两个码本的欧式距离 
double CVQ::GetDistance( const double* pCode1, const double* pCode2, unsigned int nCodeNums ) 
{ 
	double fSum = 0; 
 
	for (unsigned int i = 0; i < nCodeNums; i++) 
	{ 
		fSum += (pCode1[i] - pCode2[i]) * (pCode1[i] - pCode2[i]); 
	} 
 
	return sqrt(fSum); 
}