文档库 最新最全的文档下载
当前位置:文档库 › 手势识别

手势识别

手势识别
手势识别

目录:OpenCV学习笔记

最近也在玩手势识别,资料找了很多,基本可以分为静态手势识别和动态手势识别,先弄个简单的静态手势识别给大家看看。

基本流程如下:

先滤波去噪-->转换到HSV空间-->根据皮肤在HSV空间的分布做出阈值判断,这里用到了inRange函数,然后进行一下形态学的操作,去除噪声干扰,是手的边界更加清晰平滑-->得到的2值图像后用findContours找出手的轮廓,去除伪轮廓后,再用convexHull函数得到凸包络。

结果如下:

源代码:

控制台版本main.cpp:

1.

2.// #include "stdafx.h"

3.//#include "cv.h"

4.

5.#include

6.#include

7.

8.//#include

9.//#include

10.

11.#include

12.

https://www.wendangku.net/doc/c85952832.html,ing namespace std;

14.

15.int pmsf_value = 5;//均值飘逸分割平滑系数

16.int MopEx_value = 2;//开运算

17.int Hmatch_value = 25;//模板匹配系数

18.

19.//亮度

20.int V_low = 30;

21.int V_high = 250;

22.//饱和度

23.int S_low = 10;

24.int S_high = 170;

25.//色相

26.int H_low_max = 40;

27.int H_high_min = 100;

28.int if_high_light = 1; //是否高光补偿

29.

30.IplImage *src = 0;

31.IplImage *srcResize = 0;

32.

33.IplImage *img_YCrCb = 0;

34.CvSize newSize;

35.CvSize sz;

36.

37.IplImage *tmp1;

38.IplImage *tmp2;

39.IplImage *tmp3;

40.IplImage *src2;

41.IplImage *src1;

42.

43.///////////////////////////////////////////////////////////////////////

/////////////////////////////////////////

44.IplImage *YCrCb;

45.IplImage *YCrCb_mask;

46.IplImage *Y_channel,*Cr_channel, *Cb_channel;

47.IplImage *Y_cmp,*Cr_cmp, *Cb_cmp;

48.

49.CvScalar Y_lower;

50.CvScalar Y_upper;

51.

52.CvScalar Cr_lower;

53.CvScalar Cr_upper;

54.

55.CvScalar Cb_lower;

56.CvScalar Cb_upper;

57.

58.CvScalar YCrCb_lower;

59.CvScalar YCrCb_upper;

60.

61.///////////////////////////////////////////////////////////////////////

////////////////////////////////////////

62.

63.void init_hand_YCrCb()

64.{

65. //

66. img_YCrCb = cvCreateImage( sz, 8, 3);

67. YCrCb_mask = cvCreateImage(sz,IPL_DEPTH_8U,1);;

68.

69. //最终的图片

70. YCrCb = cvCreateImage(sz,IPL_DEPTH_8U,3);

71.

72.

73. //三通道

74. Y_channel = cvCreateImage(sz,IPL_DEPTH_8U,1);

75. Cr_channel = cvCreateImage(sz,IPL_DEPTH_8U,1);

76. Cb_channel = cvCreateImage(sz,IPL_DEPTH_8U,1);

77.

78. //按范围截取后

79. Y_cmp = cvCreateImage(sz,IPL_DEPTH_8U,1);

80. Cr_cmp = cvCreateImage(sz,IPL_DEPTH_8U,1);

81. Cb_cmp = cvCreateImage(sz,IPL_DEPTH_8U,1);

82.

83. //Y,Cr,Cb的颜色范围

84. Y_lower = CV_RGB(0,0,130);

85. Y_upper = CV_RGB(0,0,130);

86.

87. Cr_lower = CV_RGB(0,0,125);

88. Cr_upper = CV_RGB(0,0,125);

89.

90. Cb_lower = CV_RGB(0,0,132);

91. Cb_upper = CV_RGB(0,0,147);

92.

93. YCrCb_lower = cvScalar(0,0,132,0);

94. YCrCb_upper = cvScalar(130,125,147,0);

95.}

96.///////////////////////////////////////////////////////////////////////

/////////////////////////////////////////

97.void hand_YCrCb()

