日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

javacv 人脸检测_使用JavaCV进行手和手指检测

發(fā)布時(shí)間:2023/12/3 java 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 javacv 人脸检测_使用JavaCV进行手和手指检测 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

javacv 人臉檢測(cè)

這篇文章是Andrew Davison博士發(fā)布的有關(guān)自然用戶界面(NUI)系列的一部分,內(nèi)容涉及使用JavaCV從網(wǎng)絡(luò)攝像頭視頻提要中檢測(cè)手。

注意:本章的所有源代碼都可以從http://fivedots.coe.psu.ac.th/~ad/jg/nui055/下載。

第5章的彩色斑點(diǎn)檢測(cè)代碼(可從http://fivedots.coe.psu.ac.th/~ad/jg/nui05/獲得 )可以用作其他形狀分析器的基礎(chǔ),我將在此處進(jìn)行說(shuō)明。通過(guò)擴(kuò)展它來(lái)檢測(cè)手和手指。 在圖1中,我的左手戴著黑手套。 我的Handy應(yīng)用程序嘗試查找并標(biāo)記拇指,食指,中指,無(wú)名指和小指。 在指尖和手的重心(COG)之間繪制黃線。

圖1.檢測(cè)左手和手指。

我使用了第5章的HSVSelector應(yīng)用程序來(lái)確定黑手套的合適HSV范圍。 在執(zhí)行圖2所示的步驟之前,Handy會(huì)加載這些范圍,以獲取手部的輪廓,其COG和相對(duì)于水平面的方向。

圖2。找到手輪廓。

圖2中的各個(gè)階段幾乎與第5章第4.1節(jié)中的ColorRectDetector.findRect()方法執(zhí)行的階段相同。但是,Handy繼續(xù)進(jìn)行處理,使用凸包和凸缺陷來(lái)定位并標(biāo)記手中的指尖輪廓。 這些附加步驟如圖3所示。

圖3.查找和標(biāo)記指尖。

外殼和缺陷是使用標(biāo)準(zhǔn)OpenCV操作從輪廓獲得的,我將在下面進(jìn)行解釋。 但是,命名手指的最后一步采用了一種頗為怪異的策略,該策略假定輪廓的缺陷是針對(duì)伸出的左手。 拇指和食指基于它們相對(duì)于COG的角度位置來(lái)定位,而其他手指則根據(jù)它們相對(duì)于那些手指的位置來(lái)標(biāo)識(shí)。 這個(gè)過(guò)程非常脆弱,并且很容易混淆,如圖4所示。

圖4.一個(gè)錯(cuò)誤的中指。

盡管如此,該技術(shù)還是相當(dāng)可靠的,通常至少可以識(shí)別拇指和食指,而與手的方向無(wú)關(guān),這對(duì)于基本的手勢(shì)處理來(lái)說(shuō)應(yīng)該足夠了。 但是,該應(yīng)用程序無(wú)法識(shí)別手勢(shì),希望它將成為下一章的主題。

Handy的類圖如圖5所示,其中僅列出了公共方法。

圖5.方便的類圖。

Handy的頂級(jí)與第5章中的BlobsDrumming應(yīng)用程序的頂級(jí)并行(例如,參見(jiàn)第5章的圖11??),其中Handy類管理JFrame和HandPanel,顯示帶注釋的攝像頭圖像。 圖2和3總結(jié)的圖像分析由HandDetector類執(zhí)行,該類通過(guò)調(diào)用update()傳遞給當(dāng)前的網(wǎng)絡(luò)攝像頭快照。 當(dāng)HandPanel調(diào)用HandDetector.draw()時(shí),它將繪制當(dāng)前標(biāo)記的指尖,COG和連接線。

1.分析網(wǎng)絡(luò)攝像頭圖像

update()方法實(shí)際上是實(shí)現(xiàn)圖2和圖3的一系列調(diào)用。

// 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 HSVcvCvtColor(IplImage.createFrom(scaleIm), hsvImg, CV_BGR2HSV);// threshold image using loaded HSV settings for user's glovecvInRangeS(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 sizeCvSeq bigContour = findBiggestContour(imgThreshed);if (bigContour == null)return;extractContourInfo(bigContour, IMG_SCALE);// find the COG and angle to horizontal of the contourfindFingerTips(bigContour, IMG_SCALE);// detect the fingertips positions in the contournameFingers(cogPt, contourAxisAngle, fingerTips); } // end of update()

update()首先縮放提供的網(wǎng)絡(luò)攝像頭圖像以提高處理速度。 然后,它將圖片轉(zhuǎn)換為HSV格式,以便可以使用黑手套的HSV范圍生成閾值圖像。 這對(duì)應(yīng)于圖2的第一行,盡管實(shí)際上將閾值渲染為黑色背景上的白色像素。

減去小斑點(diǎn)的閾值傳遞給findBiggestContour(); 在隨后的處理階段中,假定生成的輪廓是用戶的手。 extractContourInfo()分析輪廓以找到手的重心(COG)及其相對(duì)于水平面的方向,并將其存儲(chǔ)在cogPt和ContourAxisAngle全局變量中。 extractContourInfo()的完成對(duì)應(yīng)于圖2的末尾。

findFingerTips()方法將凸包包裹在輪廓周圍,以識(shí)別形狀的缺陷(圖3的頂行),我們假設(shè)這是手的手指。 經(jīng)過(guò)少量過(guò)濾以減少缺陷數(shù)量之后,其余缺陷將被視為指尖坐標(biāo),并存儲(chǔ)在全局fingerTips列表中。

nameFingers()標(biāo)記手指(假設(shè)拇指和食指在手的左側(cè)),完成圖3的階段。

1.1找到最大的輪廓

findBiggestContour()使用OpenCV函數(shù)cvFindContours()創(chuàng)建輪廓列表。 對(duì)于我的二進(jìn)制閾值圖像,輪廓是白色像素的區(qū)域(或斑點(diǎn))。 每個(gè)斑點(diǎn)由一個(gè)邊界框近似,并且選擇并返回與最大框相對(duì)應(yīng)的輪廓。

// globals private static final float SMALLEST_AREA = 600.0f;// ignore smaller contour areasprivate CvMemStorage contourStorage;private CvSeq findBiggestContour(IplImage imgThreshed) {CvSeq bigContour = null;// generate all the contours in the threshold image as a listCvSeq contours = new CvSeq(null);cvFindContours(imgThreshed, contourStorage, contours,Loader.sizeof(CvContour.class),CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);// find the largest contour in the list based on bounded box sizefloat maxArea = SMALLEST_AREA;CvBox2D maxBox = null;while (contours != null && !contours.isNull()) {if (contours.elem_size() > 0) {CvBox2D box = cvMinAreaRect2(contours, contourStorage);if (box != null) {CvSize2D32f size = box.size();float area = size.width() * size.height();if (area > maxArea) {maxArea = area;bigContour = contours;}}}contours = contours.h_next();}return bigContour; } // end of findBiggestContour()

cvFindContours()可以返回不同類型的輪廓,這些輪廓收集在不同類型的數(shù)據(jù)結(jié)構(gòu)中。 我生成最簡(jiǎn)單的輪廓,將它們存儲(chǔ)在線性列表中,可以使用while循環(huán)進(jìn)行搜索。

經(jīng)過(guò)一些實(shí)驗(yàn)后,我在600平方像素的有邊界框上放置了一個(gè)下限,以濾除圍繞圖像噪點(diǎn)的小框。 這意味著,如果findBiggestContour()找不到足夠大的框,則可能返回null。

1.2計(jì)算COG和水平角

圖2所示的下一步是通過(guò)調(diào)用extractContourInfo()查找COG和與手部輪廓水平線的夾角。 在此,HandDetector中的代碼從ColorRectDetector.findRect()在第5章中進(jìn)行的分析開(kāi)始成為公司的一部分。在該章的4.2節(jié)中,輪廓周圍的封閉框用于獲得中心和方向。 這是足夠的,因?yàn)榛A(chǔ)形狀是矩形卡片,因此輪廓和框幾乎相同。 但是,手周圍的邊界框可能很容易與手本身具有不同的COG或角度。 在這種情況下,有必要利用力矩直接分析手部輪廓而不是邊界框。

