www.pudn.com > hbp0.1.zip > classHumunculus.cpp


/*************************************************************************** 
                          classHumunculus.cpp 
 
  Humunculus - the little man inside your head 
 
  Comments are included in machine readable XML format, for possible future  
  production of program documentation 
 
    begin                : Mon Jan 26 2004 
    copyright            : (C) 2004 by Bob Mottram 
    email                : fuzzgun@btinternet.com 
 ***************************************************************************/ 
 
/*************************************************************************** 
 *                                                                         * 
 *   This program is free software; you can redistribute it and/or modify  * 
 *   it under the terms of the GNU General Public License as published by  * 
 *   the Free Software Foundation; either version 2 of the License, or     * 
 *   (at your option) any later version.                                   * 
 *                                                                         * 
 ***************************************************************************/ 
 
 
#include "stdafx.h" 
//#include  
//#include  
//#include "classHumunculus.h" 
 
//size of the image used to analyse features of the face 
#define FACE_IMAGE_SIZE       60 
 
#define DETECTED_EYE_WIDTH    40 
#define DETECTED_EYE_HEIGHT   40 
 
#define DETECTED_NOSE_WIDTH   10 
#define DETECTED_NOSE_HEIGHT  20 
 
#define DETECTED_MOUTH_WIDTH  20 
#define DETECTED_MOUTH_HEIGHT 15 
 
//face classifier dimensions 
#define FACE_CLASSIFIER_WIDTH 20 
#define FACE_CLASSIFIER_HEIGHT 17 
 
 
///  
/// Constructor 
///  
classHumunculus::classHumunculus() 
{ 
  personID=-1; 
 
  //allow recognition of specific individuals 
  FaceRecognition = true; 
 
  //default body position - arms down at sides 
  ShoulderElevation[0] = 100; 
  ShoulderElevation[1] = 100; 
  ElbowAngle[0] = 0; 
  ElbowAngle[1] = 0; 
  prevShoulderElevation[0] = ShoulderElevation[0]; 
  prevShoulderElevation[1] = ShoulderElevation[1]; 
  prevElbowAngle[0] = ElbowAngle[0]; 
  prevElbowAngle[1] = ElbowAngle[1]; 
  prevHandSize[0]=0; 
  prevHandSize[1]=0; 
  prev_gaze_direction=0; 
  prev_head_tilt=0; 
  prev_mouth_Open=0; 
  bodyWidth=0; 
  face_x=0; 
  face_y=0; 
  gazeDirection=0; 
  headTilt=0; 
  invalid_face=false; 
  mouthWidth=0; 
  mouthOpen=0; 
 
  //define default body colour (black) 
  bodyColour[0] = 45; 
  bodyColour[1] = 45; 
  bodyColour[2] = 45; 
  bodyColourTollerance = 45; 
 
  //default background colour (white) 
  backgroundColour[0]=255; 
  backgroundColour[1]=255; 
  backgroundColour[2]=255; 
 
  //define images used for analysis of the face 
  face = new classimage; 
  face->createImage(FACE_IMAGE_SIZE,FACE_IMAGE_SIZE); 
  normalisedFace = NULL; 
  imgEye[0] = new classimage; 
  imgEye[0]->createImage(DETECTED_EYE_WIDTH,DETECTED_EYE_HEIGHT); 
  imgEye[1] = new classimage; 
  imgEye[1]->createImage(DETECTED_EYE_WIDTH,DETECTED_EYE_HEIGHT); 
  imgNose = new classimage; 
  imgNose->createImage(DETECTED_NOSE_WIDTH,DETECTED_NOSE_HEIGHT); 
  imgMouth = new classimage; 
  imgMouth->createImage(DETECTED_MOUTH_WIDTH,DETECTED_MOUTH_HEIGHT); 
 
  //initialise face classifier 
  classifier = new Ttopmap(FACE_IMAGE_SIZE/2,FACE_IMAGE_SIZE/2,FACE_CLASSIFIER_WIDTH,FACE_CLASSIFIER_HEIGHT); 
  //load("c:\\develop\\hbp\\faces.net"); 
  classifier->learningRate = 0.5f; 
  enableLearning=true; 
} 
 