98.{

99. //转换到YCrBr

100. cvCvtColor(src2,img_YCrCb, CV_RGB2YCrCb);

101.

102.

103. //分割到Y,Cr,Cb

104. cvSplit(img_YCrCb,Y_channel,Cr_channel,Cb_channel,0);

105.

106. //将Y_channel的位于 Y_lower 和 Y_upper 之间的元素复制到 Y_tmp中107. cvInRangeS( Y_channel, Y_lower, Y_upper, Y_cmp);

108. cvInRangeS(Cr_channel,Cr_lower,Cr_upper ,Cr_cmp);

109. cvInRangeS(Cb_channel,Cb_lower,Cb_upper ,Cb_cmp);

110.

111. //合并Y,Cr,Cb通道到YCrCb中

112. cvMerge(Y_cmp,Cr_cmp,Cb_cmp,0,YCrCb);

113.

114. //显示结果

115. cvShowImage("YCrCb_mask",YCrCb);

116.

117.

118. //cvInRangeS (img_YCrCb, YCrCb_lower, YCrCb_upper, YCrCb_mask); 119. //cvShowImage( "YCrCb_mask", YCrCb_mask);

120.

121.

122.}

123./////////////////////////////////////////////////////////////////////// ///////////////////////////////////////

124.

125. IplImage* hsv_image;

126. IplImage* hsv_mask;

127. CvScalar hsv_min;

128. CvScalar hsv_max;

129.

130. IplImage *H_img,*S_img, *V_img;

131. IplImage *H_mask, *H_mask1,*S_mask, *S_mask1, *V_mask, *V_mask1, *V_mask2;

132./////////////////////////////////////////////////////////////////////// ////////////////////////////////////////

133.void init_hand_HSV()

134.{

135.

136. hsv_image = cvCreateImage( sz, 8, 3);

137. hsv_mask = cvCreateImage( sz, 8, 1);

138. hsv_min = cvScalar(0, 20, 20, 0);

139. hsv_max = cvScalar(20, 250, 255, 0);

140. //hsv_mask->origin = 1;

141.

142. //方法2: 单独处理各个通道

143. H_img = cvCreateImage(sz,IPL_DEPTH_8U,1);

144. S_img = cvCreateImage(sz,IPL_DEPTH_8U,1);

145. V_img = cvCreateImage(sz,IPL_DEPTH_8U,1);

146.

147. H_mask = cvCreateImage(sz,IPL_DEPTH_8U,1);

148. H_mask1 = cvCreateImage(sz,IPL_DEPTH_8U,1);

149. S_mask = cvCreateImage(sz,IPL_DEPTH_8U,1);

150. S_mask1 = cvCreateImage(sz,IPL_DEPTH_8U,1);

151. V_mask = cvCreateImage(sz,IPL_DEPTH_8U,1);

152. V_mask2 = cvCreateImage(sz,IPL_DEPTH_8U,1);

153. V_mask1 = cvCreateImage(sz,IPL_DEPTH_8U,1);

154.

155.

156.

157.}

158./////////////////////////////////////////////////////////////////////// ///////////////////////////////////////

159.

160.void color_blance()

161.{

162. CvScalar avg = cvAvg(H_img, 0);

163. printf("%f, %f, %f, %f\n",

avg.val[0],avg.val[1],avg.val[2],avg.val[3]);

164. double d = 128 - avg.val[0];

165. avg.val[0] = d;

166. cvAddS(H_img, avg, H_img, 0);

167.

168.

169.}

170.

171.

172.

173.

174.

175./////////////////////////////////////////////////////////////////////// ////////////////////////////////////////

176.void hand_HSV()