我在第3章中使用了空間矩來(lái)查找二進(jìn)制圖像的COG。 可以將相同的技術(shù)應(yīng)用于輪廓以找到其中心(或質(zhì)心)。 我還可以計(jì)算二階混合矩,它提供了有關(guān)質(zhì)心周圍像素散布的信息。 可以組合二階矩以返回輪廓的主軸相對(duì)于x軸的方向(或角度)。

回顧第三章的OpenCV矩符號(hào),m()矩函數(shù)定義為:

該函數(shù)帶有兩個(gè)參數(shù)p和q,它們用作x和y的冪。 I()函數(shù)是由像素的(x,y)坐標(biāo)定義的像素的強(qiáng)度。 n是組成形狀的像素?cái)?shù)。

如果考慮圖6中的輪廓,則θ是其主軸線與水平面的角度,+ y軸指向下方。

圖6.輪廓線及其主軸線。

就m()函數(shù)而言,可以證明:

如下所示的extractContourInfo()方法使用空間矩獲取輪廓的質(zhì)心,并使用cvGetCentralMoment()根據(jù)上述公式計(jì)算主軸角度; 這些結(jié)果存儲(chǔ)在全局變量cogPt和ContourAxisAngle中,以備后用。

// globals private Point cogPt; // center of gravity (COG) of contour private int contourAxisAngle; // contour's main axis angle relative to horizontal (in degrees)private ArrayList fingerTips;private void extractContourInfo(CvSeq bigContour, int scale) {CvMoments moments = new CvMoments();cvMoments(bigContour, moments, 1);// center of gravitydouble m00 = cvGetSpatialMoment(moments, 0, 0) ;double m10 = cvGetSpatialMoment(moments, 1, 0) ;double m01 = cvGetSpatialMoment(moments, 0, 1);if (m00 != 0) { // calculate centerint xCenter = (int) Math.round(m10/m00)*scale;int yCenter = (int) Math.round(m01/m00)*scale;cogPt.setLocation(xCenter, yCenter);}double m11 = cvGetCentralMoment(moments, 1, 1);double m20 = cvGetCentralMoment(moments, 2, 0);double m02 = cvGetCentralMoment(moments, 0, 2);contourAxisAngle = calculateTilt(m11, m20, m02);// deal with hand contour pointing downwards/* uses fingertips information generated on the last update ofthe hand, so will be out-of-date */if (fingerTips.size() > 0) {int yTotal = 0;for(Point pt : fingerTips)yTotal += pt.y;int avgYFinger = yTotal/fingerTips.size();if (avgYFinger > cogPt.y) // fingers below COGcontourAxisAngle += 180;}contourAxisAngle = 180 - contourAxisAngle; /* this makes the angle relative to a positive y-axis thatruns up the screen */ } // end of extractContourInfo()private int calculateTilt(double m11, double m20, double m02) {double diff = m20 - m02;if (diff == 0) {if (m11 == 0)return 0;else if (m11 > 0)return 45;else // m11 < 0return -45;}double theta = 0.5 * Math.atan2(2*m11, diff);int tilt = (int) Math.round( Math.toDegrees(theta));if ((diff > 0) && (m11 == 0))return 0;else if ((diff < 0) && (m11 == 0))return -90;else if ((diff > 0) && (m11 > 0)) // 0 to 45 degreesreturn tilt;else if ((diff > 0) && (m11 < 0)) // -45 to 0return (180 + tilt); // change to counter-clockwise angleelse if ((diff < 0) && (m11 > 0)) // 45 to 90return tilt;else if ((diff < 0) && (m11 < 0)) // -90 to -45return (180 + tilt); // change to counter-clockwise angleSystem.out.println("Error in moments for tilt angle");return 0; } // end of calculateTilt()

