Created
November 7, 2015 00:17
-
-
Save spartonia/cd1078947a5c2d8ec5c3 to your computer and use it in GitHub Desktop.
Source code for face recognition and skeleton tracking with kinect: https://www.youtube.com/watch?v=LtXUzOpKmTM
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Windows.h> | |
#include <Ole2.h> | |
#include <iostream> | |
#include <fstream> | |
#include <sstream> | |
#include <WinBase.h> | |
// Kinect | |
#include <NuiApi.h> | |
#include <NuiImageCamera.h> | |
#include <NuiSensor.h> | |
// Opencv | |
#include <cv.h> | |
#include <cxcore.h> | |
#include <highgui.h> | |
#include <core.hpp> | |
#include <opencv2\contrib\contrib.hpp> | |
#include <opencv2\highgui\highgui.hpp> | |
#include <opencv2\imgproc\imgproc.hpp> | |
#include <opencv2\objdetect\objdetect.hpp> | |
#include <opencv2\core\core.hpp> | |
using namespace cv; | |
using namespace std; | |
#define pictureMode false // true: take pictures for training | |
#define cropMode false // true: crop the faces in the images | |
#define pictureOwner "Lady_Big2" // name the picture owner | |
/* Image setting */ | |
#define imgWidth 640 | |
#define imgHeight 480 | |
#define imgWidthBig 1280 | |
#define imgHeightBig 960 | |
#define useExistingModel true // train new face rec. model or load existing. | |
#define recognizerThreshold 125.0 | |
#define writeToVideo false // true: record the operation | |
bool useBigPic = true; // true: use 1280x960 images for better accuracy. false: 640x480 | |
string modelName = "fisher"; // name the face rec. model | |
// Kinect variables | |
HANDLE rgbStream; | |
HANDLE skeletonStream; | |
INuiSensor* sensor; | |
NUI_SKELETON_FRAME* nextSkeletonFrame; | |
bool *m_bArrSkeletonsTracked; | |
// Program variables | |
IplImage* kinectColorImage = cvCreateImage(cvSize(imgWidth, imgHeight), IPL_DEPTH_8U, 4); | |
IplImage* kinectColorImageBig = cvCreateImage(cvSize(1280, 960), IPL_DEPTH_8U, 4); | |
IplImage* kinectColorImageTiny = cvCreateImage(cvSize(320, 240), IPL_DEPTH_8U, 4); | |
string output_folder = "Images";//"C:/attFaces/frec"; //for face recog only | |
string fn_csv = "croppedImagesBig/frecAll.txt"; //path to the csv file. | |
//string fn_csv = "Images/BigImages.txt"; //path to the csv file. | |
string fn_haar = "C:\\opencv243\\data\\haarcascades\\haarcascade_frontalface_alt.xml"; //.._default.xml"; | |
string fn_lbp = "C:\\opencv243\\data\\lbpcascades\\lbpcascade_frontalface.xml"; //.._profileface.xml, lbpcascade_frontalface.xml | |
string eye_cascade_name = "C:/opencv243/data/haarcascades/haarcascade_eye_tree_eyeglasses.xml";//haarcascade_mcs_eyepair_small";//haarcascade_eye_tree_eyeglasses.xml"; | |
vector<Mat> images;//These vectors hold the images and corresponding labels. | |
vector<int> labels; | |
int training_im_width; // for video face recog. | |
int training_im_height; | |
bool showResult = false; // true: show the eigenvalues, eigenvectors, etc, false: save resuts in files | |
Ptr<FaceRecognizer> model; // Face recognizer model for eigenfaces. | |
CascadeClassifier haar_cascade; // cascade classifier (to detect faces, or what in needed) | |
CascadeClassifier eye_cascade; // Eye detector | |
int *sklBelongsToFaceId; | |
bool *skeletonHasFaceId; | |
/* | |
Initialize the kinect. | |
*/ | |
bool initKinect() { | |
//get a working kinect sensor | |
int numSensors; | |
if (NuiGetSensorCount(&numSensors) < 0 || numSensors < 1) return false; | |
if (NuiCreateSensorByIndex(0, &sensor) < 0) return false; | |
nextSkeletonFrame = new NUI_SKELETON_FRAME; | |
m_bArrSkeletonsTracked = new bool[NUI_SKELETON_COUNT]; | |
// init face data holder | |
skeletonHasFaceId = new bool [NUI_SKELETON_COUNT]; | |
sklBelongsToFaceId = new int [NUI_SKELETON_COUNT]; // for each skeleton, a face label will be assigned, if mathched. | |
for (int i = 0; i < NUI_SKELETON_COUNT; ++i ) | |
{ | |
m_bArrSkeletonsTracked[i] = false; | |
skeletonHasFaceId[i] = false; | |
sklBelongsToFaceId[i] = -1; | |
} | |
// Initialize sensor | |
DWORD nuiFlags = NUI_INITIALIZE_FLAG_USES_DEPTH | NUI_INITIALIZE_FLAG_USES_COLOR | NUI_INITIALIZE_FLAG_USES_SKELETON; | |
sensor->NuiInitialize(nuiFlags); | |
NUI_IMAGE_RESOLUTION kinect_resolution; | |
useBigPic? kinect_resolution = NUI_IMAGE_RESOLUTION_1280x960 : kinect_resolution = NUI_IMAGE_RESOLUTION_640x480; | |
// Get Color Data | |
sensor->NuiImageStreamOpen(NUI_IMAGE_TYPE_COLOR, // Depth camera or rgb camera ? | |
kinect_resolution ,//NUI_IMAGE_RESOLUTION_640x480, // Image resolution | |
0, // Image stream flags, e.g. near mode | |
2, // Number of frames to buffer | |
NULL, // Event Handle | |
&rgbStream); | |
// Get Skeleton Data | |
sensor->NuiImageStreamOpen( | |
HasSkeletalEngine(sensor) ? NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX : NUI_IMAGE_TYPE_DEPTH, | |
NUI_IMAGE_RESOLUTION_640x480, // Change? to 640x480 later. | |
0, | |
2, | |
NULL, | |
&skeletonStream); | |
cout <<"Kinect initialized!\n"; | |
return sensor; | |
} | |
/* | |
Retrieves the RGB image of the kinect. | |
*/ | |
void getKinectColorData(IplImage* dest) { | |
NUI_IMAGE_FRAME imageFrame; | |
NUI_LOCKED_RECT lockedRect; | |
if (sensor->NuiImageStreamGetNextFrame(rgbStream,0, &imageFrame) < 0) return; | |
INuiFrameTexture* texture = imageFrame.pFrameTexture; | |
texture->LockRect(0, &lockedRect, NULL, 0); | |
if (lockedRect.Pitch != 0) | |
{ | |
cvSetData(dest, static_cast<BYTE *>(lockedRect.pBits), | |
dest->widthStep); | |
} | |
texture->UnlockRect(0); | |
sensor->NuiImageStreamReleaseFrame(rgbStream, &imageFrame); | |
} | |
/* | |
Retrieves the kinect's skeleton data | |
*/ | |
void getKinectSkeletalData() { | |
bool bFoundSkeleton = false; | |
float fClosestDistance = 0; | |
if (SUCCEEDED(sensor->NuiSkeletonGetNextFrame(0, nextSkeletonFrame))) { | |
for (int i = 0; i < NUI_SKELETON_COUNT; i++) { | |
if (nextSkeletonFrame->SkeletonData[i].eTrackingState == NUI_SKELETON_TRACKED) // might need revision : && m_bAppTracking is ommited | |
{ | |
if ( !m_bArrSkeletonsTracked[i] ) | |
{ | |
cout << " * Skeleton " << i << " found at " << nextSkeletonFrame->SkeletonData[i].Position.z | |
<< " meters from camera, " << (int) (clock() / CLOCKS_PER_SEC) << " seconds after startup.\n\n"; | |
if (fClosestDistance == 0 || fClosestDistance > nextSkeletonFrame->SkeletonData[i].Position.z ) | |
{ | |
fClosestDistance = nextSkeletonFrame->SkeletonData[i].Position.z; | |
} | |
m_bArrSkeletonsTracked[i] = true; | |
} | |
bFoundSkeleton = true; | |
} | |
else if (m_bArrSkeletonsTracked[i]) { | |
cout << " * Skeleton " << i << " lost.\n\n"; | |
m_bArrSkeletonsTracked[i] = false; | |
skeletonHasFaceId[i] = false; | |
} | |
} | |
} | |
// no skeletons! | |
if ( !bFoundSkeleton) { | |
return; | |
} | |
// smooth out the skeleton | |
sensor->NuiTransformSmooth(nextSkeletonFrame, NULL); | |
} | |
/* | |
Scales the skeleton's 20 joints to the corresponding actual points in the RGB image. | |
*/ | |
void scaleTo(Vector4 aPoint, int *x, int *y) { | |
float fx = 0, fy = 0; | |
NuiTransformSkeletonToDepthImage(aPoint, &fx, &fy); | |
long l_x =0, l_y = 0; | |
NuiImageGetColorPixelCoordinatesFromDepthPixel(NUI_IMAGE_RESOLUTION_640x480, | |
NULL, | |
(long) fx, | |
(long) fy, | |
NULL, | |
&l_x, | |
&l_y); | |
*x = (int) l_x; | |
*y = (int) l_y; | |
} | |
/* | |
Capture and save the images in a folder with th same name as the picture owner. | |
These pictures will be used to crop faces to train the face recog. model. | |
*/ | |
void saveImages(string person){ | |
//create a folder named person | |
// | |
cout << | |
"*************************************************\n" << | |
"* Saving images for training purpose.\n"<< | |
"* Face to the camera and "; | |
system("pause"); | |
string image_folder = "Images/"; | |
image_folder.append(person.c_str()); | |
// create destination dir for images | |
CreateDirectory(image_folder.c_str(), NULL); | |
//IplImage* kinectImg = cvCreateImage(cvSize(imgWidth,imgHeight), IPL_DEPTH_8U, 4); | |
IplImage* kinectImgBig = cvCreateImage(cvSize(imgWidthBig,imgHeightBig), IPL_DEPTH_8U, 4); | |
for (int i = 0; i < 150 ; i++) { | |
getKinectColorData(kinectImgBig); | |
Mat image(kinectImgBig); | |
if (image.empty()) continue; | |
string fname = format("%s/%s/%s_%d.jpg", output_folder.c_str(), person.c_str(), person.c_str(), i); | |
//cout << "appended "<< fname<<endl; | |
//imwrite(fname, image); | |
imwrite(fname, image); | |
namedWindow(fname, CV_WINDOW_AUTOSIZE); | |
imshow(fname, image); | |
cout << "Image " << i<< " saved. Press a key to continue, Escape to end." << endl; | |
char key = (char)waitKey(0); | |
if (key == 27) | |
return; | |
destroyWindow(fname); | |
} | |
//cvReleaseImage(&kinectImgBig); | |
return; | |
} | |
/* | |
Finds and crops the faces from the captured images. These cropped faces will be used in model training. | |
*/ | |
Mat CropFaces( Mat frame ) | |
{ | |
Mat tFrame = frame.clone(); | |
std::vector<Rect> faces; | |
Mat frame_gray; | |
Mat cropped; | |
cvtColor(frame, frame_gray, CV_BGR2GRAY ); | |
equalizeHist( frame_gray, frame_gray); | |
// detect faces | |
haar_cascade.detectMultiScale(frame_gray, faces, 1.1, 2, | |
0|CV_HAAR_SCALE_IMAGE, Size(30,30) ); | |
for (int i = 0; i < faces.size(); i++ ) | |
{ | |
Point center(faces[i].x + faces[i].width*0.5, faces[i].y + faces[i].height*0.5); | |
ellipse( frame, center, Size( faces[i].width*0.5, faces[i].height*0.5), 0, 0, 360, | |
Scalar(255, 0, 255), 4, 8, 0); | |
cropped = tFrame (faces[i] ); | |
} | |
//show the crooped face | |
imshow("Face", frame); | |
waitKey(0); | |
return cropped; | |
} | |
/* | |
Histogram normalize the image. | |
*/ | |
static Mat norm_0_255(InputArray _src){ | |
Mat src = _src.getMat(); | |
//create and return normalized image: | |
Mat dst; | |
switch(src.channels()){ | |
case 1: | |
cv::normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC1); | |
break; | |
case 3: | |
cv::normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC3); | |
break; | |
default: | |
src.copyTo(dst); | |
break; | |
} | |
return dst; | |
} | |
// | |
// Calculates the TanTriggs Preprocessing as described in: | |
// | |
// Tan, X., and Triggs, B. "Enhanced local texture feature sets for face | |
// recognition under difficult lighting conditions.". IEEE Transactions | |
// on Image Processing 19 (2010), 1635\96650. | |
// | |
// Default parameters are taken from the paper. | |
// | |
Mat tan_triggs_preprocessing(InputArray src, | |
float alpha = 0.1, float tau = 10.0, float gamma = 0.2, int sigma0 = 1, | |
int sigma1 = 2) { | |
// Convert to floating point: | |
Mat X = src.getMat(); | |
X.convertTo(X, CV_32FC1); | |
// Start preprocessing: | |
Mat I; | |
pow(X, gamma, I); | |
// Calculate the DOG Image: | |
{ | |
Mat gaussian0, gaussian1; | |
// Kernel Size: | |
int kernel_sz0 = (3*sigma0); | |
int kernel_sz1 = (3*sigma1); | |
// Make them odd for OpenCV: | |
kernel_sz0 += ((kernel_sz0 % 2) == 0) ? 1 : 0; | |
kernel_sz1 += ((kernel_sz1 % 2) == 0) ? 1 : 0; | |
GaussianBlur(I, gaussian0, Size(kernel_sz0,kernel_sz0), sigma0, sigma0, BORDER_CONSTANT); | |
GaussianBlur(I, gaussian1, Size(kernel_sz1,kernel_sz1), sigma1, sigma1, BORDER_CONSTANT); | |
subtract(gaussian0, gaussian1, I); | |
} | |
{ | |
double meanI = 0.0; | |
{ | |
Mat tmp; | |
pow(abs(I), alpha, tmp); | |
meanI = mean(tmp).val[0]; | |
} | |
I = I / pow(meanI, 1.0/alpha); | |
} | |
{ | |
double meanI = 0.0; | |
{ | |
Mat tmp; | |
pow(min(abs(I), tau), alpha, tmp); | |
meanI = mean(tmp).val[0]; | |
} | |
I = I / pow(meanI, 1.0/alpha); | |
} | |
// Squash into the tanh: | |
{ | |
for(int r = 0; r < I.rows; r++) { | |
for(int c = 0; c < I.cols; c++) { | |
I.at<float>(r,c) = tanh(I.at<float>(r,c) / tau); | |
} | |
} | |
I = tau * I; | |
} | |
return I; | |
} | |
/* | |
Reads a csv file containing training images as well as corresponding labels. | |
*/ | |
static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator = ';'){ | |
std::ifstream file(filename.c_str(), ifstream::in); | |
if (!file) { | |
string error_message = "No valid input file was given, please check the given file name"; | |
CV_Error(CV_StsBadArg, error_message); | |
} | |
string line, path, classlabel; | |
while (getline(file, line)) { | |
stringstream liness(line); | |
getline(liness, path, separator); | |
getline(liness, classlabel); | |
if (!path.empty() && !classlabel.empty()) { | |
Mat img = imread(path,0); | |
//imshow("Image", img); | |
//waitKey(0); | |
//Mat processed = tan_triggs_preprocessing(img); | |
//processed = norm_0_255(processed); | |
images.push_back(img); | |
labels.push_back(atoi(classlabel.c_str())); | |
} | |
} | |
} | |
/* | |
Returns true if the skeleton with specified ID is available. | |
*/ | |
bool skeletonAvailable(int skltnId) | |
{ | |
return m_bArrSkeletonsTracked[skltnId]; | |
} | |
/* | |
Checks if a face label is associated with the specified skeleton. | |
*/ | |
bool skeletonHasFaceLabel(int skeletonId) | |
{ | |
return skeletonHasFaceId[skeletonId]; | |
} | |
/* | |
Returns the label associated to the skeleton. | |
*/ | |
int getSkeletonFaceLabel(int skeletonId) | |
{ | |
return sklBelongsToFaceId[skeletonId]; | |
} | |
/* | |
Draws the given skeleton joints on the given Mat image dest. If drawLabel == true, also shows the associated label along with a circle on the head position. | |
*/ | |
void drawSkeletonByID(int id, Mat *dest, bool drawLabel) | |
{ | |
/* | |
- id : skeleton id | |
- *dest : destination image joints to be drawn | |
- drawLabel : true ? show assigned skltn label : do not show label | |
*/ | |
CvPoint jointPos = cvPoint(200,200); | |
int xs = 0, ys = 0; | |
for (int i = 0; i < NUI_SKELETON_COUNT; ++i) { | |
if ( i == id ){ // draw selected skeleton only | |
if (nextSkeletonFrame->SkeletonData[i].eTrackingState == NUI_SKELETON_TRACKED) { | |
if ( !drawLabel ) { //show all joints | |
for (int j = 0; j < NUI_SKELETON_POSITION_COUNT; ++j) { | |
scaleTo(nextSkeletonFrame->SkeletonData[i].SkeletonPositions[j], &xs, &ys); | |
jointPos.x = xs; | |
jointPos.y = ys; | |
circle(*dest, jointPos, 10, CV_RGB(235,30,0),-1); | |
} | |
} | |
else // Draw head joint only with its label | |
{ | |
scaleTo(nextSkeletonFrame->SkeletonData[i].SkeletonPositions[NUI_SKELETON_POSITION_HEAD], &xs, &ys); | |
jointPos.x = xs; | |
jointPos.y = ys; | |
circle(*dest, jointPos, 30, CV_RGB(235,30,220),2); | |
int predictedLabel = getSkeletonFaceLabel(i); | |
if ( predictedLabel == -1 ) // face not detected | |
return; | |
// Create the text we will annotate the face with | |
string box_text = format("SID=%d", predictedLabel); | |
//calculate the position of the annotated text: | |
int pos_x = std::max(xs - 40, 0); | |
int pos_y = std::max(ys - 40, 0); | |
// Put the text in the image | |
putText(*dest, box_text, Point(pos_x, pos_y), FONT_HERSHEY_PLAIN, 1.0, CV_RGB(235,30,0), 2.0); | |
} | |
} | |
} | |
} | |
} | |
/* | |
Assigns the given label to the corresponding head position in the skeleton data. | |
*/ | |
void assignFaceToSkeleton(Point headPoint, int fLabel) | |
{ | |
int x1 = 0 ,y1 = 0; | |
for (int i = 0 ; i < NUI_SKELETON_COUNT; i++ ) | |
{ | |
float dThreshold = 15; // distance :50 | |
if (m_bArrSkeletonsTracked[i] && !skeletonHasFaceLabel(i)) // if skeleton is tracked, and not has label yet: | |
{ | |
// obtain the head joint position coordiates | |
x1 = (int) nextSkeletonFrame->SkeletonData[i].SkeletonPositions[NUI_SKELETON_POSITION_HEAD].x; | |
y1 = (int) nextSkeletonFrame->SkeletonData[i].SkeletonPositions[NUI_SKELETON_POSITION_HEAD].y; | |
// see if any of detected face coordinates mathes(close enough) | |
float dist = sqrt ((float) ((x1 - headPoint.x)^2 + (y1 - headPoint.y)^2)); | |
if (dist <= dThreshold) | |
{ | |
dThreshold = dist; | |
skeletonHasFaceId[i] = true; | |
sklBelongsToFaceId[i] = fLabel; | |
} | |
} | |
} | |
return; | |
} | |
/* | |
Performs a face detection in the given image and finds the faces. | |
*/ | |
void detecFaces(Mat *original) | |
{ | |
/* Recognition */ | |
//convert current image to grayscale | |
Mat gray; | |
cvtColor(*original, gray, CV_BGR2GRAY); | |
//find the faces in the frame | |
vector< Rect_<int> > faces; | |
CvPoint faceCenter = cvPoint(0,0); // Detected face coordinates in RGB image. | |
/* To speed up: */ | |
//1. reduce the size of the gray image before feeding to haar_cascade if the faces are much larger than 24x24 pixels: | |
//find:small_image at http://www710.univ-lyon1.fr/~bouakaz/OpenCV-0.9.5/docs/ref/OpenCVRef_Experimental.htm | |
int downgradeScale = 2; // subject to change to 4? if using 1280x960 resolution. | |
Mat small_gray; | |
// Downscale the image: | |
pyrDown(gray, small_gray, cvSize(gray.cols/downgradeScale, gray.rows/downgradeScale)); | |
// Search the faces in downscaled gray image: | |
if (modelName == "LBPH" || modelName == "lbph") { | |
haar_cascade.detectMultiScale(small_gray, faces, 1.2, 3, CV_HAAR_DO_CANNY_PRUNING, Size(50,50)); | |
} else { | |
haar_cascade.detectMultiScale(small_gray, faces, 1.2, 3, CV_HAAR_DO_ROUGH_SEARCH | CV_HAAR_DO_CANNY_PRUNING | | |
CV_HAAR_SCALE_IMAGE, Size(40, 40)); // , Size(150,150): max size | |
} | |
// At this point we have the position of faces in faces. | |
// Now, we;ll get the faces, make a prediction and annotate | |
// it in the video. Coooooool! | |
for (int i = 0; i < faces.size(); i++) { | |
// Process face by face | |
// scale up then crop | |
Rect face_i; | |
face_i.x = faces[i].x * downgradeScale; | |
face_i.width = faces[i].width * downgradeScale; | |
face_i.y = faces[i].y * downgradeScale; | |
face_i.height = faces[i].height * downgradeScale; | |
//Crop the face from the image, so simple with opencv | |
Mat face = gray(face_i); | |
// Return the image to the original size? and then feed to the prediction. | |
int prediction = -1; | |
double confidence = 0.0; | |
if (modelName == "LBPH" || modelName == "lbph") { | |
//prediction = model->predict(face); | |
model->predict(face, prediction, confidence); | |
} | |
else | |
{ | |
// FOR FISHER and EIGEN MODEL | |
// Resizing the face is necessary for Fisgerfaces and Eigrnfaces, but not | |
// for Local Binary Pattern Histograms. So this part depends on the algorithm used. | |
Mat face_resized; | |
///********* forget: (corrected: seems legit?)Probelmatic!? ONLY RESIZE THE FACE NOT HOLE IMAGE:Size(training_im_width, training_im_height) | |
cv::resize(face, face_resized,Size(training_im_width, training_im_height), 1.0, 1.0, INTER_CUBIC); | |
// Now perform the prediction | |
//prediction = model->predict(face_resized); | |
model->predict(face_resized, prediction, confidence); | |
face_resized.release(); | |
} | |
cout << modelName <<" -> Predicted: "<< prediction<< " with confidence: "<< confidence << endl; | |
// And finally write all we;ve found to the original image. | |
// First, draw a green rectangle arud the detected face: | |
CvPoint faceRectP1 = cvPoint(face_i.x, face_i.y); | |
CvPoint faceRectP2 = cvPoint((face_i.x+face_i.width), | |
(face_i.y+face_i.height)); | |
Rect fRect = Rect(faceRectP1, faceRectP2); | |
rectangle(*original, faceRectP1, faceRectP2, CV_RGB(0,255,0), 3); | |
// get the face center | |
faceCenter.x = ( faceRectP1.x + faceRectP2.x ) / 2; | |
faceCenter.y = ( faceRectP2.y + faceRectP2.y ) /2; | |
// Create the text we will annotate the face with | |
string box_text = format("P=%d", prediction); | |
//calculate the position of the annotated text: | |
int pos_x = std::max(face_i.tl().x - 10, 0); | |
int pos_y = std::max(face_i.tl().y - 10, 0); | |
// Put the text in the image | |
putText(*original, box_text, Point(pos_x, pos_y), FONT_HERSHEY_PLAIN, 2.0, CV_RGB(0,255,0), 3.0); | |
//cout << " Face Position of RGB Image " << i << " at : (" << faceCenter.x << ", " << faceCenter.y << ")\n"; | |
/* Detect eyes*/ | |
/* | |
vector<Rect> eyes; | |
eye_cascade.detectMultiScale(face, eyes, 1.1, 2, | |
0|CV_HAAR_SCALE_IMAGE, Size(5,5) ); | |
for ( int j = 0; j < eyes.size(); j++ ) | |
{ | |
Point center ( faces[i].x + eyes[j].x + eyes[j].width*0.5, | |
faces[i].y + eyes[j].y + eyes[j].height*0.5); | |
int radius = cvRound( (eyes[j].width + eyes[j].height*0.25)); | |
circle(*original,center, radius, Scalar(30, 150, 220), 4, 8, 0); | |
} | |
*/ | |
//assign face to a skeleton | |
assignFaceToSkeleton(Point(pos_x, pos_y), prediction); | |
face.release(); | |
} | |
} | |
/* | |
Draws all of the available skeletons. | |
*/ | |
void drawAllSkeletons (Mat *anImage) | |
{ | |
for (int i = 0; i < NUI_SKELETON_COUNT; i++ ) | |
{ | |
drawSkeletonByID(i, anImage, false); | |
} | |
} | |
/* | |
Check if a file already exists with the given name. | |
*/ | |
bool fileExist(string filename) | |
{ | |
GetFileAttributes(filename.c_str()); | |
if (INVALID_FILE_ATTRIBUTES == GetFileAttributes(filename.c_str()) && GetLastError() == ERROR_FILE_NOT_FOUND) | |
return false; | |
return true; | |
} | |
int main() { | |
if (!initKinect()) return 1; | |
Sleep(1000); // Wait for initialization | |
// if we are taking the images for traing purpose, enter to the picture mode. | |
// The program terminates once the operation is done. To start over change the | |
// status to "pictureMode = false". | |
if (pictureMode) | |
{ | |
saveImages(pictureOwner); | |
//cout << "Finished saving images." << endl; | |
//system("pause"); | |
exit(0); | |
} | |
// Load images and corresponding labels: | |
try { | |
read_csv(fn_csv, images, labels); | |
} catch(cv::Exception& e) { | |
cerr <<"Error opening file \""<<fn_csv << "\". Reason: " <<e.msg << endl; | |
system("pause"); | |
exit(1); | |
} | |
//Quit if there are not enough images for training. | |
if (images.size() <= 1) { | |
string error_message = "This demo needs at least 2 images to work. Please add more images."; | |
CV_Error(CV_StsError, error_message); | |
system("pause"); | |
} | |
//get the hight from first image. We'll need this later in code to reshape | |
// the incoming images to their original size: | |
training_im_width = images[0].cols; | |
training_im_height = images[0].rows; | |
// To save or load the model with name | |
//string modelStr = format("%s-model.yml", modelName); | |
string modelStr = modelName + "_model.yml"; | |
/***********************************************************************************/ | |
if ( modelName == "Fisher" || modelName == "fisher") | |
model = createFisherFaceRecognizer(); | |
else if (modelName == "Eigen" || modelName == "eigen") | |
model = createEigenFaceRecognizer(); | |
else if (modelName == "LBPH" || modelName == "lbph") | |
model = createLBPHFaceRecognizer(); | |
else { | |
cout << "Please provide a valid recognizer type:" << endl; | |
cout << "\"Fisher\", \"Eigen\" or \"LBPH\"" << endl; | |
system("pause"); | |
exit(1); | |
} | |
// Now we are going to use the haar cascade you have specified in the command line | |
if (modelName == "LBPH" || modelName == "lbph") { | |
haar_cascade.load(fn_lbp); | |
} else { | |
haar_cascade.load(fn_haar); | |
} | |
if( !eye_cascade.load(eye_cascade_name)) {printf(" Error loading eye cascade"); return -1;} | |
if ( cropMode ) | |
{ | |
Mat frame = images[images.size() - 1]; | |
images.pop_back(); | |
imshow("Poped Image", frame); | |
waitKey(10); | |
Mat cropped = CropFaces(frame); | |
//imshow("cropped", cropped); | |
waitKey(0); | |
exit(1); | |
} | |
// Training Phase | |
if (!useExistingModel) { | |
// train a new model | |
cout << "Training a new recognizer of type " << modelName << " model." << endl; | |
// Train the model | |
model->train(images, labels); | |
// Save the trained madel | |
model->save(modelStr); | |
} else { | |
// load the existing model | |
cout << "Loading the existing recognizer of type " << modelName<< " model." << endl; | |
model->load(modelStr); | |
cout << "Loading finished.\n"; | |
} | |
// requirements for recording the video | |
VideoWriter vid_unlabeled, vid_labeled, vid_skeleton; | |
Size vidSize = Size(imgWidth, imgWidth); | |
int codec = CV_FOURCC('F', 'L', 'V', '1');//CV_FOURCC('M', 'J', 'P', 'G'); | |
int fps = 4; | |
/**************************************/ | |
// Go! | |
// If video recording is on, prepare .. | |
if ( writeToVideo ) | |
{ | |
string pname = "oldLady"; // name of the person who is acting in the kitchen | |
string vidName_unlabeled = format("%s_unlabeledVideo.avi",pname.c_str()); | |
string vidName_labeled = format("%s_LabeledVideo.avi", pname.c_str()); | |
string vidName_skeleton = format("%s_skeletonVideo.avi", pname.c_str()); | |
// check that the video file name not already exists. | |
if ( fileExist( vidName_labeled.c_str()) || fileExist(vidName_skeleton.c_str()) || fileExist(vidName_unlabeled.c_str()) ) | |
{ | |
cout << "Error! Avi file already exist! Change the file name and "; | |
system("pause"); | |
return -8; | |
} | |
vid_unlabeled.open(vidName_unlabeled, codec , fps, vidSize, true); | |
if ( !vid_unlabeled.isOpened()) | |
{ | |
cout << "Error opening "<<vidName_unlabeled << " "; | |
system("pause"); return -1; | |
} | |
vid_labeled.open(vidName_labeled, codec, fps, vidSize, true); | |
if ( !vid_labeled.isOpened()) | |
{ | |
cout << "Error opening "<<vidName_labeled << " "; | |
system("pause"); return -1; | |
} | |
vid_skeleton.open(vidName_skeleton, codec, fps, vidSize, true); | |
if ( !vid_skeleton.isOpened()) | |
{ | |
cout << "Error opening "<<vidName_skeleton << " "; | |
system("pause"); return -1; | |
} | |
} | |
for (;;) { | |
int tic = (int) (clock()); // start time | |
if ( useBigPic) | |
{ | |
getKinectSkeletalData(); | |
getKinectColorData(kinectColorImageBig); | |
Mat originalBig(kinectColorImageBig); | |
Mat rs; | |
resize(originalBig, rs, Size(imgWidth, imgHeight)); | |
Mat imSkeleton = rs.clone(); | |
Mat rgbImage = originalBig.clone(); | |
detecFaces(&rgbImage); | |
drawAllSkeletons(&imSkeleton); | |
Mat rgb_rsz; | |
resize(rgbImage, rgb_rsz, Size(imgWidth, imgHeight)); | |
for ( int i = 0; i < NUI_SKELETON_COUNT; i++ ) | |
{ | |
if ( skeletonAvailable(i) && skeletonHasFaceLabel(i) ) | |
drawSkeletonByID( i , &rgb_rsz, true); | |
} | |
/*Mat rgb_rsz; | |
resize(rgbImage, rgb_rsz, Size(640,480));*/ | |
/* Write to video files */ | |
// Get the elapsed time | |
int t_elapsed = clock() / CLOCKS_PER_SEC; | |
string t_string = format("Elapsed time: %d", t_elapsed); | |
putText(rgb_rsz, t_string, Point(300, 100), CV_FONT_HERSHEY_PLAIN, 2.0, Scalar(255, 0, 0), 2); | |
if ( writeToVideo ) | |
{ | |
Mat aFrame; | |
// convert the color, otherwise videowriter does not work. | |
cvtColor(rs, aFrame, CV_RGB2BGR); | |
// conver back again to retain the original colors | |
cvtColor(aFrame, aFrame, CV_BGR2RGB); | |
resize(aFrame, aFrame, vidSize); | |
//imshow("aFrame", aFrame); | |
//waitKey(10); | |
vid_unlabeled << aFrame; | |
vid_unlabeled << aFrame; | |
vid_unlabeled << aFrame; | |
vid_unlabeled << aFrame; | |
Mat sFrame; | |
cvtColor(imSkeleton, sFrame, CV_RGB2BGR); | |
cvtColor(sFrame, sFrame, CV_BGR2RGB); | |
// resize the image to be square (height = width), otherwise videowriter doesnt work. | |
resize(sFrame, sFrame, vidSize); | |
vid_skeleton << sFrame; | |
vid_skeleton << sFrame; | |
vid_skeleton << sFrame; | |
vid_skeleton << sFrame; | |
//waitKey(30); | |
Mat rgb_vid; | |
resize(rgb_rsz, rgb_vid, vidSize); | |
cvtColor(rgb_vid, rgb_vid, CV_RGB2BGR); | |
cvtColor(rgb_vid, rgb_vid, CV_BGR2RGB); | |
vid_labeled << rgb_vid; | |
vid_labeled << rgb_vid; | |
vid_labeled << rgb_vid; | |
vid_labeled << rgb_vid; | |
//waitKey(30); | |
} | |
//imshow("Face recognizer(Esc to exit)", rgb_rsz); | |
// put images together | |
// | |
//Mat twoImages = Mat::zeros(Size(imgWidth*2+3, imgHeight), rgb_rsz.type()); | |
//Rect roi = Rect(0,0, imgWidth, imgHeight); | |
//Mat roiImage = twoImages(roi); | |
//rs.copyTo(roiImage); | |
//// move roi | |
//roi.x = roi.x + imgWidth+2; | |
//roiImage = twoImages(roi); | |
//rgb_rsz.copyTo(roiImage); | |
//namedWindow("Skeleton Joints(Esc to exit)", CV_WINDOW_AUTOSIZE); | |
//imshow("Skeleton Joints(Esc to exit)", twoImages); | |
// | |
namedWindow("Skeleton Joints(Esc to exit)", CV_WINDOW_AUTOSIZE); | |
resize(imSkeleton, imSkeleton, Size(320, 240)); | |
imshow("Skeleton Joints(Esc to exit)", imSkeleton); | |
namedWindow("Face recognizer(Esc to exit)", CV_WINDOW_AUTOSIZE); | |
imshow("Face recognizer(Esc to exit)",rgb_rsz); | |
//cout << t_elapsed << endl; | |
// convert the RGB image to BGR, otherwise videowriter won't work!! | |
char key = (char) waitKey(30); | |
// Exit the loop on escape: | |
if (key == 27) | |
{ | |
vid_unlabeled.release(); | |
vid_labeled.release(); | |
vid_skeleton.release(); | |
break; | |
} | |
//clean-up | |
originalBig.release(); | |
imSkeleton.release(); | |
//rgb_rsz.release(); | |
rs.release(); | |
int toc = (int) (clock()); // end time | |
cout << " One cycle done in "<< toc - tic << " miliseconds" << endl; | |
} | |
else // normal size images | |
{ | |
getKinectSkeletalData(); | |
getKinectColorData(kinectColorImage); | |
Mat original(kinectColorImage); | |
Mat imSkeleton = original.clone(); | |
Mat rgbImage = original.clone(); | |
detecFaces(&rgbImage); | |
drawAllSkeletons(&imSkeleton); | |
for ( int i = 0; i < NUI_SKELETON_COUNT; i++ ) | |
{ | |
if ( skeletonAvailable(i) && skeletonHasFaceLabel(i) ) | |
drawSkeletonByID( i , &rgbImage, true); | |
} | |
// Show the result | |
namedWindow("Skeleton Joints(Esc to exit)", CV_WINDOW_AUTOSIZE); | |
imshow("Skeleton Joints(Esc to exit)", imSkeleton); | |
namedWindow("Face recognizer(Esc to exit)", CV_WINDOW_AUTOSIZE); | |
imshow("Face recognizer(Esc to exit)", rgbImage); | |
char key = (char) waitKey(10); | |
// Exit the loop on escape: | |
if (key == 27) | |
break; | |
//clean-up | |
original.release(); | |
imSkeleton.release(); | |
rgbImage.release(); | |
} | |
//vid_unlabeled.release(); | |
//vid_labeled.release(); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment