《基于“手势识别”的人机交互系统设计.docx》由会员分享,可在线阅读,更多相关《基于“手势识别”的人机交互系统设计.docx(26页珍藏版)》请在工友文库上搜索。
1、第十二届智能控制设计大赛华南理工大学第十二届智能控制设计大赛初级组基于“手势识别”的人机交互系统自动化科学与工程学院队名: 队长: 时间: 目录初级组基于”手势识别”的人机交互系统1摘要3一、题目分析4A.设计要求4B.题目分析4二、已实现功能5A.基本要求5B. 拓展要求5三、方案设计与选择5A. 手势分割方案5(1)方案1:基于HSV色彩空间的肤色识别5(2)方案2:基于YCrCb色彩空间的肤色检测6(3)方案分析比较与选择7B. 手势识别方案8(1)方案1:重心距离法8(2)方案2:凸包检测法8(3)方案分析比较与选择9四、方案实施9A.手势分割9B.重心距离法实现锤子剪刀布的游戏10C
2、.凸包检测法15D.鼠标控制18E.人脸识别20五、调试及成品展示22A.MFC人机交互界面22B.锤子剪刀布识别展示22C. 数字识别展示23D. 人脸检测展示24F.空间中写字展示25六、总结25七、参考文献26摘要 手势也是表达信息的一种重要方式,对于手势进行识别具有很广泛的应用。该作品以计算机自带的摄像头为硬件设备,以opencv库为算法基础,进行原始图像的采集和处理。结合MFC建立良好的人机交互系统,将采集的原始图像、处理得到的图像展示出来,具有一定的美感。该作品实现了设计要求的全部基本功能和拓展功能,同时附加数字识别和人脸检测拓展功能,能够进行多功能展示。一、 题目分析A. 设计要
3、求1、 基本要求(1)基于摄像头获取的图像数据,实现手部检测;(2)实现拳头、剪刀、布三个手势的识别;(3)设计良好的人机界面,用于展示原始图像以及处理结果等信息;2、 拓展要求(1)控制鼠标完成移动、单击、双击、拖动等操作;(2)控制电脑关屏、开屏; (3)设计锥子剪刀布游戏:播放提示语、判断胜负等;(4)实现空中手写轨迹识别; (5) 其他拓展功能。B. 题目分析肤色区域打开摄像头并获取图像图像预处理得到二值化图像YCrCb输出识别结果辨别手指根数定位手指确认无误二、 已实现功能A. 基本要求1、 打开摄像头并获取图像数据,实现手部检测2、 实现拳头、剪刀、布三个手势的识别3、 MFC良好
4、的人机界面,用于展示原始图像以及处理结果等信息B. 拓展要求1、 控制鼠标完成移动、单击、双击、拖动等操作;2、 控制电脑关屏、开屏3、 锥子剪刀布游戏:判断胜负、播放音乐、记录分数等4、 实现空中手写轨迹识别5、 实现05的数字识别6、 实现人脸检测三、 方案设计与选择A. 手势分割方案(1)方案1:基于HSV色彩空间的肤色识别HSV(Hue, Saturation, Value)是根据颜色的直观特性由A. R. Smith在1978年创建的一种颜色空间, 也称六角锥体模型(Hexcone Model)。、这个模型中颜色的参数分别是:色调(H),饱和度(S),亮度(V)。从RGB到或HSV的
5、转换关系如下:【1】(2)方案2:基于YCrCb色彩空间的肤色检测1)、YCbCr分为Y,Cb,Cr三个分量,其中Y是指亮度分量,Cb指蓝色色度分量,而Cr指红色色度分量。2)、阈值分割:根据多次实验确定,正常黄种人的Cr分量大约在140175之间,Cb分量大约在100120之间。3)、Opencv从RGB到YCrCb色彩空间的转换关系如下:【2】(3)方案分析比较与选择【3】可以看出HSV颜色空间中H、S、V的标准差相对于YCrCb空间的标准差要大。而在YCrCb空间中,Y的标准差要明显大于其他两者。标准差越大则表明其值在亮度变化时波动大,因此选择标准差小的颜色通道比较好。因此,选用方案2.
6、B. 手势识别方案(1)方案1:重心距离法见下图,红色点是手的重心,那么手的边缘的所有点与重心点的距离按顺时针方向或者逆时针方向遍历,就会出现五个峰值,分别是五个手指,这样我们就可以简单找到了。如果你是只伸出一两个手指,那么就只有一两个峰值了。【4】(2)方案2:凸包检测法【5】如上图所示,黑色的轮廓线为convexityhull,而convexityhull与手掌之间的部分为convexitydefects.每个convexitydefect区域有四个特征量:起始点(startPoint),结束点(endPoint),距离convexityhull最远点(farPoint),最远点到conv
7、exityhull的距离(depth)。(3)方案分析比较与选择在方案1中,按顺时针方向或者逆时针方向遍历出现峰值保持的点数超过80个即认为这个点是指尖,辨别存在偏差。在方案2中,通过凸包检测到的点集识别,只要有指尖凸包检测法就会检测到相关的点,这个点就是指尖。此法辨别率高。因此,锤子剪刀布游戏对于指尖检测辨别率不高的识别方法选用方案1,数字识别对于指尖检测辨别率高的识别方法选用方案2.四、 方案实施得到二值化图像并以此作为掩码迭代器访问CrCb元素转换为YCrCb颜色空间打开摄像头获取图像A. 手势分割与摄像头获得的原图像进行融合得到手的部分形态学操作过滤噪点代码实现:/肤色提取,skinA
8、rea为二值化肤色图像 void skinExtract(const Mat &frame, Mat &skinArea)Mat YCbCr;vector planes;/转换为YCrCb颜色空间 cvtColor(frame, YCbCr, CV_RGB2YCrCb);/将多通道图像分离为多个单通道图像 split(YCbCr, planes);/运用迭代器访问矩阵元素 MatIterator_ it_Cb = planes1.begin(),it_Cb_end = planes1.end();MatIterator_ it_Cr = planes2.begin();MatIterator_
9、 it_skin = skinArea.begin();/人的皮肤颜色在YCbCr色度空间的分布范围:100=Cb=127, 138=Cr=170 for (; it_Cb != it_Cb_end; +it_Cr, +it_Cb, +it_skin)if (100 = *it_Cb & *it_Cb = 127) & (138 = *it_Cr & *it_Cr = 170)*it_skin = 255;else*it_skin = 0;/ 形态学操作,去除噪声,并使手的边界更加清晰Mat element = getStructuringElement(MORPH_RECT, Size(3,
10、 3);erode(skinArea, skinArea, element);morphologyEx(skinArea, skinArea, MORPH_OPEN, element);dilate(skinArea, skinArea, element);morphologyEx(skinArea, skinArea, MORPH_CLOSE, element);B. 重心距离法实现锤子剪刀布的游戏点与重心的距离保持一定的点数逆时针遍历轮廓点集检测手的轮廓,得到轮廓点集手的部分输出结果并播放提示音得到手势结果代码实现:void CMF_ImageProcessingDlg:OnBnClick
11、edPlay()/ TODO: 在此添加控件通知处理程序代码if (musicFlag)/如果原来正在播放,则先关闭MCIWndClose(hMCI);MCIWndDestroy(hMCI);/首先显示原始图像img = &frame.operator IplImage();DrawToMFC(IDC_OriginalImage, img);skinArea.create(frame.rows, frame.cols, CV_8UC1);/创建同frame大小的单通道8位矩阵skinExtract(frame, skinArea);Mat show_img;frame.copyTo(show_
12、img, skinArea);/skinArea作为掩码将frame拷贝至show_imgvectorvector contours; / 轮廓的结构信息vector hierarchy;/ 凸包络的点集/寻找轮廓 findContours(skinArea, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);/ 找到最大的轮廓 int index;/最大轮廓索引double area, maxArea(0);for (size_t i = 0; i maxArea)maxArea = area;index = i;Mome
13、nts moment = moments(skinArea, true);/中心矩,用于计算重心Point center(int(moment.m10 / moment.m00), int(moment.m01 / moment.m00);circle(show_img, center, 8, Scalar(0, 0, 255), CV_FILLED);/画重心/ 寻找指尖 vector couPoint = contoursindex;vector fingerTips;/指尖点Point tmp;int max(0), count(0), notice(0);int gesture = 0
14、,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 couPointnotice.y)continue;/ 离得太近的不算 for (size_t j = 0; j fingerTips.size(); j+)if (abs(couPointno
15、tice.x - fingerTipsj.x) 125)/点到重心的距离不小于125gesture+;circle(show_img, couPointnotice, 6, Scalar(0, 255, 0), CV_FILLED);/line(show_img, center, couPointnotice, Scalar(255, 0, 0), 2);if (gesture = 0 | gesture = 1)/0表示石头,1表示剪刀,2表示布flag = 0;else if (gesture = 2)flag = 1;elseflag = 2;IplImage *showImage;sh
16、owImage = &show_img.operator IplImage();DrawToMFC(IDC_ShowResult, showImage);srand(unsigned)time(NULL);/产生随机数int r = rand() % 3;int result = judge(flag, r);/判断胜负CString yourGes, compGes,res;CString choose;/音频switch (result)case 0:yourGes = _T(石头);compGes = _T(石头);res = _T(平手!);if (musicPlay)choose =
17、 _T(G:KwDownloadsong纯音乐-忧伤还是快乐.mp3); /音乐文件的路径 break;case 1:yourMark+;yourGes = _T(石头);compGes = _T(剪刀);res = _T(你赢了!);if (musicPlay)choose = _T(G:KwDownloadsong纯音乐-梦中的婚礼-Mariage DAmour.mp3);break;case 2:computer+;yourGes = _T(石头);compGes = _T(布);res = _T(你输了!);if (musicPlay)choose = _T(G:KwDownloads
18、ong纯音乐-The Truth That You Leave.mp3);break;case 3:computer+;yourGes = _T(剪刀);compGes = _T(石头);res = _T(你输了!);if (musicPlay)choose = _T(G:KwDownloadsong纯音乐-The Truth That You Leave.mp3);break;case 4:yourGes = _T(剪刀);compGes = _T(剪刀);res = _T(平手!);if (musicPlay)choose = _T(G:KwDownloadsong纯音乐-忧伤还是快乐.m
19、p3); /音乐文件的路径 break;case 5:yourMark+;yourGes = _T(剪刀);compGes = _T(布);res = _T(你赢了!);if (musicPlay)choose = _T(G:KwDownloadsong纯音乐-梦中的婚礼-Mariage DAmour.mp3);break;case 6:yourMark+;yourGes = _T(布);compGes = _T(石头);res = _T(你赢了!);if (musicPlay)choose = _T(G:KwDownloadsong纯音乐-梦中的婚礼-Mariage DAmour.mp3);
20、break;case 7:computer+;yourGes = _T(布);compGes = _T(剪刀);res = _T(你输了!);if (musicPlay)choose = _T(G:KwDownloadsong纯音乐-The Truth That You Leave.mp3);break;case 8:yourGes = _T(布);compGes = _T(布);res = _T(平手!);if (musicPlay)choose = _T(G:KwDownloadsong纯音乐-忧伤还是快乐.mp3); /音乐文件的路径 break;default:break;yours.
21、Format(_T(%d), yourMark);comp.Format(_T(%d), computer);SetDlgItemText(IDC_EDITYOURMARK, yours);SetDlgItemText(IDC_EDITCOMPUTER, comp);if (NULL = mTipDlg)/如果还没有新建则创建mTipDlg = new TipDlg();mTipDlg-Create(IDD_GAMEDIALOG, this);mTipDlg-ShowWindow(SW_SHOW);mTipDlg-SetDlgItemTextW(IDC_EDITYOURS, yourGes);
22、mTipDlg-SetDlgItemTextW(IDC_EDITCOMPS, compGes);mTipDlg-SetDlgItemTextW(IDC_EDITRESULT, res);UpdateData(FALSE);hMCI = MCIWndCreate(NULL, NULL, WS_POPUP | MCIWNDF_NOPLAYBAR | MCIWNDF_NOMENU, choose);/音频部分MCIWndPlay(hMCI);C. 凸包检测法点集筛选,每个指尖只画一个点凸包检测得到凸包点集轮廓检测得到最大轮廓手的部分输出结果顺时针遍历确定每个指尖只画一个点代码实现:Point gra
23、vity;int CMF_ImageProcessingDlg:numRecog()numResult = NULL;skinArea.create(frame.rows, frame.cols, CV_8UC1);/创建同frame大小的单通道8位矩阵skinExtract(frame, skinArea);frame.copyTo(numResult, skinArea);/skinArea作为掩码将frame拷贝至show_imgvector vector contours; / 轮廓 vector hierarchy; / 轮廓的结构信息 vector hull; / 凸包络的点集 v
24、ector fingerTips;/指尖点contours.clear();hierarchy.clear();fingerTips.clear();/ 得到手的轮廓 findContours(skinArea, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);Moments moment = moments(skinArea, true);/中心矩,用于计算重心Point center(int(moment.m10 / moment.m00), int(moment.m01 / moment.m00);circle
25、(numResult, center, 8, Scalar(0, 0, 255), CV_FILLED);/画重心gravity = center;/ 找到最大的轮廓 int index;/最大轮廓索引double area, maxArea(0);for (size_t i = 0; i maxArea)maxArea = area;index = i;vector filterContours = contoursindex; / 筛选后的轮廓 convexHull(Mat(filterContours), hull, true);/ 得到轮廓的凸包络 int hullcount = (i
26、nt)hull.size();for (int i = 0; ihullcount - 1; i+)/逆时针遍历line(numResult, hulli + 1, hulli, Scalar(255, 0, 0), 2, CV_AA);/画凸包轮廓线if (center.y 40)/两点之间的距离不小于40fingerTips.push_back(hulli);line(numResult, hullhullcount - 1, hull0, Scalar(255, 0, 0), 2, CV_AA);/终点到起始点的线段for (int j = hullcount - 1; j 0; j-)
27、/顺时针遍历找最后一个指尖点if (sqrt(pow(hullj.x - hullj - 1.x), 2) + pow(hullj.y - hullj - 1.y), 2) 40)/两点之间的距离不小于40fingerTips.push_back(hullj);break;int fingerNum = 0;for (size_t j = fingerTips.size() - 1; j 0; j-)/画指尖点/两点之间的距离不小于40,点到重心的距离不小于112if (sqrt(pow(fingerTipsj.x - fingerTipsj - 1.x, 2) + pow(fingerTip
28、sj.y - fingerTipsj - 1.y, 2) 40& int(sqrt(pow(fingerTipsj.x - center.x, 2) + pow(fingerTipsj.y - center.y, 2) 112)circle(numResult, fingerTipsj, 6, Scalar(0, 255, 0), CV_FILLED);fingerNum+;if (int(sqrt(pow(fingerTips0.x - center.x, 2) + pow(fingerTips0.y - center.y, 2) 112 &int(sqrt(pow(fingerTips0.
29、x - fingerTipsfingerTips.size() - 1.x, 2) +pow(fingerTips0.y - fingerTipsfingerTips.size() - 1.y, 2) 40)/点到重心的距离不小于112同时第一个点和最后一个点的距离大于40circle(numResult, fingerTips0, 6, Scalar(0, 255, 0), CV_FILLED);fingerNum+;return fingerNum;手的部分D. 鼠标控制鼠标响应辨别手指根数,控制鼠标手指根数鼠标响应0移动1单击左键2单击右键3双击左键4拖动5松开左键5结束线程,退出鼠标控
30、制代码实现:void CMF_ImageProcessingDlg:BnClickedBtmouse()/ 主要处理函数在这里写 this-SetCapture();/捕获鼠标消息threadFlag = true;/线程启动标志MF_ImageProcessingcap.open(0);cap frame;delayms(1000);int l = 1, r = 1;/单击标志int x, y;/鼠标坐标int fingerNum;IplImage *showImage;Point point;while (true)delayms(5);fingerNum = numRecog();sho
31、wImage = &numResult.operator IplImage();DrawToMFC(IDC_ShowImage, showImage);point = gravity;if (fingerNum = 0)/移动x = int(point.x * 1550 / showImage-width);/屏幕宽1366,高768y = int(point.y * 850 / showImage-height);mouse_event(MOUSEEVENTF_MOVE, 0, 0, 0, 0);SetCursorPos(x, y);l = 1;r = 1;else if (fingerNu
32、m = 1)/单击左键if (l = 1)mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);l = 0;r = 1;else if (fingerNum = 2)/单击右键if (r = 1)mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);r = 0;l = 1;else if (fingerNum = 3)/双击if (l = 1)mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENT
33、F_LEFTUP, 0, 0, 0, 0);mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);l = 0;r = 1;else if (fingerNum = 4)/单击拖动if (l = 1)mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);l = 0;x = int(point.x * 1366 / showImage-width);y = int(point.y * 768 / showImage-height);SetCursorPos(x, y);r = 1;
34、else if (fingerNum = 5)/松开左键if (l = 0)mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);l = 1;r = 1;if (fingerNum 3)return;if (!threadFlag)/关闭摄像头按钮被按下,结束线程return;获取摄像头图像E. 人脸识别识别并标记输出加载Haar特征分类器代码实现:void CMF_ImageProcessingDlg:OnBnClickedBtfacedetect()/ TODO: 在此添加控件通知处理程序代码img = &frame.operator IplImage()
35、;Mat faceDetImg(img);const char *pStrCascadeFileName = haarcascade_frontalface_alt.xml;CvHaarClassifierCascade *pHaarCascade = NULL;pHaarCascade = (CvHaarClassifierCascade*)cvLoad(pStrCascadeFileName);IplImage *pGrayImage = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);cvCvtColor(img, pGrayImage, C
36、V_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 = cvCreateMemStorage(0);cvClearMemStorage(pcvMStorage);/ 识别DWORD dwTimeBegin, dwTimeEnd;dwTimeB
37、egin = GetTickCount();CvSeq *pcvSeqFaces = cvHaarDetectObjects(pGrayImage, pHaarCascade, pcvMStorage);dwTimeEnd = GetTickCount();/AfxMessageBox(TEXT(共检测出人数:%d,使用时间%f), pcvSeqFaces-total, dwTimeEnd);/ 标记for (int i = 0; i total; i+)CvRect* r = (CvRect*)cvGetSeqElem(pcvSeqFaces, i);Rect face_region;fac
38、e_region.x = r-x;face_region.y = r-y;face_region.width = r-width;face_region.height = r-height;rectangle(faceDetImg, face_region, FaceCirclecolorsi % 8, 1, 8, 0);/矩形标记cvReleaseMemStorage(&pcvMStorage);IplImage *face_detect;face_detect = &faceDetImg.operator IplImage();DrawToMFC(IDC_OriginalImage, face_detect);五、 调试及成品展示A. MFC人机交互界面B. 锤