Johannes Kilian在http://public.cranfield.ac.uk/c5354/teaching/dip/opencv/SimpleImageAnalysisbyMoments.pdf上的 “通過(guò)矩的簡(jiǎn)單圖像分析”中對(duì)OpenCV的矩進(jìn)行了詳細(xì)說(shuō)明。 calculateTilt()內(nèi)的代碼基于Kilian論文表1中列出的θ特殊情況。

不幸的是,軸角無(wú)法區(qū)分手指指向上方的手和手指指向下方的手,因此有必要檢查指尖相對(duì)于COG的相對(duì)位置,以確定是否應(yīng)調(diào)整角度。 問(wèn)題在于,只有在檢查了手部輪廓的凸包是否存在缺陷之后(在extractContourInfo()完成之后才出現(xiàn)此缺陷),該信息才可用。

我的解決方案是使用在上次調(diào)用update()時(shí)計(jì)算出的指尖坐標(biāo),該指針?lè)治隽水?dāng)前幀之前的攝像頭幀。 數(shù)據(jù)將是過(guò)時(shí)的,但是在兩次捕捉之間的200 ms間隔內(nèi)指針不會(huì)移動(dòng)太多。

1.3找到指尖

指尖的識(shí)別在圖3的第一行中進(jìn)行; 在代碼中,通過(guò)OpenCV的cvConvexHull2()將凸包包裹在輪廓上,并通過(guò)cvConvexityDefects()將多邊形與輪廓進(jìn)行比較以查找其缺陷。

