利用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

你可能感兴趣的:(opencv)