这篇文章是Andrew Davison博士发布的有关自然用户界面(NUI)系列的一部分,内容涉及使用JavaCV从网络摄像头视频提要中检测手。
注意:可以从http://fivedots.coe.psu.ac.th/~ad/jg/nui055/下载本章的所有源代码。
第5章的彩色斑点检测代码(可从http://fivedots.coe.psu.ac.th/~ad/jg/nui05/获得 )可以用作其他形状分析器的基础,我将在此处进行说明。通过扩展它来检测手和手指。 在图1中,我的左手戴着黑手套。 我的Handy应用程序尝试查找并标记拇指,食指,中指,无名指和小指。 在指尖和手的重心(COG)之间绘制黄线。
图1.检测左手和手指。
我使用了第5章的HSVSelector应用程序来确定黑手套的合适HSV范围。 在执行图2所示的步骤之前,Handy会加载这些范围,以获取手部的轮廓,其COG和相对于水平面的方向。
图2。找到手轮廓。
图2中的各个阶段几乎与第5章第4.1节中的ColorRectDetector.findRect()方法执行的阶段相同。但是,Handy继续进行处理,使用凸包和凸凹缺陷来定位并标记手中的指尖轮廓。 这些附加步骤如图3所示。
图3.查找和标记指尖。
船体和缺陷是通过标准的OpenCV操作从轮廓获得的,我将在下面进行解释。 但是,命名手指的最后一步使用了一种颇为怪异的策略,该策略假定轮廓的缺陷是针对伸出的左手。 拇指和食指基于它们相对于COG的角度位置来定位,而其他手指则根据它们相对于那些手指的位置来标识。 这个过程非常脆弱,并且很容易混淆,如图4所示。
图4.错误的中指。
但是,该技术相当可靠,通常至少可以识别拇指和食指,而与手的方向无关,这对于基本的手势处理来说应该足够了。 但是,该应用程序无法识别手势,希望它将成为下一章的主题。
Handy的类图如图5所示,仅列出了公共方法。
图5.方便的类图。
Handy的顶级与第5章中的BlobsDrumming应用程序的顶级并行(例如,参见第5章的图11),其中Handy类管理JFrame和HandPanel,显示带注释的网络摄像头图像。 图2和3总结的图像分析由HandDetector类执行,该类通过调用update()传递给当前的网络摄像头快照。 当HandPanel调用HandDetector.draw()时,它将绘制当前标记的指尖,COG和连接线。
1.分析网络摄像头图像
update()方法实质上是实现图2和图3的一系列调用。
// globals
private static final int IMG_SCALE = 2;
// scaling applied to webcam image
// HSV ranges defining the glove color
private int hueLower, hueUpper, satLower, satUpper,
briLower, briUpper;
// OpenCV elements
private IplImage hsvImg; // HSV version of webcam image
private IplImage imgThreshed; // threshold for HSV settings
// hand details
private Point cogPt; // center of gravity (COG) of contour
private int contourAxisAngle;
// contour's main axis angle relative to the horiz (in degrees)
private ArrayList fingerTips;
public void update(BufferedImage im)
{
BufferedImage scaleIm = scaleImage(im, IMG_SCALE);
// reduce the size of the image to make processing faster
// convert image format to HSV
cvCvtColor(IplImage.createFrom(scaleIm), hsvImg, CV_BGR2HSV);
// threshold image using loaded HSV settings for user's glove
cvInRangeS(hsvImg, cvScalar(hueLower, satLower, briLower, 0),
cvScalar(hueUpper, satUpper, briUpper, 0),
imgThreshed);
cvMorphologyEx(imgThreshed, imgThreshed, null, null,
CV_MOP_OPEN, 1);
// erosion followed by dilation on the image to remove
// specks of white while retaining the image size
CvSeq bigContour = findBiggestContour(imgThreshed);
if (bigContour == null)
return;
extractContourInfo(bigContour, IMG_SCALE);
// find the COG and angle to horizontal of the contour
findFingerTips(bigContour, IMG_SCALE);
// detect the fingertips positions in the contour
nameFingers(cogPt, contourAxisAngle, fingerTips);
} // end of upda