利用輪廓的低多邊形近似值而不是原始值,可以加快船體創(chuàng)建和缺陷分析的速度。

這些階段在findFingerTips()方法的前半部分執(zhí)行:

// globals private static final int MAX_POINTS = 20; // max number of points stored in an array// OpenCV elements private CvMemStorage contourStorage, approxStorage,hullStorage, defectsStorage;// defects data for the hand contour private Point[] tipPts, foldPts; private float[] depths;private void findFingerTips(CvSeq bigContour, int scale) {CvSeq approxContour = cvApproxPoly(bigContour,Loader.sizeof(CvContour.class),approxStorage, CV_POLY_APPROX_DP, 3, 1);// reduce number of points in the contourCvSeq hullSeq = cvConvexHull2(approxContour,hullStorage, CV_COUNTER_CLOCKWISE, 0);// find the convex hull around the contourCvSeq defects = cvConvexityDefects(approxContour,hullSeq, defectsStorage);// find the defect differences between the contour and hullint defectsTotal = defects.total();if (defectsTotal > MAX_POINTS) {System.out.println("Processing " + MAX_POINTS + " defect pts");defectsTotal = MAX_POINTS;}// copy defect information from defects sequence into arraysfor (int i = 0; i < defectsTotal; i++) {Pointer pntr = cvGetSeqElem(defects, i);CvConvexityDefect cdf = new CvConvexityDefect(pntr);CvPoint startPt = cdf.start();tipPts[i] = new Point( (int)Math.round(startPt.x()*scale),(int)Math.round(startPt.y()*scale));// array contains coords of the fingertipsCvPoint endPt = cdf.end();CvPoint depthPt = cdf.depth_point();foldPts[i] = new Point( (int)Math.round(depthPt.x()*scale),(int)Math.round(depthPt.y()*scale));//array contains coords of the skin fold between fingersdepths[i] = cdf.depth()*scale;// array contains distances from tips to folds}reduceTips(defectsTotal, tipPts, foldPts, depths); } // end of findFingerTips()

findFingerTips()的后半部分從缺陷序列中提取尖端和褶皺坐標(biāo)以及深度。 先前使用CV_COUNTER_CLOCKWISE參數(shù)調(diào)用凸包方法cvConvexHull2()意味著將以逆時(shí)針順序存儲(chǔ)坐標(biāo),如圖7所示。

圖7.指尖,褶皺和深度。

指尖存儲(chǔ)在tipPts []數(shù)組中,手指在foldPts []中折疊(手指之間的凹痕),深度在depths []中。

如圖7所示,分析通常會(huì)產(chǎn)生太多缺陷,因此在findFingerTips()的末尾會(huì)調(diào)用reduceTips()。 它應(yīng)用了兩個(gè)簡(jiǎn)單的測(cè)試來(lái)濾除不太可能是指尖的缺陷-丟棄缺陷深度較淺的點(diǎn),并在其相鄰折疊點(diǎn)之間以太大的角度進(jìn)行坐標(biāo)。 兩者的示例如圖8所示。

圖8.淺深度和廣角。

reduceTips()將其余的提示點(diǎn)存儲(chǔ)在全局fingerTips列表中:

// globals private static final int MIN_FINGER_DEPTH = 20; private static final int MAX_FINGER_ANGLE = 60; // degreesprivate ArrayList fingerTips;private void reduceTips(int numPoints, Point[] tipPts,Point[] foldPts, float[] depths) {fingerTips.clear();for (int i=0; i < numPoints; i++) {if (depths[i] < MIN_FINGER_DEPTH) // defect too shallowcontinue;// look at fold points on either side of a tipint pdx = (i == 0) ? (numPoints-1) : (i - 1); // predecessor of iint sdx = (i == numPoints-1) ? 0 : (i + 1); // successor of iint angle = angleBetween(tipPts[i], foldPts[pdx], foldPts[sdx]);if (angle >= MAX_FINGER_ANGLE) continue; // angle between finger and folds too wide// this point is probably a fingertip, so add to listfingerTips.add(tipPts[i]);} } // end of reduceTips()private int angleBetween(Point tip, Point next, Point prev) // calculate the angle between the tip and its neighboring folds // (in integer degrees) {return Math.abs( (int)Math.round(Math.toDegrees(Math.atan2(next.x - tip.x, next.y - tip.y) -Math.atan2(prev.x - tip.x, prev.y - tip.y)) )); }