///  
/// Destructor 
///  
classHumunculus::~classHumunculus() 
{ 
  delete face; 
  delete normalisedFace; 
  delete imgEye[0]; 
  delete imgEye[1]; 
  delete imgNose; 
  delete imgMouth; 
 
  //save("c:\\develop\\hbp\\faces.net"); 
  delete classifier; 
} 
 
 
///  
/// save the classifier data 
///  
/// File name to save learned faces to 
void classHumunculus::save(char *filename) 
{ 
  //if (enableLearning) classifier->save("c:\\develop\\hbp\\faces.net"); 
  if (enableLearning) classifier->save(filename); 
} 
 
 
///  
/// load the classifier data 
///  
/// File name to load learned faces from 
void classHumunculus::load(char *filename) 
{ 
  //classifier->load("c:\\develop\\hbp\\faces.net"); 
  classifier->load(filename); 
  classifier->learningRate = 0.5f; 
} 
 
 
///  
/// detect facial features using the given image and face coordinates 
///  
/// image object containing the face 
/// The top left x coordinate of the detected face within the image object 
/// The top left y coordinate of the detected face within the image object 
/// The bottom right x coordinate of the detected face within the image object 
/// The bottom right y coordinate of the detected face within the image object 
void classHumunculus::analyseFace(classimage *img, int face_tx, int face_ty, int face_bx, int face_by) 
{ 
  int x,y,c,xx,yy,w,h; 
  int lateral_symetry,lefteye_x,righteye_x,lefteye_y,righteye_y,mouth_y,mouth_width; 
 
  //dimensions of the face within the original image 
  w = face_bx - face_tx; 
  h = face_by - face_ty; 
 
  //dump the face region into a separate image of fixed dimensions 
  for (x=0;ximage[x][y][c] = img->image[xx][yy][c]; 
	} 
  } 
  //face->updateIntegralImage(); 
 
  //detect the positions of features within the face image 
  detectFeatures(lateral_symetry, lefteye_x, righteye_x, lefteye_y, righteye_y, mouth_y, mouth_width); 
  detectKeypoints(lateral_symetry, lefteye_x, righteye_x, lefteye_y, righteye_y, mouth_y, mouth_width); 
 
} 
 
 
 
