zoukankan      html  css  js  c++  java
  • 利用KINECT+OPENCV检测手势的演示程序

    2011-4-10 增加结果图片,更新代码,将模板改为6个(0-5)

    1,原理:读入KINECT深度数据,转换为二值图像,找到轮廓,与轮廓模板比较,找到HU矩阵最小的为匹配结果

    2,基础:OPENNI, OPENCV2.2 以及http://blog.163.com/gz_ricky/blog/static/182049118201122311118325/
    的例程基础上修改

    3,结果:仅仅用于演示利用OPENCV+OPENNI编程,对结果精度,处理速度等没有优化,仅供参考

    对0,1和5的比较比较准确

    Hand detection

    废话少说,一切都在代码中。

    Hand detection
    1 // KinectOpenCVTest.cpp : 定义控制台应用程序的入口点。
    2  //
    3
    4 #include "stdafx.h"
    5
    6
    7 #include <stdlib.h>
    8 #include <iostream>
    9 #include <string>
    10 #include <XnCppWrapper.h>
    11 #include <opencv2/opencv.hpp>
    12
    13 //#include "opencv/cv.h"
    14 //#include "opencv/highgui.h"
    15 using namespace std;
    16 using namespace cv;
    17
    18 #define SAMPLE_XML_PATH "http://www.cnblogs.com/Data/SamplesConfig.xml"
    19
    20 //全局模板轮廓
    21 vector<vector<Point>> g_TemplateContours;
    22
    23 //模板个数
    24 int g_handTNum = 6;
    25
    26 void CheckOpenNIError( XnStatus eResult, string sStatus )
    27 {
    28 if( eResult != XN_STATUS_OK )
    29 {
    30 cerr << sStatus << " Error: " << xnGetStatusString( eResult ) << endl;
    31 return;
    32 }
    33 }
    34
    35 //载入模板的轮廓
    36 void init_hand_template()
    37 {
    38 //int handTNum = 10;
    39 string temp = "HandTemplate/";
    40
    41 int i = 0;
    42
    43
    44 for(i=0; i<g_handTNum; i++)
    45 {
    46 stringstream ss;
    47 ss << i << ".bmp";
    48
    49 string fileName = temp + ss.str();
    50
    51 //读入灰度图像
    52 Mat src = imread(fileName, 0);
    53
    54 if(!src.data)
    55 {
    56 printf("未找到文件: %s\n", fileName);
    57 continue;
    58 }
    59
    60 vector<vector<Point>> contours;
    61 vector<Vec4i> hierarchy;
    62
    63 findContours(src, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
    64 //findContours(src, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
    65
    66 g_TemplateContours.push_back(contours[0]);
    67 }
    68 }
    69
    70 //模板匹配手
    71 int hand_template_match(Mat& hand)
    72 {
    73 //int handTNum = 10;
    74 int minId = -1;
    75 double minHu = 1;
    76
    77 double hu;
    78 int method = CV_CONTOURS_MATCH_I1;
    79
    80 //match_num = 0;
    81
    82 for(int i=0; i<g_handTNum; i++){
    83
    84 Mat temp(g_TemplateContours.at(i));
    85 hu = matchShapes(temp, hand, method, 0);
    86
    87 //找到hu矩最小的模板
    88 if(hu < minHu){
    89 minHu = hu;
    90 minId = i;
    91 }
    92
    93 //printf("%f ", hu);
    94 }
    95
    96 //显示匹配结果
    97 int Hmatch_value = 25;//模板匹配系数
    98
    99 if(minHu<((double)Hmatch_value)/100)
    100 return minId;
    101 else
    102 return -1;
    103 }
    104
    105 void findHand(Mat& src, Mat& dst)
    106 {
    107 vector<vector<Point>> contours;
    108 vector<Vec4i> hierarchy;
    109
    110 //找到外部轮廓
    111 //findContours(src, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
    112 findContours(src, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); //CV_CHAIN_APPROX_NONE);
    113 //findContours(src, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
    114
    115 Mat dst_r = Mat::zeros(src.rows, src.cols, CV_8UC3);
    116 dst_r.copyTo(dst);
    117
    118 // iterate through all the top-level contours,
    119 // draw each connected component with its own random color
    120 int idx = 0;
    121 double maxArea = 0.0;
    122 int maxId = -1;
    123
    124 for(unsigned int i = 0; i<contours.size(); i++)
    125 {
    126 Mat temp(contours.at(i));
    127 double area = fabs(contourArea(temp));
    128 if(area > maxArea)
    129 {
    130 maxId = i;
    131 maxArea = area;
    132 }
    133 }
    134
    135 //for( ; idx >= 0; idx = hierarchy[idx][0] )
    136 //{
    137 // //Scalar color( rand()&255, rand()&255, rand()&255 );
    138 // //drawContours(dst, contours, idx, color, CV_FILLED, 8, hierarchy );
    139
    140 // double area = contourArea(contours.at(idx));
    141 // if(area > maxArea)
    142 // {
    143 // maxId = idx;
    144 // maxArea = area;
    145 // }
    146 //}
    147
    148 //显示最大轮廓外形,以及最佳匹配的模板ID
    149 if(contours.size() > 0)
    150 {
    151 Scalar color(0, 255, 255 );
    152 drawContours(dst, contours, maxId, color);
    153
    154 Mat hand(contours.at(maxId));
    155 int value = hand_template_match(hand);
    156
    157 if(value >= 0)
    158 {
    159 Scalar templateColor(255, 0, 255 );
    160 drawContours(dst, g_TemplateContours, value, templateColor);
    161
    162 printf("Match %d \r\n", value);
    163
    164 stringstream ss;
    165 ss << "Match " << value;
    166 string text = ss.str();
    167 putText(dst, text, Point(300, 30), FONT_HERSHEY_SIMPLEX, 1.0, templateColor);
    168 }
    169 }
    170 }
    171
    172
    173 int HandDetect()
    174 {
    175 init_hand_template();
    176
    177 XnStatus eResult = XN_STATUS_OK;
    178
    179 // 1. initial val
    180 xn::DepthMetaData m_DepthMD;
    181 xn::ImageMetaData m_ImageMD;
    182
    183 // for opencv Mat
    184 Mat m_depth16u( 480,640,CV_16UC1);
    185 Mat m_rgb8u( 480,640,CV_8UC3);
    186 Mat m_DepthShow( 480,640,CV_8UC1);
    187 Mat m_ImageShow( 480,640,CV_8UC3);
    188
    189 Mat m_DepthThreshShow( 480,640,CV_8UC1);
    190 Mat m_HandShow( 480,640,CV_8UC3);
    191
    192 //cvNamedWindow("depth");
    193 //cvNamedWindow("image");
    194 //cvNamedWindow("depthThresh");
    195
    196 char key=0;
    197
    198 // 2. initial context
    199 xn::Context mContext;
    200
    201 eResult = mContext.Init();
    202
    203 //xn::EnumerationErrors errors;
    204 //eResult = mContext.InitFromXmlFile(SAMPLE_XML_PATH, &errors);
    205
    206 CheckOpenNIError( eResult, "initialize context" );
    207
    208 //Set mirror
    209 mContext.SetGlobalMirror(!mContext.GetGlobalMirror());
    210
    211 // 3. create depth generator
    212 xn::DepthGenerator mDepthGenerator;
    213 eResult = mDepthGenerator.Create( mContext );
    214 CheckOpenNIError( eResult, "Create depth generator" );
    215
    216 // 4. create image generator
    217 xn::ImageGenerator mImageGenerator;
    218 eResult = mImageGenerator.Create( mContext );
    219 CheckOpenNIError( eResult, "Create image generator" );
    220
    221 // 5. set map mode
    222 XnMapOutputMode mapMode;
    223 mapMode.nXRes = 640;
    224 mapMode.nYRes = 480;
    225 mapMode.nFPS = 30;
    226 eResult = mDepthGenerator.SetMapOutputMode( mapMode );
    227 eResult = mImageGenerator.SetMapOutputMode( mapMode );
    228
    229 //由于 Kinect 的深度摄像机和彩色摄像机是在不同的位置,而且镜头本身的参数也不完全相同,所以两个摄像机所取得的画面会有些微的差异
    230 //将深度摄像机的视角调整到RGB摄像机位置
    231 // 6. correct view port
    232 mDepthGenerator.GetAlternativeViewPointCap().SetViewPoint( mImageGenerator );
    233
    234 // 7. start generate data
    235 eResult = mContext.StartGeneratingAll();
    236
    237 // 8. read data
    238 eResult = mContext.WaitNoneUpdateAll();
    239 while( (key!=27) && !(eResult = mContext.WaitNoneUpdateAll( )) )
    240 {
    241 // 9a. get the depth map
    242 mDepthGenerator.GetMetaData(m_DepthMD);
    243 memcpy(m_depth16u.data,m_DepthMD.Data(), 640*480*2);
    244
    245 // 9b. get the image map
    246 mImageGenerator.GetMetaData(m_ImageMD);
    247 memcpy(m_rgb8u.data,m_ImageMD.Data(),640*480*3);
    248
    249 //将未知深度转为白色,便于在OPENCV中分析
    250 XnDepthPixel* pDepth = (XnDepthPixel*)m_depth16u.data;
    251
    252 for (XnUInt y = 0; y < m_DepthMD.YRes(); ++y)
    253 {
    254 for (XnUInt x = 0; x < m_DepthMD.XRes(); ++x, ++pDepth)
    255 {
    256 if (*pDepth == 0)
    257 {
    258 *pDepth = 0xFFFF;
    259 }
    260 }
    261 }
    262
    263
    264 //由于OpenNI获得的深度图片是16位无符号整数,而OpenCV显示的是8位的,所以要作转换。
    265
    266 //将距离转换为灰度值(0-2550mm 转换到 0-255),例如1000毫米转换为 1000×255/2550 = 100
    267 //m_depth16u.convertTo(m_DepthShow,CV_8U, 255/2096.0);
    268 m_depth16u.convertTo(m_DepthShow,CV_8U, 255/2550.0);
    269
    270 //可以考虑根据数据缩减图像大小到有效范围
    271
    272 //在此对灰度图像进行处理,平滑和去噪声
    273 //medianBlur(m_DepthShow, m_DepthThreshShow, 3);
    274 //m_DepthThreshShow.copyTo(m_DepthShow);
    275 //medianBlur(m_DepthThreshShow, m_DepthShow, 3);
    276 blur(m_DepthShow, m_DepthThreshShow, Size(3, 3));
    277 //m_DepthThreshShow.copyTo(m_DepthShow);
    278 blur(m_DepthThreshShow, m_DepthShow, Size(3, 3));
    279
    280 Mat pyrTemp( 240,320,CV_8UC1);
    281 pyrDown(m_DepthShow, pyrTemp);
    282 pyrUp(pyrTemp, m_DepthShow);
    283
    284 //dilate(m_DepthShow, m_DepthThreshShow, Mat(), Point(-1,-1), 3);
    285 //erode(m_DepthThreshShow, m_DepthShow, Mat(), Point(-1,-1), 3);
    286
    287 //for(int i = 0; i < m_depth16u.rows; i++)
    288 // for(int j = 0; j < m_depth16u.cols; j++)
    289 // {
    290 // if(m_depth16u.at<unsigned short>(i,j) < 1)
    291 // m_depth16u.at<unsigned short>(i,j) == 0xFFFF;
    292
    293 // //m_depth16u.at<double>(i,j)=1./(i+j+1);
    294 // }
    295
    296 //RGB和BGR在内存对应的位置序列不同,所以也要转换。
    297 cvtColor(m_rgb8u,m_ImageShow,CV_RGB2BGR);
    298
    299 //imshow("depth", m_DepthShow);
    300 //imshow("image", m_ImageShow);
    301
    302 double thd_max = 0xFFFF;
    303 double thd_val = 100.0;
    304
    305 //反转黑白图像,以便找到最大外部轮廓
    306 //threshold(m_DepthShow, m_DepthThreshShow, thd_val, thd_max, CV_THRESH_BINARY);
    307 threshold(m_DepthShow, m_DepthThreshShow, thd_val, thd_max, CV_THRESH_BINARY_INV);
    308 imshow("depthThresh", m_DepthThreshShow);
    309
    310 findHand(m_DepthThreshShow, m_HandShow);
    311 imshow( "Hand", m_HandShow );
    312
    313 key=cvWaitKey(20);
    314 }
    315
    316 // 10. stop
    317 mContext.StopGeneratingAll();
    318 mContext.Shutdown();
    319
    320 return 0;
    321 }
    322
    323 int _tmain(int argc, _TCHAR* argv[])
    324 {
    325 HandDetect();
    326 }

    trackback: http://blog.csdn.net/firefight/archive/2011/04/06/6304050.aspx

  • 相关阅读:
    Linux shell 进制转换
    shell 的getopts用法
    COOKIE 和SESSION
    wcf在iis6上的部署
    fastjson修改json的值(转)
    jsonpath的简单用法(转)
    fastjson序列化定制过滤器
    java操作excel给单元格加下拉列表(转)
    Django Admin管理后台详解1(转)
    Django Admin管理后台详解2(转)
  • 原文地址:https://www.cnblogs.com/JohnShao/p/2047671.html
Copyright © 2011-2022 走看看