1.4命名手指

nameFingers()使用指尖坐標(biāo)列表以及輪廓的COG和軸角來(lái)分兩步標(biāo)記手指。 首先,它會(huì)基于它們相對(duì)于COG的可能角度調(diào)用labelThumbIndex()來(lái)標(biāo)記拇指和食指,假設(shè)它們位于手的左側(cè)。 nameFingers()會(huì)根據(jù)相對(duì)于拇指和食指的已知順序,嘗試在labelUnknowns()中標(biāo)記其他手指。

// globals private ArrayList namedFingers;private void nameFingers(Point cogPt, int contourAxisAngle,ArrayList fingerTips) { // reset all named fingers to unknownnamedFingers.clear();for (int i=0; i < fingerTips.size(); i++)namedFingers.add(FingerName.UNKNOWN);labelThumbIndex(fingerTips, namedFingers);labelUnknowns(namedFingers); } // end of nameFingers()

Finger ID及其相對(duì)順序在FingerName枚舉中維護(hù):

public enum FingerName {LITTLE, RING, MIDDLE, INDEX, THUMB, UNKNOWN;public FingerName getNext(){ int nextIdx = ordinal()+1;if (nextIdx == (values().length))nextIdx = 0;return values()[nextIdx]; } // end of getNext()public FingerName getPrev(){ int prevIdx = ordinal()-1;if (prevIdx < 0)prevIdx = values().length-1;return values()[prevIdx]; } // end of getPrev()} // end of FingerName enum

可能的手指名稱之一是UNKNOWN,該名稱用于在調(diào)用命名方法之前標(biāo)記所有指尖。

labelThumbIndex()嘗試根據(jù)圖9中所示的角度范圍來(lái)標(biāo)記拇指和食指。

圖9.拇指和食指的角度范圍。

食指可以圍繞COG旋轉(zhuǎn)60至120度,而拇指可以在120至200度之間移動(dòng)。 我通過(guò)反復(fù)試驗(yàn)得出了這些角度,他們認(rèn)為手是筆直向上的。

labelThumbIndex()還假定拇指和食指最有可能存儲(chǔ)在fingerTips列表的末尾,因?yàn)檩喞w是按逆時(shí)針順序構(gòu)建的。 因此,通過(guò)向后遍歷列表,可以增加與正確缺陷匹配的機(jī)會(huì)。

// globals private static final int MIN_THUMB = 120; // angle ranges private static final int MAX_THUMB = 200;private static final int MIN_INDEX = 60; private static final int MAX_INDEX = 120;// hand details private Point cogPt private int contourAxisAngle; private void labelThumbIndex(ArrayList fingerTips,ArrayList nms) {boolean foundThumb = false;boolean foundIndex = false;int i = fingerTips.size()-1;while ((i >= 0)) {int angle = angleToCOG(fingerTips.get(i),cogPt, contourAxisAngle);// check for thumbif ((angle <= MAX_THUMB) && (angle>MIN_THUMB) && !foundThumb) {nms.set(i, FingerName.THUMB);foundThumb = true;}// check for indexif ((angle <= MAX_INDEX) && (angle > MIN_INDEX) && !foundIndex) {nms.set(i, FingerName.INDEX);foundIndex = true;}i--;} } // end of labelThumbIndex()

angleToCOG()計(jì)算指尖相對(duì)于COG的角度,記住要記住輪廓軸角度,以便使手筆直向上。