177.{

178.

179. cvCvtColor(src2, hsv_image, CV_BGR2HSV);

180. //cvInRangeS (hsv_image, hsv_min, hsv_max, hsv_mask); 181. //cvShowImage( "hsv_msk", hsv_mask);

182.

183.

184.

185.

186. //方法2: 单独处理各个通道

187. cvSplit(hsv_image,H_img,S_img,V_img,0);

188.

189. //color_blance();

190. //cvMerge(H_img,S_img,V_img,0,hsv_image);

191. //cvShowImage( "色彩平衡后", hsv_image);

192. //cvShowImage( "H通道", H_img);

193.

194.

195.

196.

197.

198. //直方图均衡化(效果更差)

199. //cvEqualizeHist(H_img, H_img);

200.

201. //cvShowImage( "H通道_均衡化", H_img);

202.

203. //自适应

204. //cvAdaptiveThreshold(H_img, H_mask, 30, 0, 0, 3, 5); 205.

206. //cvShowImage( "H", H_img);

207. //cvShowImage( "S", S_img);

208. //cvShowImage( "V", V_img);

209.

210. //色相

211. cvInRangeS(H_img,cvScalar(0,0,0,0),cvScalar(H_low_max,0 ,0,0),H_mask);//红色区

212. cvInRangeS(H_img,cvScalar(256 -

H_high_min,0,0,0),cvScalar(256,0,0,0),H_mask1);//紫色区

213.

214. //饱和度

215. cvInRangeS(S_img,cvScalar(S_low,0,0,0),cvScalar(S_high, 0,0,0),S_mask); //中间区

216. //cvInRangeS(S_img,cvScalar(20,0,0,0),cvScalar(100,0,0,

0),S_mask1); //低饱和度

217.

218.

219.

220. //亮度

221. cvInRangeS(V_img,cvScalar(V_high,0,0,0),cvScalar(256,0, 0,0),V_mask);//高亮区

222. cvInRangeS(V_img,cvScalar(V_low,0,0,0),cvScalar(V_high, 0,0,0),V_mask1); //中间区

223. //cvInRangeS(V_img,cvScalar(150,0,0,0),cvScalar(250,0,0 ,0),V_mask2); //较亮区

224.

225.

226. //红黄, 和蓝紫的混合

227. cvOr(H_mask1, H_mask, H_mask, 0);

228.

229. //消除饱和度过低区域

230. cvAnd(H_mask,S_mask,H_mask,0);

231.

232. //cvShowImage( "饱和度过滤", H_mask);

233.

234. //消去过亮过暗区域

235. cvAnd(H_mask,V_mask1,H_mask,0);

236.

237. //cvShowImage( "亮度过滤", H_mask);

238.

239. //cvShowImage( "hsv_msk", H_mask);

240.

241.

242.

243. //补偿过亮区域

244. if(if_high_light) cvOr(H_mask,V_mask,H_mask,0);

245.

246. //cvShowImage( "补偿高光", H_mask);

247.

248. //cvShowImage( "曝光过度 V", V_mask);

249. //cvShowImage( "曝光补偿", S_mask);

250.

251.

252. //是否补偿曝光过度

253. hsv_mask = H_mask;

254.

255. //cvShowImage( "hsv_msk", H_mask);

256.

257.}

258.

259./////////////////////////////////////////////////////////////////////// ////////////////////////////////////////

260.

261.

262.//阀值化

263.IplImage* thd_src;

264.IplImage* thd_dst1;

265.IplImage* thd_dst2;

266.int thd_max = 255;

267.int thd_val = 100;

268.

269.void inti_threshold()

270.{

271. thd_src = cvCreateImage(sz,IPL_DEPTH_8U,1);

272. thd_dst1 = cvCreateImage(sz,IPL_DEPTH_8U,1);

273. thd_dst2 = cvCreateImage(sz,IPL_DEPTH_8U,1);

274.}

275.

276.void threshold()

277.{

278. cvCvtColor(src1, thd_src, CV_RGB2GRAY);

279. cvAdaptiveThreshold(thd_src, thd_dst1, thd_max,

CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, 3, 5);

280. cvThreshold(thd_src, thd_dst2, thd_val, thd_max, CV_THRESH_BINARY);

281.

282.

283. cvShowImage( "阀值前", thd_src);

284.

285. cvShowImage( "阀值化1", thd_dst1);

286. cvCreateTrackbar( "thd_max", "阀值化1", &thd_max, 256, 0); 287. cvShowImage( "阀值化2", thd_dst2);

288. cvCreateTrackbar( "thd_val", "阀值化2", &thd_val, 256, 0); 289.

290.}

291.

292.

293.

294./////////////////////////////////////////////////////////////////////// ////////////////////////////////////////

295.void resizeSrc()

296.{

297. double scale = 0.5;

298.

299.

300. //获得图像大小

301. sz = cvGetSize(src);

302. newSize.height = (int)(sz.height * scale);

303. newSize.width = (int)(sz.width * scale);

304.

305. src = cvCreateImage(newSize,IPL_DEPTH_8U,3);

306.

307. cvResize(src, src, CV_INTER_LINEAR);

308.

309.}

