源代码来自http://hi.baidu.com/lvchengbin405/blog/item/82c059cb8203c8f552664f98.html

本人截取其中训练和识别部分的代码进行注释。

训练部分:

void Face::TrainHMM(HMMParams params)
{
if (Release() == false)
   return;
int vect_len = params.obsSize.height * params.obsSize.width;
m_vectSize = vect_len;

m_hmm = cvCreate2DHMM(params.stateNum, params.mixNum, vect_len);//创建隐马尔科夫模型,第一个参数为初始状态数,第二个参数高斯混合矩阵,具体作用不明,但感觉是在函数cvEstimateHMMStateParams中使用,重复计算隐马尔科夫模型,第三个参数是观测向量的大小

int num_img = m_p_w_picpathlist.size();
CvImgObsInfo** obs_info_array = new CvImgObsInfo*[num_img];//观测向量的信息
list<char*>::iterator it;
int i = 0;
for(it=m_p_w_picpathlist.begin(); it!=m_p_w_picpathlist.end(); it++)
{
   IplImage* ipl = cvLoadImage(*it, -1);
   if (ipl == NULL)
    return;
   CvSize num_obs;
   CvSize roi = cvSize( ipl->roi ? ipl->roi->width : ipl->width,
    ipl->roi ? ipl->roi->height : ipl->height);//获取所需要分段的区域
   CV_COUNT_OBS(&roi, &(params.dctSize), &(params.delta), &num_obs);//获取num_obs,此为一图像要分成的段数,第一个参数是所要分段的图像,第二个参数是分段所使用的向量的大小,第三个是相邻两段的重叠大小
   obs_info_array[i] = cvCreateObsInfo(num_obs, vect_len);//创建观测向量信息
   CvImgObsInfo* info = obs_info_array[i];

   // 从图像中提取的观测向量,即DCT系数
   cvImgToObs_DCT(ipl, info->obs, params.dctSize, params.obsSize, params.delta);
   // 以HMM状态统一分割图像观测值
   cvUniformImgSegm(info, m_hmm);
   i++;
}

// 由混合分量来分割HMM的每个内在状态的所有观测值
cvInitMixSegm(obs_info_array, num_img, m_hmm);

bool trained = false;
float old_likelihood = 0;
int counter = 0;
while ((!trained) && (counter<params.maxiterations))//开始训练
{
   counter++;
   int j;
   // 计算每个HMM状态中的所有内在参数
   cvEstimateHMMStateParams(obs_info_array, num_img, m_hmm);
   // 运用现有的图像观测值分割为所有嵌入和内部的HMM函数,计算可能的变换矩阵
   cvEstimateTransProb(obs_info_array, num_img, m_hmm);
   float likelihood = 0;
   for (j=0; j<num_img; j++)
   {
    cvEstimateObsProb(obs_info_array[j], m_hmm);//获取观测向量矩阵,由上述的dct进行运算所得
    likelihood += cvEViterbi(obs_info_array[j], m_hmm);//Viterbi算法,计算最有可能的隐马尔科夫模型,即两次迭代的概率不超过阈值0.01
   }
   likelihood /= num_img*obs_info_array[0]->obs_size;
   // 
   cvMixSegmL2(obs_info_array, num_img, m_hmm);
   trained = (fabs(likelihood - old_likelihood) < 0.01);
   old_likelihood = likelihood;
}

// 
for(i=0; i<num_img; i++)
{
   cvReleaseObsInfo(&(obs_info_array[i]));
}
m_trained = true;
}

识别部分:识别部分主要是根据所得的观测向量,通过viterbi算法,与数据库中的每个subject的隐马尔科夫模型进行运算,选取其中最大的概率的subject

void FaceRecognition::Recognize(IplImage* ipl)
{
float like_array[1000];
CvSize cvroi = cvSize(ipl->roi ? ipl->roi->width : ipl->width,
   ipl->roi ? ipl->roi->height : ipl->height);

CvSize num_obs;
CvImgObsInfo* info;

CV_COUNT_OBS(&cvroi, &m_hmmp.dctSize, &m_hmmp.delta, &num_obs);

int vect_len = m_hmmp.obsSize.height * m_hmmp.obsSize.width;

info = cvCreateObsInfo(num_obs, vect_len);

cvImgToObs_DCT(ipl, info->obs, m_hmmp.dctSize, m_hmmp.obsSize, m_hmmp.delta);

float max_like = -100000000; 
list<Person*>::iterator it;
Person* per;
int per_num=0;
for (it=m_personList.begin(); it!=m_personList.end(); it++)
{
   CvEHMM* hmm = 0;
   per = *it;
   if (!per->GetFace()->IsTrained())
   {
    m_rnum = -1;
    return;
   }
   hmm = per->GetFace()->GetEHMM();
   if (!hmm) //person not trained
   {
    m_rnum = -1;
    return;
   }

   cvEstimateObsProb(info, hmm);//获取图像的观测矩阵
   like_array[per_num++] = cvEViterbi(info, hmm);//据算概率
}
cvReleaseObsInfo(&info);

for (int i = 0; i < MIN(3, per_num); i++)//选择最大概率
{
   float maxl = -FLT_MAX;
   int maxind = -1;

   for(int j = 0; j < per_num; j++)
   {
    if (like_array[j] > maxl)
    {
     maxl = like_array[j];
     maxind = j;
    }
   }
   m_threeFirst[i] = maxind;
   like_array[maxind] = -FLT_MAX;
}
m_rnum = MIN(3, per_num);
}