fingertip in c++

Jun 29, 2011 at 8:14 AM

Hi.

I'm trying to implement a fingertip detector in c++ with openNI and OpenCV.

Here is my code:

#include "XnCppWrapper.h"
	using namespace xn;
#include "opencv/cv.h"
#include "opencv\cvaux.h"
#include "opencv/highgui.h"
	using namespace cv;
#include <iostream>
	using namespace std;

#define MAX_DEPTH 10000

#define CHECK_RC(rc)													\
	if (rc != XN_STATUS_OK)												\
	{																	\
		printf("Operazione fallita: %s\n", xnGetStatusString(rc));		\
		return false;													\
	}																	\



Context context;
DepthGenerator Xn_depth;
ImageGenerator Xn_image;
XnStatus nRetVal = XN_STATUS_OK;

int depth[MAX_DEPTH];
char *depth_data;

void myDrawContours(CvSeq* contour, IplImage* edge){
	
	CvScalar sc = cvScalar(0xff, 0xff, 0xff, 0);

	for( CvSeq* c=contour; c!=NULL; c=c->h_next )
		cvDrawContours( edge, c, sc, sc, 2, 1, 8 );

	cvShowImage("Contours", edge);

	return;
}

void myDrawFingers(vector<Point2i> p){

	Mat1f debugFrame(480, 640);

	for(int i = 0; i < (int)p.size(); i++)
		circle(debugFrame, p.at(i), 10, Scalar(1), -1);

	imshow("Points", debugFrame);

	return;
}

IplImage* split(IplImage* src){
	IplImage* r = cvCreateImage( cvGetSize(src), IPL_DEPTH_8U, 1 );
	IplImage* g = cvCreateImage( cvGetSize(src), IPL_DEPTH_8U, 1 );
	IplImage* b = cvCreateImage( cvGetSize(src), IPL_DEPTH_8U, 1 );
	
	cvSplit( src, r, g, b, NULL );
	
	IplImage* t = cvCreateImage( cvGetSize(src), IPL_DEPTH_8U, 1 );
	
	cvAddWeighted( r, 1./3., g, 1./3., 0.0, t );
	cvAddWeighted( t, 2./3., b, 1./3., 0.0, t );

	cvReleaseImage(&r);
	cvReleaseImage(&g);
	cvReleaseImage(&b);

	return t;
}