///  
/// Detect the locations of facial features 
///  
/// Returned x coordinate for the lateral symetry of the face 
/// Returned x coordinate for the centre of the left eye 
/// Returned x coordinate for the centre of the right eye 
/// Returned y coordinate for the centre of the left eye 
/// Returned y coordinate for the centre of the right eye 
/// Returned y coordinate for the centre of the mouth 
/// Returned x width of the mouth in pixels 
void classHumunculus::detectFeatures(int &lateral_symetry, int &lefteye_x, int &righteye_x, int &lefteye_y, int &righteye_y, int &mouth_y, int &mouth_width) 
{  
  int x,y,halfwidth,quarterwidth,symetry_x; 
  int halfheight,quarterheight,eyes_separation,eyes_centre; 
  long tot,tot_w,w,xpos; 
  float mouth_ratio = (float)1.4; 
 
  //check symetry within the edges image 
  halfwidth = FACE_IMAGE_SIZE/2; 
  quarterwidth = halfwidth/2; 
  halfheight = FACE_IMAGE_SIZE/2; 
  quarterheight = halfheight/2; 
 
  symetry_x = halfwidth; 
  lateral_symetry = (symetry_x * 100) / FACE_IMAGE_SIZE; 
 
  //search for the left eye horizontal position 
  tot=0; 
  tot_w=0; 
  for (x=symetry_x-(FACE_IMAGE_SIZE/3);ximage[x][quarterheight+y][0]; 
	} 
 
	if (w>200) 
	{ 
	  tot_w += w; 
      tot += (x * w); 
	} 
  } 
  if (tot_w>0) tot /= tot_w; else tot=halfwidth; 
  xpos=tot; 
  lefteye_x = xpos; //(xpos * 100) / FACE_IMAGE_SIZE; 
 
  //search for the left eye vertical position 
  tot=0; 
  tot_w=0; 
  for (y=quarterheight;yimage[xpos+x][y][0]; 
 
	if (w>200) 
	{ 
  	  tot_w += w; 
      tot += y * w; 
	} 
  } 
  if (tot_w>0) tot /= tot_w; else tot=halfheight; 
  lefteye_y = tot; //(tot * 100) / FACE_IMAGE_SIZE; 
 
 
  //search for the right eye horizontal position 
  tot=0; 
  tot_w=0; 
  for (x=symetry_x;ximage[x][quarterheight+y][0]; 
	} 
 
	if (w>200) 
	{ 
	  tot_w += w; 
      tot += (x * w); 
	} 
  } 
  if (tot_w>0) tot /= tot_w; else tot=halfwidth; 
  xpos=tot; 
  righteye_x = xpos; //(xpos * 100) / FACE_IMAGE_SIZE; 
 
  //search for the right eye vertical position 
  tot=0; 
  tot_w=0; 
  for (y=quarterheight;yimage[xpos+x][y][0]; 
 
	if (w>200) 
	{ 
	  tot_w += w; 
      tot += y * w; 
	} 
  } 
  if (tot_w>0) tot /= tot_w; else tot=halfheight; 
  righteye_y = tot; //(tot * 100) / FACE_IMAGE_SIZE; 
 
 
 
  //mouth vertical position 
  eyes_separation = righteye_x - lefteye_x;                             //separation between the eyes 
  eyes_centre = lefteye_x + (eyes_separation/2);                        //horizontal centre point between the eyes 
  mouth_y = ((lefteye_y + righteye_y)/2) + (int)(eyes_separation*mouth_ratio);  //mouth vertical position found using fixed ratio 
  if (mouth_y>99) mouth_y=99; 
  mouth_width = (eyes_separation*120)/100;                                //mouth width is a little wider than the separation between the eyes 
} 
 
 
///  
/// Detects the presence of a horizontal line between the given start and end y coordinates 
///  
/// The y coordinate from which to begin the search 
/// The y coordinate at which to end the search 
int classHumunculus::detectHorizintalLine(int start_y, int end_y) 
{  
  int x,y,winner,start_x,end_x; 
  long p[2],max,diff; 
 
  start_x = FACE_IMAGE_SIZE / 3; 
  end_x = FACE_IMAGE_SIZE - (FACE_IMAGE_SIZE/3); 
 
  winner=-1; 
  max=(end_x-start_x)*10; 
  for (y=start_y+2;yimage[x][y-2][0]; 
	  p[0] += face->image[x][y-1][0]; 
	  p[0] += face->image[x][y][0]; 
	  p[1] += face->image[x][y+1][0]; 
	  p[1] += face->image[x][y+2][0]; 
	  p[1] += face->image[x][y+3][0]; 
	} 
	diff = abs(p[0] - p[1]); 
	if (diff>max) 
	{ 
	  max = diff; 
	  winner = y; 
	} 
  } 
 
  return(winner); 
} 
 
 
///  
/// Detects the presence of a vertical line between the given start and end x coordinates. 
/// A bias value may be used in order to favour detections either to the left or right.  This 
/// bias is typically used for detection of the left and right sides of the face. 
///  
/// The x coordinate from which to begin the search 
/// The x coordinate at which to end the search 
/// This is used in order to bias the decision either to the left or to the right 
/// The value of the bias to be applied during the search 
int classHumunculus::detectVerticalLine(int start_x, int end_x, bool leftBias, int biasValue) 
{  
  int x,y,winner,start_y,end_y; 
  long p[2],max,diff; 
 
  //there is no need to scan the entire column, just the area around the eyes will do 
  start_y = FACE_IMAGE_SIZE / 20; //6; 
  end_y = FACE_IMAGE_SIZE - (FACE_IMAGE_SIZE/3); 
 
  winner=-1; 
  max=(end_y-start_y)*20; 
  for (x=start_x+3;ximage[x-3][y][0]; 
	  p[0] += face->image[x-2][y][0]; 
	  p[0] += face->image[x-1][y][0]; 
	  p[0] += face->image[x][y][0]; 
 
	  p[0] += face->image[x-3][y-1][0]; 
	  p[0] += face->image[x-2][y-1][0]; 
	  p[0] += face->image[x-1][y-1][0]; 
	  p[0] += face->image[x][y-1][0]; 
 
	  p[1] += face->image[x][y][0]; 
	  p[1] += face->image[x+1][y][0]; 
	  p[1] += face->image[x+2][y][0]; 
	  p[1] += face->image[x+3][y][0]; 
 
	  p[1] += face->image[x][y-1][0]; 
	  p[1] += face->image[x+1][y-1][0]; 
	  p[1] += face->image[x+2][y-1][0]; 
	  p[1] += face->image[x+3][y-1][0]; 
	} 
	diff = abs(p[0] - p[1]); 
 
	//bias the difference value either to the left or right 
	//this helps to ensure that the first peak value in the given search direction is detected 
	if (leftBias) 
	  diff *= 1 + ((x * biasValue) / (end_x-start_x)); 
	  else 
	  diff *= 1 + (((end_x-x) * biasValue) / (end_x-start_x)); 
 
	if (diff>max) 
	{ 
	  max = diff; 
	  winner = x; 
	} 
  } 
 
  return(winner); 
} 
 
 
///  
/// Create a normalised face image which can be used for recognition purposes 
///  
/// x coordinate for the centre of the left eye 
/// x coordinate for the centre of the right eye 
/// y coordinate for the centre of the left eye 
/// y coordinate for the centre of the right eye 
/// x coordinate for the left side of the face 
/// x coordinate for the right side of the face 
/// y coordinate for the eyebrows 
void classHumunculus::updateNormalisedFace(int lefteye_x, int lefteye_y, int righteye_x, int righteye_y, int leftFaceSide, int rightFaceSide, int eyebrows_y) 
{ 
  int x,y,c,w,h,xx,yy,yyy,facewidth,p,diff; 
  int lefteye_x2,righteye_x2,top_y,bottom_y,leftSide,rightSide; 
  long av_illumination=0; 
 
  w = FACE_IMAGE_SIZE/2; 
  h = FACE_IMAGE_SIZE/2; 
  facewidth = rightFaceSide - leftFaceSide; 
 
  //create the face object if necessary 
  if (normalisedFace == NULL) 
  { 
    normalisedFace = new classimage; 
    normalisedFace->createImage(w,h); 
  } 
 
 
  lefteye_x2 = w/4; 
  righteye_x2 = w*3/4; 
  top_y = eyebrows_y - (FACE_IMAGE_SIZE/8); 
  if (top_y<0) top_y=0; 
  bottom_y = FACE_IMAGE_SIZE-1; 
 
  for (y=0;y=FACE_IMAGE_SIZE) xx = FACE_IMAGE_SIZE-1; 
	  if (yyy<0) yyy=0; 
	  if (yyy>=FACE_IMAGE_SIZE) yyy = FACE_IMAGE_SIZE-1; 
 
	  //update the normalised face image 
	  for (c=0;c<3;c++) normalisedFace->image[x][y][c] = face->image[xx][yyy][c]; 
	  av_illumination += normalisedFace->image[x][y][0]; 
	} 
  } 
 
  //adjust for lighting 
  av_illumination /= w*h; 
  for (y=0;yimage[x][y][c]; 
		diff = normalisedFace->image[x][y][c] - av_illumination; 
        diff = (diff * 127) / av_illumination; 
		p = diff + 127; 
		if (p<0) p=0; 
		if (p>255) p=255; 
		normalisedFace->image[x][y][c] = (unsigned char)p; 
	  } 
	} 
  } 
 
} 
 
 
///  
/// Classify (recognise) the normalised face image and update the person ID 
///  
void classHumunculus::classifyFace() 
{ 
  int x,y,w,h; 
 
  //dimensions of the normalised face image 
  w = FACE_IMAGE_SIZE/2; 
  h = FACE_IMAGE_SIZE/2; 
 
  //feed the image into the classifier 
  for (y=0;ysetInput(x,y,normalisedFace->image[x][y][0]); 
 
  //update the classifier 
  classifier->update(); 
  if (enableLearning) classifier->learn(); 
 
  //get the ID number for the person recognised 
  personID = classifier->getNearestClassification(classifier->WinnerX,classifier->WinnerY); 
} 
 
 
 
///  
/// Uses the points detected earlier with the function detectfeatures to do a more detailed 
/// search for specific points on the eyes, eyebrows, nose and mouth 
///  
/// x coordinate for lateral symetry of the face 
/// x coordinate for the centre of the left eye 
/// x coordinate for the centre of the right eye 
/// y coordinate for the centre of the left eye 
/// y coordinate for the centre of the right eye 
/// y coordinate for the centre of the mouth 
/// width of the mouth in pixels 
void classHumunculus::detectKeypoints(int lateral_symetry, int lefteye_x, int righteye_x, int lefteye_y, int righteye_y, int mouth_y, int mouth_width) 
{  
  int eye_width,eye_height,nose_width,nose_height,mouth_height; 
  int x,y,x1,x2,y1,y2,xx,yy,lateral,lefteye,righteye,lefteyeY,righteyeY,c; 
  int noseY,noseX,point_x,point_y,px; 
  int eyesY[2]; 
  int smile,mouth_Open,minY,maxY,mouthOpenWidth; 
  int pxx,rightEyeCorner[2],leftEyeCorner[2],eyeCentre[2],eyebrow_y[2]; 
  int threshold_mouthopen = 20; //threshold for detecting whether the mouth is open 
  int threshold_smile = 20;     //threshold for detecting smiling 
  int leftSide,rightSide,faceCentre,d1,d2,head_tilt,gaze_direction,eyes_separation,eyeb_y; 
 
  lateral    = FACE_IMAGE_SIZE * lateral_symetry / 100; 
  lefteye    = FACE_IMAGE_SIZE * lefteye_x / 100; 
  righteye   = FACE_IMAGE_SIZE * righteye_x / 100; 
  lefteyeY   = FACE_IMAGE_SIZE * lefteye_y / 100; 
  righteyeY  = FACE_IMAGE_SIZE * righteye_y / 100; 
  noseY      = FACE_IMAGE_SIZE / 2; 
  noseX      = FACE_IMAGE_SIZE / 2; 
 
  invalid_face = true; 
  leftSide=-1; 
  rightSide=-1; 
 
  //left eye 
  eye_height = (int)(FACE_IMAGE_SIZE / 2.5); 
  eye_width = (int)(FACE_IMAGE_SIZE / 2.6); 
  for (y=0;y=face->height-1) yy=face->height-1; 
    for (x=0;x=face->width-1) xx=face->width-1; 
       
	  for (c=0;c<3;c++) 
        imgEye[0]->image[x][y][c] = face->image[xx][yy][c]; 
	} 
  } 
 
     
  //find the centre of the left eye 
  imgEye[0]->CG((DETECTED_EYE_WIDTH/8), DETECTED_EYE_HEIGHT / 2, DETECTED_EYE_WIDTH - (DETECTED_EYE_WIDTH/8), DETECTED_EYE_HEIGHT - 1, 0, 0, 0, point_x, point_y, 255); 
  eyesY[0] = point_y; 
     
  //inner left eye 
  px = point_x; 
  imgEye[0]->CG(px, DETECTED_EYE_HEIGHT / 2, DETECTED_EYE_WIDTH - 1, DETECTED_EYE_HEIGHT - 1, 0, 0, 0, point_x, point_y, 255 * 3); 
  rightEyeCorner[0] = point_x; 
     
  //outer left eye 
  pxx = px - 1; 
  if (pxx < 0) pxx = 0; 
  imgEye[0]->CG(0, DETECTED_EYE_HEIGHT / 2, pxx, DETECTED_EYE_HEIGHT - 1, 0, 0, 0, point_x, point_y, 255 * 2); 
  leftEyeCorner[0] = point_x; 
     
  minY = eyesY[0] - (DETECTED_EYE_HEIGHT / 8); 
  maxY = eyesY[0] + (DETECTED_EYE_HEIGHT / 8); 
  if (minY < 0) minY = 0; 
  if (maxY >= DETECTED_EYE_HEIGHT) maxY = DETECTED_EYE_HEIGHT - 1; 
     
  //left eye centre 
  imgEye[0]->CG(leftEyeCorner[0], minY, rightEyeCorner[0], maxY, 0, 0, 0, point_x, point_y, 255); 
  if (point_x < leftEyeCorner[0] + 1) point_x = leftEyeCorner[0] + 1; 
  if (point_x > rightEyeCorner[0] - 1) point_x = rightEyeCorner[0] - 1; 
  eyeCentre[0] = point_x; 
     
  //left eye direction 
  imgEye[0]->CG(leftEyeCorner[0], minY, rightEyeCorner[0], maxY, 255, 255, 255, point_x, point_y, 200 * 3); 
     
  //centre of left eyebrow 
  imgEye[0]->CG(0, 0, DETECTED_EYE_WIDTH - 1, DETECTED_EYE_HEIGHT / 2, 0, 0, 0, point_x, point_y, 255); 
  eyebrow_y[0] = point_y; 
     
  //right eye 
  for (y=0;y=face->height-1) yy=face->height-1; 
    for (x=0;x=face->width-1) xx=face->width-1; 
       
	  for (c=0;c<3;c++) 
        imgEye[1]->image[x][y][c] = face->image[xx][yy][c]; 
	} 
  } 
 
  //centre of right eye 
  imgEye[1]->CG((DETECTED_EYE_WIDTH/8), DETECTED_EYE_HEIGHT / 2, DETECTED_EYE_WIDTH - (DETECTED_EYE_WIDTH/8), DETECTED_EYE_HEIGHT - 1, 0, 0, 0, point_x, point_y, 255); 
  eyesY[1] = point_y; 
     
  //inner right eye 
  px = point_x; 
  imgEye[1]->CG(0, DETECTED_EYE_HEIGHT / 2, px, DETECTED_EYE_HEIGHT - 1, 0, 0, 0, point_x, point_y, 255 * 3); 
  leftEyeCorner[1] = point_x; 
     
  //outer right eye 
  pxx = px + 1; 
  if (pxx >= DETECTED_EYE_WIDTH) pxx = DETECTED_EYE_WIDTH - 1; 
  imgEye[1]->CG(pxx, DETECTED_EYE_HEIGHT / 2, DETECTED_EYE_WIDTH - 1, DETECTED_EYE_HEIGHT - 1, 0, 0, 0, point_x, point_y, 255 * 2); 
  rightEyeCorner[1] = point_x; 
     
  minY = eyesY[1] - (DETECTED_EYE_HEIGHT / 8); 
  maxY = eyesY[1] + (DETECTED_EYE_HEIGHT / 8); 
  if (minY < 0) minY = 0; 
  if (maxY >= DETECTED_EYE_HEIGHT) maxY = DETECTED_EYE_HEIGHT - 1; 
     
  //centre of right eye 
  imgEye[1]->CG(leftEyeCorner[1], minY, rightEyeCorner[1], maxY, 0, 0, 0, point_x, point_y, 255); 
  //if (point_x == 0) point_x = (eyeCentre[0] - leftEyeCorner[0]) + leftEyeCorner[1]; 
  if (point_x < leftEyeCorner[1] + 1) point_x = leftEyeCorner[1] + 1; 
  if (point_x > rightEyeCorner[1] - 1) point_x = rightEyeCorner[1] - 1; 
  eyeCentre[1] = point_x; 
     
  //right eye direction 
  imgEye[1]->CG(leftEyeCorner[1], minY, rightEyeCorner[1], maxY, 255, 255, 255, point_x, point_y, 200 * 3); 
         
  //centre of right eyebrow 
  imgEye[1]->CG(0, 0, DETECTED_EYE_WIDTH - 1, DETECTED_EYE_HEIGHT / 2, 0, 0, 0, point_x, point_y, 255); 
  eyebrow_y[1] = point_y; 
 
   
  //nose 
  nose_height = FACE_IMAGE_SIZE / 5; 
  nose_width = FACE_IMAGE_SIZE / 6; 
  for (y=0;y=face->height-1) yy=face->height-1; 
    for (x=0;x=face->width-1) xx=face->width-1; 
       
	  for (c=0;c<3;c++) 
        imgNose->image[x][y][c] = face->image[xx][yy][c]; 
	} 
  } 
 
  //nose tip 
  imgNose->CG(0, DETECTED_NOSE_HEIGHT / 2, DETECTED_NOSE_WIDTH - 1, DETECTED_NOSE_HEIGHT - 1, 0, 0, 0, point_x, point_y, 255); 
   
  //mouth 
  mouth_height = FACE_IMAGE_SIZE / 3; 
  for (y=0;y=face->height-1) yy=face->height-1; 
    for (x=0;x=face->width-1) xx=face->width-1; 
       
	  for (c=0;c<3;c++) 
        imgMouth->image[x][y][c] = face->image[xx][yy][c]; 
	} 
  } 
 
   
  mouth_Open = imgMouth->relativeThreshold(threshold_mouthopen,DETECTED_MOUTH_WIDTH/4,0,(DETECTED_MOUTH_WIDTH*3)/4,DETECTED_MOUTH_HEIGHT,mouthOpenWidth); 
  if (mouth_Open>10) 
  { 
    smile = 0;	 
  } 
  else 
  { 
	smile = imgMouth->relativeThreshold(threshold_smile,DETECTED_MOUTH_WIDTH/4,0,DETECTED_MOUTH_WIDTH/2,DETECTED_MOUTH_HEIGHT,mouthOpenWidth); 
	smile += imgMouth->relativeThreshold(threshold_smile,DETECTED_MOUTH_WIDTH*3/4,0,DETECTED_MOUTH_WIDTH,DETECTED_MOUTH_HEIGHT,mouthOpenWidth); 
	smile -= mouth_Open/2; 
	mouth_Open = 0; 
	mouthOpenWidth = 0; 
	if (smile<4) smile=0; 
  } 
 
 
   
  //show pupils 
  x1 = lefteye_x + (((eyeCentre[0] - (DETECTED_EYE_WIDTH/2)) * eye_width) / DETECTED_EYE_WIDTH); 
  y1 = lefteye_y + (((eyesY[0] - (DETECTED_EYE_HEIGHT/2)) * eye_height) / DETECTED_EYE_HEIGHT); 
  x2 = righteye_x + (((eyeCentre[1] - (DETECTED_EYE_WIDTH/2)) * eye_width) / DETECTED_EYE_WIDTH); 
  y2 = righteye_y + (((eyesY[1] - (DETECTED_EYE_HEIGHT/2)) * eye_height) / DETECTED_EYE_HEIGHT); 
 
  if ((x1>(FACE_IMAGE_SIZE/5)) && (x20) 
	{ 
	  //calculate the head tilt from side to side using the vertical positions of the eyes 
	  head_tilt = ((y1 - y2)*100)/(x2 - x1); 
 
      //detect the left and right sides of the face 
      leftSide = detectVerticalLine(0,x1,true,5); 
	  if (leftSide==-1) leftSide=0; 
      rightSide = detectVerticalLine(x2,FACE_IMAGE_SIZE,false,5); 
	  if (rightSide==-1) rightSide=FACE_IMAGE_SIZE-1; 
	  if (leftSide>x1-2) leftSide = x1-2; 
	  if (leftSide<0) leftSide=0; 
	  if (rightSide=FACE_IMAGE_SIZE) rightSide=FACE_IMAGE_SIZE-1; 
 
	  //calculate the apparent centre position of the face 
	  faceCentre = leftSide + ((rightSide - leftSide)/2); 
 
	  //calculate the distance of left and right eyes from the centre of the face 
	  //in order to estimate gaze direction 
	  d1 = x1 - faceCentre; 
	  d2 = faceCentre - x2; 
      gaze_direction = ((d1-d2) * 500) / (rightSide - leftSide); 
	  if (gaze_direction>100) gaze_direction=100; 
	  if (gaze_direction<-100) gaze_direction=-100; 
	} 
	else 
	{ 
	  //gazeDirection = 0; 
	  head_tilt = 0; 
	} 
 
     
	//if the geometry of the detected features doesn't make sense then assume it isn't a face 
	if ((abs(head_tilt)<40) && (eyes_separation>FACE_IMAGE_SIZE/6) && (eyes_separation-1) face->DrawBox(FACE_IMAGE_SIZE/2,mouth_y,FACE_IMAGE_SIZE/8,1,90); 
 
	  //show eyes 
      face->DrawBox(x1,y1,FACE_IMAGE_SIZE/10,FACE_IMAGE_SIZE/10,90); 
      face->DrawBox(x2,y2,FACE_IMAGE_SIZE/10,FACE_IMAGE_SIZE/10,90); 
 
      //show mouth open 
      if (mouth_Open>0) 
        face->DrawBox(lateral,mouth_y,mouth_width,(FACE_IMAGE_SIZE/4)*mouth_Open/DETECTED_MOUTH_HEIGHT,90); 
 
      //show inner corners 
      x = lefteye_x + (((rightEyeCorner[0] - (DETECTED_EYE_WIDTH/2)) * eye_width) / DETECTED_EYE_WIDTH); 
      y = lefteye_y + (((eyesY[0] - (DETECTED_EYE_HEIGHT/2)) * eye_height) / DETECTED_EYE_HEIGHT); 
      face->DrawBox(x,y,1,FACE_IMAGE_SIZE/15,90); 
      x = righteye_x + (((leftEyeCorner[1] - (DETECTED_EYE_WIDTH/2)) * eye_width) / DETECTED_EYE_WIDTH); 
      y = righteye_y + (((eyesY[1] - (DETECTED_EYE_HEIGHT/2)) * eye_height) / DETECTED_EYE_HEIGHT); 
      face->DrawBox(x,y,1,FACE_IMAGE_SIZE/15,90); 
 
      //show outer corners 
      x = lefteye_x + (((leftEyeCorner[0] - (DETECTED_EYE_WIDTH/2)) * eye_width) / DETECTED_EYE_WIDTH); 
      y = lefteye_y + (((eyesY[0] - (DETECTED_EYE_HEIGHT/2)) * eye_height) / DETECTED_EYE_HEIGHT); 
      face->DrawBox(x,y,1,FACE_IMAGE_SIZE/15,90); 
      x = righteye_x + (((rightEyeCorner[1] - (DETECTED_EYE_WIDTH/2)) * eye_width) / DETECTED_EYE_WIDTH); 
      y = righteye_y + (((eyesY[1] - (DETECTED_EYE_HEIGHT/2)) * eye_height) / DETECTED_EYE_HEIGHT); 
      face->DrawBox(x,y,1,FACE_IMAGE_SIZE/15,90); 
 
	  //show the left and right sides of the face 
	  if (leftSide>-1) face->DrawBox(leftSide,FACE_IMAGE_SIZE/2,1,FACE_IMAGE_SIZE/4,90); 
	  if (rightSide>-1) face->DrawBox(rightSide,FACE_IMAGE_SIZE/2,1,FACE_IMAGE_SIZE/4,90); 
 
      //eyebrows 
      x = lefteye_x + (((eyeCentre[0] - (DETECTED_EYE_WIDTH/2)) * eye_width) / DETECTED_EYE_WIDTH); 
      y = lefteye_y + (((eyebrow_y[0] - (DETECTED_EYE_HEIGHT/2)) * eye_height) / DETECTED_EYE_HEIGHT); 
      face->DrawBox(x,y,eye_width/5,1,90); 
      x = righteye_x + (((eyeCentre[1] - (DETECTED_EYE_WIDTH/2)) * eye_width) / DETECTED_EYE_WIDTH); 
      y = righteye_y + (((eyebrow_y[1] - (DETECTED_EYE_HEIGHT/2)) * eye_height) / DETECTED_EYE_HEIGHT); 
      face->DrawBox(x,y,eye_width/5,1,90); 
 
      //dump the results into the appropriate public properties 
	  headTilt = (prev_head_tilt + head_tilt)/2; 
	  prev_head_tilt = headTilt; 
	  gazeDirection = (prev_gaze_direction + gaze_direction)/2; 
	  prev_gaze_direction = gazeDirection; 
      EyeOuter[0] = leftEyeCorner[0]; 
      EyeOuter[1] = rightEyeCorner[1]; 
      EyePupil_x[0] = eyeCentre[0]; 
      EyePupil_x[1] = eyeCentre[1]; 
      EyePupil_y[0] = eyesY[0]; 
      EyePupil_y[1] = eyesY[1]; 
      Eyebrow_y[0] = eyebrow_y[0]; 
      Eyebrow_y[1] = eyebrow_y[1]; 
      mouthOpen = (prev_mouth_Open + ((mouth_Open * 100)/DETECTED_MOUTH_HEIGHT))/2; 
	  prev_mouth_Open = mouthOpen; 
      mouthWidth = mouthOpenWidth; 
      eyebrows_vertical = (((eyesY[1] - Eyebrow_y[1]) + (eyesY[0] - Eyebrow_y[0]))*100)/DETECTED_EYE_HEIGHT; 
	  invalid_face = false; 
	} 
  } 
} 
 
 
///  
/// Returns the name for the person with the given ID 
///  
/// The ID number corresponding to the person 
/// The returned name of the person 
void classHumunculus::getPersonName(int classID, char *className) 
{ 
  classifier->getClassificationName(classID,className); 
} 
 