private int angleToCOG(Point tipPt, Point cogPt,int contourAxisAngle) {int yOffset = cogPt.y - tipPt.y; // make y positive up screenint xOffset = tipPt.x - cogPt.x;double theta = Math.atan2(yOffset, xOffset);int angleTip = (int) Math.round( Math.toDegrees(theta));return angleTip + (90 - contourAxisAngle);// this ensures that the hand is orientated straight up } // end of angleToCOG()

labelUnknowns()傳遞了一個(gè)手指名稱列表,該列表希望在某些位置包含THUMB和INDEX,而在其他位置包含UNKNOWN。 使用命名的手指作為起點(diǎn),根據(jù)手指在FingerName枚舉中的順序,將UNKNOWN更改為手指名稱。

private void labelUnknowns(ArrayList nms) {// find first named fingerint i = 0;while ((i < nms.size()) && (nms.get(i) == FingerName.UNKNOWN))i++;if (i == nms.size()) // no named fingers found, so give upreturn;FingerName name = nms.get(i);labelPrev(nms, i, name); // fill-in backwardslabelFwd(nms, i, name); // fill-in forwards } // end of labelUnknowns()

labelPrev()和labelFwd()的區(qū)別僅在于它們?cè)诿Q列表中移動(dòng)的方向。 labelPrev()向后移動(dòng)以嘗試將UNKNOWNS更改為已命名的手指,但前提是尚未將名稱分配給列表。

2.畫(huà)出手指

由update()執(zhí)行的分析將得到指尖點(diǎn)列表(在fingertips全局中),關(guān)聯(lián)的已命名手指列表(在namedFingers中)以及輪廓COG和軸角度。 除角度外,所有這些都由draw()用來(lái)將命名的手指標(biāo)簽添加到網(wǎng)絡(luò)攝像頭圖像中,如下圖1和圖10所示。

圖10.命名的手指和未知的手指。

未知的手指“尖端”(在namedFingers中標(biāo)記為UNKNOWN)被繪制為紅色圓圈。

// globals private Point cogPt; private ArrayList fingerTips; private ArrayList namedFingers;public void draw(Graphics2D g2d) {if (fingerTips.size() == 0)return;g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); // line smoothingg2d.setPaint(Color.YELLOW);g2d.setStroke(new BasicStroke(4)); // thick yellow pen// label tips in red or green, and draw lines to named tipsg2d.setFont(msgFont);for (int i=0; i < fingerTips.size(); i++) {Point pt = fingerTips.get(i);if (namedFingers.get(i) == FingerName.UNKNOWN) {g2d.setPaint(Color.RED); // unnamed fingertip is redg2d.drawOval(pt.x-8, pt.y-8, 16, 16);g2d.drawString("" + i, pt.x, pt.y-10); // label with a digit}else { // draw yellow line to the named fingertip from COGg2d.setPaint(Color.YELLOW);g2d.drawLine(cogPt.x, cogPt.y, pt.x, pt.y);g2d.setPaint(Color.GREEN); // named fingertip is greeng2d.drawOval(pt.x-8, pt.y-8, 16, 16);g2d.drawString(namedFingers.get(i).toString().toLowerCase(),pt.x, pt.y-10);}}// draw COGg2d.setPaint(Color.GREEN);g2d.fillOval(cogPt.x-8, cogPt.y-8, 16, 16); } // end of draw()

3.手勢(shì)檢測(cè)

Handy應(yīng)用程序會(huì)盡力將已命名的指尖轉(zhuǎn)換為手勢(shì),這需要分析手指隨著時(shí)間在空間中的移動(dòng)方式。

初步測(cè)試表明,Handy僅在涉及伸出的拇指和/或食指(也許與其他手指結(jié)合在一起)時(shí),才能可靠地識(shí)別手勢(shì)。 這種手勢(shì)包括圖11中所示的“勝利”,“波浪”,“良好”,“指向”和“槍支”。

圖11.適用于便捷式檢測(cè)的手勢(shì)。

Handy無(wú)法檢測(cè)到的常見(jiàn)手勢(shì)是“ ok”(參見(jiàn)圖12),因?yàn)樗枰獙⑹种阜旁谝黄?#xff0c;而這不能僅根據(jù)輪廓缺陷來(lái)檢測(cè)到。

圖12.不合適的“確定”手勢(shì)。

參考: Java Advent Calendar博客上的JCG合作伙伴 Attila-Mihaly Balazs 使用JavaCV進(jìn)行的手和手指檢測(cè) 。

翻譯自: https://www.javacodegeeks.com/2012/12/hand-and-finger-detection-using-javacv.html

javacv 人臉檢測(cè)

總結(jié)

以上是生活随笔為你收集整理的javacv 人脸检测_使用JavaCV进行手和手指检测的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。