310.

311./////////////////////////////////////////////////////////////////////// ////////////////////////////////////////

312.

313.IplImage* smooth1;

314.IplImage* smooth2;

315.IplImage* smooth3;

316.IplImage* smooth4;

317.IplImage* smooth5;

318.

319.void reduce_noise()

320.{

321. //cvSmooth(hsv_mask, smooth1, CV_GAUSSIAN, 3, 0, 0, 0);

322. //cvSmooth(hsv_mask, smooth1, CV_MEDIAN, 3, 0, 0, 0);

323.

324. //cvSmooth(smooth1, smooth1, CV_BILATERAL, 3, 0, 0, 0);

325.

326. //cvDilate(hsv_mask, smooth1, 0 ,2);

327. //cvErode(smooth1 ,smooth2, 0, 2);

328.

329. cvMorphologyEx(hsv_mask, smooth1, 0, CV_SHAPE_RECT, CV_MOP_CLOSE, MopEx_value);

330. //cvMorphologyEx(smooth1, smooth2, 0, CV_SHAPE_RECT, CV_MOP_OPEN,

1);

331.

332. cvShowImage( "扩张腐蚀", smooth1);

333.

334. //cvSmooth(smooth2, smooth3, CV_MEDIAN, 9, 0, 0, 0);

335.

336. //cvShowImage( "平滑", smooth3);

337.}

338.

339.

340.

341./////////////////////////////////////////////////////////////////////// /////////////////////////////////////////

342.

343.//轮廓匹配

344.

345.IplImage* g_image = NULL;

346.IplImage* g_gray = NULL;

347.int g_thresh = 100;

348.CvMemStorage* g_storage = NULL;

349.CvMemStorage* g_storage1 = NULL;

350.CvMemStorage* g_storage2 = NULL;

351.

352.CvSeq* seqMidObj = 0;//塞选后的轮廓集合

353.int handNum = 0;

354.

355.

356./////////////////////////////////////////////////////////////////////// ////

357.