void myFindContours(IplImage* img, CvSeq** contour, CvMemStorage** storage){

	IplImage* temp = cvCreateImage(cvGetSize(img), (img)->depth, 1);
	temp = split(img);
	
	IplImage* ath = cvCreateImage(cvGetSize(temp), temp->depth, temp->nChannels);
	
	cvAdaptiveThreshold(temp, ath, 50);
		
	cvFindContours(ath, *storage, contour, sizeof(CvContour), CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
	*contour = cvApproxPoly(*contour, sizeof(CvContour), *storage, CV_POLY_APPROX_DP, 3, 1);

	cvReleaseImage(&temp);
	cvReleaseImage(&ath);

	return;
}

vector<Point2i> findFingerTips(CvSeq* sequence){

	IplImage* points = cvCreateImage(cvSize(640,480), 8, 3);
	CvSeqReader reader;

	vector< vector< Point > > pts;
	int j = 0;

	for(CvSeq* cpy_seq = sequence; cpy_seq != NULL; cpy_seq = cpy_seq->h_next ){
		
		pts.push_back( vector<Point>() );

		cvStartReadSeq( cpy_seq, &reader, 0 );
	
		for (int i = 0; i < cpy_seq->total; i++){
			Point current;
			CV_READ_SEQ_ELEM(current, reader); //??
			pts.at(j).push_back( current );
			//cvCircle(points, pts.at(j).at(i), 3, CV_RGB(255, 0, 0 ), -1);
		}

		j++;
	}

	vector<Point> fingerTips;
	
	for(int k = 0; k <= j; k++){
		Mat contourMat;
		contourMat = Mat(pts.at(k), true);

		double area = 0.0;
		area = contourArea(contourMat, false);

		if (area > 3000)  { //possible hand
			Scalar center = mean(contourMat);
			Point centerPoint = Point(center.val[0], center.val[1]);
			
						vector<Point> approxCurve (contourMat.cols, contourMat.rows);
/*PROBLEM*/				approxPolyDP(contourMat, approxCurve, 20, true);
						vector<int> hull;
/*POSSIBLE PROBLEM*/	Mat matForHull = Mat(approxCurve);
/*PROBLEM*/				convexHull(matForHull, hull);

			// find upper and lower bounds of the hand and define cutoff threshold (don't consider lower vertices as fingers)
			int upper = 640, lower = 0;
			for (int j=0; j<hull.size(); j++) {
				int idx = hull[j]; // corner index
				if (approxCurve[idx].y < upper) upper = approxCurve[idx].y;
				if (approxCurve[idx].y > lower) lower = approxCurve[idx].y;
			}
			float cutoff = lower - (lower - upper) * 0.1f;
			
			// find interior angles of hull corners
			for (int j=0; j<hull.size(); j++) {
				int idx = hull[j]; // corner index
				int pdx = idx == 0 ? approxCurve.size() - 1 : idx - 1; //  predecessor of idx
				int sdx = idx == approxCurve.size() - 1 ? 0 : idx + 1; // successor of idx
				Point v1 = approxCurve[sdx] - approxCurve[idx];
				Point v2 = approxCurve[pdx] - approxCurve[idx];
				float angle = acos( (v1.x*v2.x + v1.y*v2.y) / (norm(v1) * norm(v2)) );
			
				// low interior angle + within upper 90% of region -> we got a finger
				if (angle < 1 && approxCurve[idx].y < cutoff) {
					int u = approxCurve[idx].x;
					int v = approxCurve[idx].y;
						fingerTips.push_back(Point2i(u,v));
				}
			}
		}
	}

	return fingerTips;
}

void raw2depth(){
	int i;
	for ( i=0; i<MAX_DEPTH; i++) {
        float v = (float)i/MAX_DEPTH;
		v = powf(v, 2);
		v = v*36*256;
        depth[i] = v;
	} 
}

void depth2rgb(const XnDepthPixel* Xn_disparity){
	int i;
    
	for (i=0; i<307200; i++) {
		int pval = depth[Xn_disparity[i]];
		int lb = pval & 0xff;
		switch (pval>>8) {
			case 0:
				depth_data[3*i+0] = 255;
				depth_data[3*i+1] = 255-lb;
				depth_data[3*i+2] = 255-lb;
				break;
			case 1:
				depth_data[3*i+0] = 255;
				depth_data[3*i+1] = lb;
				depth_data[3*i+2] = 0;
				break;
			case 2:
				depth_data[3*i+0] = 255-lb;
				depth_data[3*i+1] = 255;
				depth_data[3*i+2] = 0;
				break;
			case 3:
				depth_data[3*i+0] = 0;
				depth_data[3*i+1] = 255;
				depth_data[3*i+2] = lb;
				break;
			case 4:
				depth_data[3*i+0] = 0;
				depth_data[3*i+1] = 255-lb;
				depth_data[3*i+2] = 255;
				break;
			case 5:
				depth_data[3*i+0] = 0;
				depth_data[3*i+1] = 0;
				depth_data[3*i+2] = 255-lb;
				break;
			default:
				depth_data[3*i+0] = 0;
				depth_data[3*i+1] = 0;
				depth_data[3*i+2] = 0;
				break;
		}
	}
}

bool rgbd_init(){
nRetVal = context.Init();
CHECK_RC(nRetVal);

nRetVal = Xn_depth.Create(context);
CHECK_RC(nRetVal);

nRetVal = Xn_image.Create(context);
CHECK_RC(nRetVal);
	
nRetVal = context.StartGeneratingAll();
CHECK_RC(nRetVal);

return true;
}

int main(int argc, char **argv) {

	if( !rgbd_init() )
		return 1;

	cvNamedWindow("Contours", CV_WINDOW_AUTOSIZE);
	cvNamedWindow("RGB", CV_WINDOW_AUTOSIZE);
	cvNamedWindow("Points", CV_WINDOW_AUTOSIZE);
	
	IplImage* rgbimg = cvCreateImageHeader(cvSize(640,480), 8, 3);

	raw2depth();
	depth_data = (char*)malloc(640*480*3);

	const XnRGB24Pixel* pImage;
	XnRGB24Pixel* ucpImage;
	vector<CvPoint> contours;
	vector<Point2i> finger;
	CvSeq* contour = NULL;
	
	char key;

	while (true) {
		nRetVal = context.WaitAndUpdateAll();
		if (nRetVal != XN_STATUS_OK){
			printf("Failed updating data: %s\n", xnGetStatusString(nRetVal));
			continue;
		}

		pImage = Xn_image.GetRGB24ImageMap();
    
		ucpImage = const_cast<XnRGB24Pixel*> (pImage);
		cvSetData(rgbimg, ucpImage, 640*3);
		cvShowImage("RGB",rgbimg);

		CvMemStorage* storage = cvCreateMemStorage();
		myFindContours(rgbimg, &contour, &storage);

		IplImage* edge = cvCreateImage(cvGetSize(rgbimg), rgbimg->depth, 3);
		myDrawContours(contour, edge);

		finger = findFingerTips(contour);

		myDrawFingers(finger);
    
		key = cvWaitKey(30);
		if (key==27)
			break;

		cvReleaseImage(&edge);
		cvReleaseMemStorage(&storage);
	}
	
	cvReleaseImageHeader(&rgbimg);
	context.Shutdown();
	return 0;
}

 

Where there are the comments /*PROBLEM*/ ther are some complications.

I obtain a matrix of 4291323215 elements the most part of wich are negative:

hull[4291323198](-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,-17891602,...,...)
it's obviously an error.
 Any idea that can help me?