///  
/// Returns the filename for an image of the person with the given ID 
///  
/// The ID number corresponding to the person 
/// A returned image filename 
void classHumunculus::getPortrait(int classID, char *filename) 
{ 
  classifier->getPortrait(classID,filename); 
} 
 
 
///  
/// Update the position of an arm. 
/// There is some faffing about within this function, with the aim of eliminating 
/// occasional single frame errors 
///  
/// Indicates whether this is the left or right arm 
/// shoulder elevation in the range 0-100 
/// elbow angle in the range 0-100 
/// Hand size in pixels 
void classHumunculus::updateArmPosition(bool leftArm, int Shoulder_Elevation, int Elbow_Angle, int hand_size) 
{ 
  int index,ds,de,dh,diff; 
 
  if (leftArm) index=0; else index=1; 
  if (hand_size==0) hand_size = bodyWidth / 4; 
 
  ds = Shoulder_Elevation - prevShoulderElevation[index]; 
  de = Elbow_Angle - prevElbowAngle[index]; 
  dh = hand_size - prevHandSize[index]; 
   
  diff = abs(ds) + abs(de); 
  if (diff<80) 
  { 
    ShoulderElevation[index] = prevShoulderElevation[index] + (ds/2); 
    ElbowAngle[index] = prevElbowAngle[index] + (de/2); 
	handSize[index] = prevHandSize[index] + (dh/2); 
  } 
 
  prevShoulderElevation[index] = Shoulder_Elevation; 
  prevElbowAngle[index] = Elbow_Angle; 
  prevHandSize[index] = hand_size; 
}