358.void hand_contours(IplImage* dst) {

359.

360. if( g_storage==NULL ) {

361. g_storage = cvCreateMemStorage(0);

362. g_storage1 = cvCreateMemStorage(0);

363. g_storage2 = cvCreateMemStorage(0);

364. } else {

365. cvClearMemStorage( g_storage );

366. cvClearMemStorage( g_storage1 );

367. cvClearMemStorage( g_storage2 );

368. }

369.

370.

371. int i = 0, j=0;

372. CvSeq* contours = 0;

373. CvSeq* contoursHead = 0;

374. CvSeq* p = NULL;

375. CvSeq* q = NULL;

376.

377.

378. seqMidObj = 0;

379. handNum = 0;

380.

381. //cvThreshold( g_gray, g_gray, g_thresh, 255, CV_THRESH_BINARY ); 382.

383. cvFindContours( dst, g_storage, &contours, sizeof(CvContour), CV_RETR_EXTERNAL);//只查找外轮廓

384. contoursHead = contours;//contours的头

385. //cvZero( g_gray );

386.

387.

388.

389. /*CvSeq* seqApprox;

390.

391. CvSeq* head;

392. int j=0;*/

393. /*for(p=contours;p != NULL; p = p->h_next){

394.

395. j++;

396.

397. seqApprox = cvApproxPoly(p, sizeof(CvContour),g_storage1, CV_POLY_APPROX_DP, 2, 0);

398. seqApprox = seqApprox->h_next;

399. if(p==contours) head = seqApprox;

400.

401. }

402. printf("total = %d\n ",j );

432.

433. if ( (double)contArea/imgArea < 0.015 ){continue;} 434.

435. //如果边界与窗口相连, 则排除

436. bound = cvBoundingRect(contours, 0);

437.

438. if( bound.x < dat

439. || bound.y < dat

440. || bound.x + bound.width + dat > newSize.width 441. || bound.y + bound.height + dat > newSize.height) 442. {

443. //printf(" %d, %d, %d, %d\n",bound.x, bound.y, bound.width, bound.height );

444. //cvRectangle(tmp3, cvPoint(bound.x, bound.y),cvPoint(bound.x + bound.width, bound.y +

bound.height),cvScalar(255,255,255,255),1,8,0);

445. continue;

446. }

447.

448. //建立轮廓链表

449. q = p;

450. //p = cvCloneSeq(contours, g_storage2);

451. p = contours;

452.

453. if(q == NULL){

454. seqMidObj = p;

455. //p->h_next = NULL;

456. //p->h_prev = NULL;

457. //printf("第1个!");

458. }else{

459. q->h_next = p;

460. p->h_prev = q;

461. //printf("1个!");

462. }

463. //j++;

464. handNum++;

465.

466. }

467.

468. //printf("找到轮廓: %d 个塞选: %d 个\n", i,j);

469.

470.

471.

472. if(seqMidObj){

473. seqMidObj->h_prev = NULL;

474. p->h_next = NULL;

475. }

476. if(handNum>0) printf("找到手: %d ",handNum);

477. //CvSeq* seqMidObj_head = seqMidObj;

478.

479.

480.

481.

482. cvZero( tmp3 );

483. if( seqMidObj )cvDrawContours( tmp3, seqMidObj,

cvScalarAll(255),cvScalar(255,0,0,0),1);//绘制轮廓

484.

485. //cvShowImage( "轮廓塞选", tmp3);

486.

487.

488.}

489./////////////////////////////////////////////////////////////////////// //////////////////////////////////////

490.

491.IplImage* tmp_img = 0;

492.CvMemStorage* storage_tmp = 0;

493.

494.CvSeq* handT = 0;

495.CvSeq* handT1 = 0;

496.CvSeq* handT2 = 0;

497.

498.

499.int handTNum = 10;//10个模板

500.

501.char *tmp_names[] =

{"1.bmp","2.bmp","3.bmp","4.bmp","5.bmp","6.bmp","7.bmp","8.bmp","9.bmp ","10.bmp"};

502.char *num_c[] = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}; 503.

504.

505.

506./////////////////////////////////////////////////////

507.

508.//载入模板的轮廓

509.void init_hand_template()

510.{

511.

512. storage_tmp = cvCreateMemStorage(0);

513.

514. int i = 0;

515. for(i=0; i

516.

517. tmp_img = cvLoadImage(tmp_names[i],

CV_LOAD_IMAGE_GRAYSCALE);

518. if(!tmp_img){

519. printf("未找到文件: %s\n", tmp_names[i]);

520. continue;

521. }

522. //cvShowImage("载入模板", tmp_img);

523. handT1 = handT2;

524. cvFindContours( tmp_img, storage_tmp, &handT2, sizeof(CvContour), CV_RETR_EXTERNAL);

525.

526. if(handT2){

527. printf("载入模板: %s 成功!\n",tmp_names[i]); 528. if(handT1 == NULL){

529. printf("载入第一个模板!\n");

530. handT = handT2;

531. }else{

532. handT2->h_prev = handT1;

533. handT1->h_next = handT2;

534. }

535.

536. }

537.

538. }

539.

540.

541.}

542./////////////////////////////////////////////////////////////////////// //////////////////////////////////////

543.

544.bool if_match_num = false;

545.int match_num = -1;

546.

547.//模板匹配手

548.void hand_template_match(CvSeq* handT, CvSeq* hand){

549. int i=0;

550. int kind = -1;

551. double hu = 1;

552.

553. double hutmp;

554. CvSeq* handp = handT;

555. int method = CV_CONTOURS_MATCH_I1;

556.

557. match_num = 0;

558.

559. if(handT==NULL){return;printf("handT==NULL!\n");} 560. if(hand==NULL){return;printf("hand==NULL!\n");}

561.

562. for(i=0; i

563. hutmp = cvMatchShapes(handp, hand, method, 0); 564. handp = handp->h_next;

565.

566. //找到hu矩最小的模板

567. if(hu > hutmp){

568. hu = hutmp;

569. kind = i+1;

570. }

571.

572. //printf("%f ", hu);

573. }

574.

575. //显示匹配结果

576. if(hu<((double)Hmatch_value)/100){

577. printf("匹配模板: %d (%f)", kind, hu);

578. match_num = kind;

579. if_match_num = true;

580. }else{

581. if_match_num = false;

相关文档