基于“手势识别”的人机交互系统设计.docx
第十二届智能控制设计大赛 华南理工大学第十二届智能控制设计大赛 初级组 基于“手势识别”的人机交互系统 自动化科学与工程学院 队名 队长 时间 目录 初级组基于”手势识别”的人机交互系统1 摘要3 一、题目分析4 A.设计要求4 B.题目分析4 二、已实现功能5 A.基本要求5 B. 拓展要求5 三、方案设计与选择5 A. 手势分割方案5 (1)方案1基于HSV色彩空间的肤色识别5 (2)方案2基于YCrCb色彩空间的肤色检测6 (3)方案分析比较与选择7 B. 手势识别方案8 (1)方案1重心距离法8 (2)方案2凸包检测法8 (3)方案分析比较与选择9 四、方案实施9 A.手势分割9 B.重心距离法实现锤子剪刀布的游戏10 C.凸包检测法15 D.鼠标控制18 E.人脸识别20 五、调试及成品展示22 A.MFC人机交互界面22 B.锤子剪刀布识别展示22 C. 数字识别展示23 D. 人脸检测展示24 F.空间中写字展示25 六、总结25 七、参考文献26 摘要 手势也是表达信息的一种重要方式,对于手势进行识别具有很广泛的应用。该作品以计算机自带的摄像头为硬件设备,以opencv库为算法基础,进行原始图像的采集和处理。结合MFC建立良好的人机交互系统,将采集的原始图像、处理得到的图像展示出来,具有一定的美感。该作品实现了设计要求的全部基本功能和拓展功能,同时附加数字识别和人脸检测拓展功能,能够进行多功能展示。 一、 题目分析 A. 设计要求 1、 基本要求 (1)基于摄像头获取的图像数据,实现手部检测; (2)实现拳头、剪刀、布三个手势的识别; (3)设计良好的人机界面,用于展示原始图像以及处理结果等信息; 2、 拓展要求 (1)控制鼠标完成移动、单击、双击、拖动等操作; (2)控制电脑关屏、开屏; (3)设计锥子剪刀布游戏播放提示语、判断胜负等; (4)实现空中手写轨迹识别; (5) 其他拓展功能。 B. 题目分析 肤色区域 打开摄像头并获取图像 图像预处理得到二值化图像 YCrCb 输出识别结果 辨别手指根数 定位手指 确认无误 二、 已实现功能 A. 基本要求 1、 打开摄像头并获取图像数据,实现手部检测 2、 实现拳头、剪刀、布三个手势的识别 3、 MFC良好的人机界面,用于展示原始图像以及处理结果等信息 B. 拓展要求 1、 控制鼠标完成移动、单击、双击、拖动等操作; 2、 控制电脑关屏、开屏 3、 锥子剪刀布游戏判断胜负、播放音乐、记录分数等 4、 实现空中手写轨迹识别 5、 实现05的数字识别 6、 实现人脸检测 三、 方案设计与选择 A. 手势分割方案 (1)方案1基于HSV色彩空间的肤色识别 HSVHue, Saturation, Value是根据颜色的直观特性由A. R. Smith在1978年创建的一种颜色空间, 也称六角锥体模型Hexcone Model。、这个模型中颜色的参数分别是色调(H),饱和度(S),亮度(V)。从RGB到或HSV的转换关系如下 【1】 (2)方案2基于YCrCb色彩空间的肤色检测 1)、YCbCr分为Y,Cb,Cr三个分量,其中Y是指亮度分量,Cb指蓝色色度分量,而Cr指红色色度分量。 2)、阈值分割根据多次实验确定,正常黄种人的Cr分量大约在140·175之间,Cb分量大约在100120之间。 3)、Opencv从RGB到YCrCb色彩空间的转换关系如下 【2】 (3)方案分析比较与选择 【3】 可以看出HSV颜色空间中H、S、V的标准差相对于YCrCb空间的标准差要大。而在YCrCb空间中,Y的标准差要明显大于其他两者。标准差越大则表明其值在亮度变化时波动大,因此选择标准差小的颜色通道比较好。 因此,选用方案2. B. 手势识别方案 (1)方案1重心距离法 见下图,红色点是手的重心,那么手的边缘的所有点与重心点的距离按顺时针方向或者逆时针方向遍历,就会出现五个峰值,分别是五个手指,这样我们就可以简单找到了。如果你是只伸出一两个手指,那么就只有一两个峰值了。 【4】 (2)方案2凸包检测法 【5】 如上图所示,黑色的轮廓线为convexityhull,而convexityhull与手掌之间的部分为convexitydefects.每个convexitydefect区域有四个特征量起始点(startPoint),结束点endPoint,距离convexityhull最远点farPoint,最远点到convexityhull的距离depth。 (3)方案分析比较与选择 在方案1中,按顺时针方向或者逆时针方向遍历出现峰值保持的点数超过80个即认为这个点是指尖,辨别存在偏差。在方案2中,通过凸包检测到的点集识别,只要有指尖凸包检测法就会检测到相关的点,这个点就是指尖。此法辨别率高。 因此,锤子剪刀布游戏对于指尖检测辨别率不高的识别方法选用方案1,数字识别对于指尖检测辨别率高的识别方法选用方案2. 四、 方案实施 得到二值化图像并以此作为掩码 迭代器访问CrCb元素 转换为YCrCb颜色空间 打开摄像头获取图像 A. 手势分割 与摄像头获得的原图像进行融合得到手的部分 形态学操作过滤噪点 代码实现 //肤色提取,skinArea为二值化肤色图像 void skinExtractconst Mat vector planes; //转换为YCrCb颜色空间 cvtColorframe, YCbCr, CV_RGB2YCrCb; //将多通道图像分离为多个单通道图像 splitYCbCr, planes; //运用迭代器访问矩阵元素 MatIterator_ it_Cb planes[1].begin, it_Cb_end planes[1].end; MatIterator_ it_Cr planes[2].begin; MatIterator_ it_skin skinArea.begin; //人的皮肤颜色在YCbCr色度空间的分布范围100 contours; // 轮廓的结构信息 vector hierarchy;// 凸包络的点集 //寻找轮廓 findContoursskinArea, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE; // 找到最大的轮廓 int index;//最大轮廓索引 double area, maxArea0; for size_t i 0; i maxArea { maxArea area; index i; } } Moments moment momentsskinArea, true;//中心矩,用于计算重心 Point centerintmoment.m10 / moment.m00, intmoment.m01 / moment.m00; circleshow_img, center, 8, Scalar0, 0, 255, CV_FILLED;//画重心 // 寻找指尖 vector couPoint contours[index]; vector fingerTips;//指尖点 Point tmp; int max0, count0, notice0; int gesture 0,flag 0;//用于记录手指根数 for size_t i 0; i max { max dist; notice i; count 0; } if dist max { count; if count 80// 计算最大值保持的点数,如果大于80,那么就认为这个是指尖 { count 0; max 0; bool flag false; // 低于手心的点不算 if center.y CreateIDD_GAMEDIALOG, this; } mTipDlg-ShowWindowSW_SHOW; mTipDlg-SetDlgItemTextWIDC_EDITYOURS, yourGes; mTipDlg-SetDlgItemTextWIDC_EDITCOMPS, compGes; mTipDlg-SetDlgItemTextWIDC_EDITRESULT, res; UpdateDataFALSE; hMCI MCIWndCreateNULL, NULL, WS_POPUP | MCIWNDF_NOPLAYBAR | MCIWNDF_NOMENU, choose;//音频部分 MCIWndPlayhMCI; } C. 凸包检测法 点集筛选,每个指尖只画一个点 凸包检测得到凸包点集 轮廓检测得到最大轮廓 手的部分 输出结果 顺时针遍历确定每个指尖只画一个点 代码实现 Point gravity; int CMF_ImageProcessingDlgnumRecog { numResult NULL; skinArea.createframe.rows, frame.cols, CV_8UC1;//创建同frame大小的单通道8位矩阵 skinExtractframe, skinArea; frame.copyTonumResult, skinArea;//skinArea作为掩码将frame拷贝至show_img vector contours; // 轮廓 vector hierarchy; // 轮廓的结构信息 vector hull; // 凸包络的点集 vector fingerTips;//指尖点 contours.clear; hierarchy.clear; fingerTips.clear; // 得到手的轮廓 findContoursskinArea, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE; Moments moment momentsskinArea, true;//中心矩,用于计算重心 Point centerintmoment.m10 / moment.m00, intmoment.m01 / moment.m00; circlenumResult, center, 8, Scalar0, 0, 255, CV_FILLED;//画重心 gravity center; // 找到最大的轮廓 int index;//最大轮廓索引 double area, maxArea0; for size_t i 0; i maxArea { maxArea area; index i; } } vector filterContours contours[index]; // 筛选后的轮廓 convexHullMatfilterContours, hull, true;// 得到轮廓的凸包络 int hullcount inthull.size; for int i 0; i 0; j--//顺时针遍历找最后一个指尖点 { if sqrtpowhull[j].x - hull[j - 1].x, 2 powhull[j].y - hull[j - 1].y, 2 40//两点之间的距离不小于40 { fingerTips.push_backhull[j]; break; } } int fingerNum 0; for size_t j fingerTips.size - 1; j 0; j--//画指尖点 { //两点之间的距离不小于40,点到重心的距离不小于112 if sqrtpowfingerTips[j].x - fingerTips[j - 1].x, 2 powfingerTips[j].y - fingerTips[j - 1].y, 2 40 fingerNum; } } if intsqrtpowfingerTips[0].x - center.x, 2 powfingerTips[0].y - center.y, 2 112 fingerNum; } return fingerNum; } 手的部分 D. 鼠标控制 鼠标响应 辨别手指根数,控制鼠标 手指根数 鼠标响应 0 移动 1 单击左键 2 单击右键 3 双击左键 4 拖动 5 松开左键 5 结束线程,退出鼠标控制 代码实现 void CMF_ImageProcessingDlgBnClickedBtmouse// 主要处理函数在这里写 { this-SetCapture;//捕获鼠标消息 threadFlag true;//线程启动标志MF_ImageProcessing cap.open0; cap frame; delayms1000; int l 1, r 1;//单击标志 int x, y;//鼠标坐标 int fingerNum; IplImage *showImage; Point point; while true { delayms5; fingerNum numRecog; showImage DrawToMFCIDC_ShowImage, showImage; point gravity; if fingerNum 0//移动 { x intpoint.x * 1550 / showImage-width;//屏幕宽1366,高768 y intpoint.y * 850 / showImage-height; mouse_eventMOUSEEVENTF_MOVE, 0, 0, 0, 0; SetCursorPosx, y; l 1; r 1; } else if fingerNum 1//单击左键 { if l 1 { mouse_eventMOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0; l 0; } r 1; } else if fingerNum 2//单击右键 { if r 1 { mouse_eventMOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0; r 0; } l 1; } else if fingerNum 3//双击 { if l 1 { mouse_eventMOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0; mouse_eventMOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0; l 0; } r 1; } else if fingerNum 4//单击拖动 { if l 1 { mouse_eventMOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0; l 0; } x intpoint.x * 1366 / showImage-width; y intpoint.y * 768 / showImage-height; SetCursorPosx, y; r 1; } else if fingerNum 5//松开左键 { if l 0 mouse_eventMOUSEEVENTF_LEFTUP, 0, 0, 0, 0; l 1; r 1; } if fingerNum 3 return; if threadFlag//关闭摄像头按钮被按下,结束线程 return; } } 获取摄像头图像 E. 人脸识别 识别并标记输出 加载Haar特征分类器 代码实现 void CMF_ImageProcessingDlgOnBnClickedBtfacedetect { // TODO 在此添加控件通知处理程序代码 img Mat faceDetImgimg; const char *pStrCascadeFileName “haarcascade_frontalface_alt.xml“; CvHaarClassifierCascade *pHaarCascade NULL; pHaarCascade CvHaarClassifierCascade*cvLoadpStrCascadeFileName; IplImage *pGrayImage cvCreateImagecvGetSizeimg, IPL_DEPTH_8U, 1; cvCvtColorimg, pGrayImage, CV_BGR2GRAY; // 人脸识别与标记 if pHaarCascade NULL { CvScalar FaceCirclecolors[] { { { 0, 0, 255 } }, { { 0, 128, 255 } }, { { 0, 255, 255 } }, { { 0, 255, 0 } }, { { 255, 128, 0 } }, { { 255, 255, 0 } }, { { 255, 0, 0 } }, { { 255, 0, 255 } } }; CvMemStorage *pcvMStorage cvCreateMemStorage0; cvClearMemStoragepcvMStorage; // 识别 DWORD dwTimeBegin, dwTimeEnd; dwTimeBegin GetTickCount; CvSeq *pcvSeqFaces cvHaarDetectObjectspGrayImage, pHaarCascade, pcvMStorage; dwTimeEnd GetTickCount; //AfxMessageBoxTEXT“共检测出人数d,使用时间f“, pcvSeqFaces-total, dwTimeEnd; // 标记 for int i 0; i total; i { CvRect* r CvRect*cvGetSeqElempcvSeqFaces, i; Rect face_region; face_region.x r-x; face_region.y r-y; face_region.width r-width; face_region.height r-height; rectanglefaceDetImg, face_region, FaceCirclecolors[i 8], 1, 8, 0;//矩形标记 } cvReleaseMemStorage } IplImage *face_detect; face_detect DrawToMFCIDC_OriginalImage, face_detect; } 五、 调试及成品展示 A. MFC人机交互界面 B. 锤