CS4670 Final Report Hand Gesture Detection and Recognition For Human-Computer Interaction
CS4670 Final Report Hand Gesture Detection and Recognition For Human-Computer Interaction
I. Abstract:
This project deals with the detection and recognition of hand gestures. Images of the
hand gestures are taken using a Nokia N900 cell phone and matched with the
images in the database and the best match is returned. Gesture recognition is one of
the essential techniques to build user-friendly interfaces. For example, a robot that
can recognize hand gestures can take commands from humans, and for those who
are unable to speak or hear, having a robot that can recognize sign language would
allow them to communicate with it. Hand gesture recognition could help in video
gaming by allowing players to interact with the game using gestures instead of using
a controller. However, such an algorithm needs to be more robust to account for the
myriad of possible hand positions in three-dimensional space. It also needs to work
with video rather than static images. That is beyond the scope of our project.
II. Overview:
Distance Transform: The distance transform is an operator normally only applied to
binary images. The result of the transform is a grayscale image that looks similar to
the input image, except that the intensities of points inside foreground regions are
changed to show the distance to the closest boundary from each point.
For example,
In the above image, each pixel p of the object is labeled by the distance to the closest
point q in the background.
Contours: Contours are sequences of points defining a line/curve in an image.
Contour matching can be used to classify image objects.
Database: Contains the images of various hand gestures.
Moments: Image moments are useful to describe objects after segmentation. Simple
properties of the image, which are found via image moments, include area (or total
intensity), its centroid and information about its orientation.
Ratio of the two distance transformed images of the same size = (No of pixels whose
difference is zero or less than a certain threshold) / (Total number of pixels in the
distance transformed image)
III. Design:
Step 1: User takes a picture of the hand to be tested either through the cell phone
camera or from the Internet.
Step 2: The image is converted into gray scale and smoothed using a Gaussian
kernel.
Step 3: Convert the gray scale image into a binary image. Set a threshold so that the
pixels that are above a certain intensity are set to white and those below are set to
black.
Step 4: Find contours, then remove noise and smooth the edges to smooth big
contours and melt numerous small contours.
Step 5: The largest contour is selected as a target.
Step 6: The angles of inclination of the contours and also the location of the center of
the contour with respect to the center of the image are obtained through the
bounding box information around the contour.
Step 7: The hand contours inside the bounding boxes are extracted and rotated in
such a way that the bounding boxes are made upright (inclination angle is 0) so that
matching becomes easy.
Step 8: Both the images are scaled so that their widths are set to the greater of the
two widths and their heights are set to the greater of the two heights. This is done
so that the images are the same size.
Step 9: The distance transform of both the query image and the candidate images
are computed and the best match is returned.
IV. Constraints:
1. The picture of the hand must be taken against a dark background
2. The program recognizes a limited number of gestures as long as there are
gestures similar to them in the database.
3. We must have each gesture with at least four orientations at 90 degrees each to
return the best match.
VI. Results:
Case 1:
(A)
In the above case, we can see the query image on the left hand side and the matched
candidate image from the database on the right.
Now, the distance transforms of both the query image and the candidate images are
computed as follows:
Now, the difference of the two image images is computed and the ratio of the match
is found by the number of pixels whose difference between the two corresponding
pixels is zero or below a certain threshold divided by the total number of pixels in
one of the image. If the ratio is above 65% then the candidate image is determined
as a match and returned.
(B)
Case 2:
In the above case, the hand gesture on the left hand side is slightly tilted and still the
right gesture from the database is returned.
Case 3:
Case 4:
In the above case, the program returns the right gesture image, even though the
database doesnt contain the left hand gesture as in the query image because, the
candidate image passes the ratio test.
Case 5:
If a similar gesture as the query image is not present in the database then a nomatch is returned.
Case 6:
Cases of False Positives. The right gesture is returned but not logical.
VIII. Conclusion:
Based on our observation, we can conclude that the results mainly depend on:
1. Threshold, while converting the gray image to the binary image and finding
contours. For example found that uneven lighting across the picture of the hand
caused the algorithm to draw contours around the darkened areas in addition to the
contour around the hand. Changing the threshold prevented that from happening.
2. The threshold for the Ratio test while matching the distance transformed images.
The ratio we used
3. The background, which must preferably be black to get accurate results.
4. An additional check on moments is useful to check if the contours of both the
query image and the candidate image have the same shape.
5. In order to maintain performance the database contains images of small
dimensions.
cvContourArea ()
XII. Functions implemented by our own:
1. rotate_inverse_warp()
This function extracts the image contained within the bounding box, rotates the image
in the negative direction (the angle is obtained from the bounding box) and creates a
new image.
2. distance transform
This function uses the algorithm described in Euclidean Distance Transform, which
replaces binary image pixel intensities with values equal to their Euclidean distance from
the nearest edge. While we implemented this function, we were unable to get it to
work completely, so we ended up using OpenCVs cvDistTransform().
XIII. Distribution of Work
Work done by Jonathan:
Wrote functions for rotation, scaling, and distance transform. Debugged code, worked
on the report, the presentation slides, and presented the slides during the final
presentation.
Work done by Roopa:
Wrote the main pipeline for the project and also, got the program working on the cell
phone. Debugged code, worked on the report, the presentation slides, and presented
the slides during the final presentation.
XIV: Platform
Ubuntu, OpenCV, C++, Nokia900 (phone device)
XV: Outside Sources
1. The hand gesture images were taken from Google Images.
2. Distance Transform. David Coeurjolly.
3. Euclidean Distance Transform, from http://www.cs.auckland.ac.nz/~rklette
Acknowledgements
Wed like to thank Professor Noah Snavely and Kevin Matzen for guiding and assisting us
during this project and the course.
ang2 = box2.angle;
// for size
CvSize2D32f siz2 = box2.size;
double wid2 = siz2.width;
double hei2 = siz2.height;
/*
printf("Width and Height of Query_Image Box\n");
printf("Width : %f
Height : %f Angle : %f\n", wid2, hei2,
ang2);
*/
//find the center
CvPoint2D32f cen2 = box2.center;
double x2 = cen.x;
double y2 = cen.y;
//printf("Center (x, y) : (%f, %f)", x2, y2);
/********************/
IplImage* res = cvCreateImage(cvSize(cmp_img->width, cmp_img>height),IPL_DEPTH_8U,3);
res = convert3channel(gray_ath_img2);
// rotate and scale the image
IplImage* rot_sc_img = inverse_warp_rotate(bin_img1, box);//.angle);
IplImage* rot_sc_img2 = inverse_warp_rotate(bin_img2, box2);//.angle *
-1);
int sw, sh;
if (rot_sc_img->width > rot_sc_img2->width)
sw = rot_sc_img->width;
else
sw = rot_sc_img2->width;
if (rot_sc_img->height > rot_sc_img2->height)
sh = rot_sc_img->height;
else
sh = rot_sc_img2->height;
scale_img1 = cvCreateImage(cvSize(sw, sh),rot_sc_img->depth,
rot_sc_img->nChannels);
scale_img2 = cvCreateImage(cvSize(sw, sh),rot_sc_img2->depth,
rot_sc_img2->nChannels);
// resize the rotated image
cvResize(rot_sc_img, scale_img1);
cvResize(rot_sc_img2, scale_img2);
cvSaveImage( "s1.jpg",scale_img1);
cvSaveImage( "s2.jpg",scale_img2);
// we are computing the moments here
0);
rotsc1);
rotsc2);
= (float)count/(float)maxcount;
<< "\tcount : " << count;
<< "\tmaxcount : " << maxcount;
<< "\tratio : " << ratio;
<< endl;
if ( (ratio >= 0.5 and ratio <= 1.0) and (val >= 0.0 and val <=0.4))
//0.2
{
std::cout << "\nMatch Found with DT";
found = 1;
return res;
}
else
{
std::cout << "No Match Found";
res = cvLoadImage("no_match.jpg");
//cvShowImage("no match", res);
return res;
}
}
dist = 0;
dist8u1 = 0;
dist8u2 = 0;
dist8u = 0;
dist32s = 0;
edge = 0;
gray = 0;
if(flag == 1)
{
gray = cvLoadImage("rot1.jpg", 0);
edge = cvLoadImage("s1.jpg", 0);
}
else
{
gray = cvLoadImage("rot2.jpg", 0);
edge = cvLoadImage("s2.jpg", 0);
}
dist8u1 = cvCloneImage( gray );
dist8u2 = cvCloneImage( gray );
dist8u = cvCreateImage( cvGetSize(gray), IPL_DEPTH_8U, 3 );
dist32s = cvCreateImage( cvGetSize(gray), IPL_DEPTH_32S, 1 );
dist = cvCreateImage( cvGetSize(scale_img1), IPL_DEPTH_32F, 1 );
int edge_thresh = 50;
int msize = mask_size;
int _dist_type = dist_type;
cvThreshold( gray, edge, (float)edge_thresh, (float)edge_thresh,
CV_THRESH_BINARY );
cvDistTransform( edge, dist, _dist_type, msize, NULL, NULL );
/*
Note: This is the distance transform function that we wrote, although
it did not work as expected, so we ended up using cvDistTransform()
above. The definitions for distanceFunction1() and
distanceFunction2(), which are used in this function, are shown below
the function.
IplImage* dist_origin = cvCloneImage(edge);
dist_origin->depth = dist->depth;
for(int a = 0; a < dist->height; a++)
{
cout << "Row " << a << "/" << dist->height << ": ";
for(int b = 0; b < dist->width; b++)
{
CvScalar dTFvalue = cvGet2D(dist_origin, a, b);
dTFvalue.val[0] = distanceFunction2(b, a, dist_origin);
cvSet2D(dist, a, b, dTFvalue);
cout << dTFvalue.val[0] << " ";
}
cout << endl;
}
*/
cvConvertScale( dist, dist, 150.0, 0 );
cvPow( dist, dist, 0.5 );
cvConvertScale( dist, dist32s, 1.0, 0.5 );
cvAndS( dist32s, cvScalarAll(255), dist32s, 0 );
cvConvertScale( dist32s, dist8u1, 1, 0 );
return dist8u1;
}
/* These functions are used in our implementation of the distance
transform algorithm.
int distanceFunction1(int x, int y, IplImage *Input)
{
if(x >= 0 && x < Input->width && y >= 0 && y < Input->height)
{
CvScalar P = cvGet2D(Input, y, x);
if(P.val[0] > 0)
{
return distanceFunction1(x - 1, y, Input) + 1;
}
else
{
return 0;
}
}
}
int distanceFunction2(int x, int y, IplImage *Input)
{
int df1 = distanceFunction1(x, y, Input);
if(df1 != 0)
{
return min(df1, distanceFunction2(x + 1, y, Input) + 1);
}
else
{
return 0;
}
}*/
/* This function uses the Bounding Box information obtained from the
P
Q
R
S
=
=
=
=
cvGet2D(before,
cvGet2D(before,
cvGet2D(before,
cvGet2D(before,
Ny[0],
Ny[1],
Ny[2],
Ny[3],
Nx[0]);
Nx[1]);
Nx[2]);
Nx[3]);