首先通过摄像头采集图像,用Otsu方法进行二值化处理,然后找出最大两个连通区域,此处默认有手和脸,最后通过指尖检测算法,将脸部排除。
1 #include "cxcore.h"
2 #include "math.h"
3 #include <cmath>
4 #include <vector>
5 #include <stdio.h>
6 #include <string.h>
7 #include <sstream>
8 #include <time.h>
9 #include <iostream>
10 #include <cstring>
11 #include <cv.h>
12 #include <highgui.h>
13 #include <assert.h>
14
15 using namespace std;
16 using namespace cv;
17
18 void cvThresholdOtsu(IplImage* src,IplImage* dst)//otsu 最大类间差分法,一种自适应阈值确定方法
19 {
20 int height = src->height;
21 int width = src->width;
22
23 float histogram[256] = {0};
24 for (int i = 0; i < height; i++)
25 {
26 unsigned char* p = (unsigned char*)src->imageData + src->widthStep*i;
27 for (int j = 0; j < width; j++)
28 {
29 histogram[*p++]++;
30 }
31 }
32 int size = height * width;
33 for (int i = 0; i < 256; i++)
34 {
35 histogram[i] = histogram[i] / size;
36 }
37 float avgValue = 0;
38 for (int i = 0; i < 256; i++)
39 {
40 avgValue += i*histogram[i];
41 }
42
43 int threshold;
44 float maxVariance = 0;
45 float w = 0, u = 0;
46 for (int i = 0; i < 256; i++)
47 {
48 w += histogram[i];
49 u += i*histogram[i];
50 float t = avgValue*w - u;
51 float variance = t*t / (w*(1-w));
52 if (variance > maxVariance)
53 {
54 maxVariance = variance;
55 threshold = i;
56 }
57 }
58 cvThreshold(src,dst,threshold,255,CV_THRESH_BINARY);
59 }
60
61 void cvSkinOtsu(IplImage* src,IplImage* dst)
62 {
63 assert(dst->nChannels == 1 && src->nChannels == 3);
64
65 IplImage* ycrcb = cvCreateImage(cvGetSize(src),8,3);
66 IplImage* cr = cvCreateImage(cvGetSize(src),8,1);
67 cvCvtColor(src,ycrcb,CV_BGR2YCrCb);
68 cvSplit(ycrcb,0,cr,0,0);
69 cvThresholdOtsu(cr,cr);
70 cvCopyImage(cr,dst);
71 cvReleaseImage(&cr);
72 cvReleaseImage(&ycrcb);
73 }
74
75 // 计算两点(p1,p2) 和 (p3,p4) 之间的距离
76 double distance(double p1,double p2,double p3,double p4)
77 {
78 double a = (p1 - p3)*(p1 - p3) + (p2 - p4)*(p2 - p4);
79 double b = sqrt(a);
80 return b;
81 }
82
83 //主函数
84 int main()
85 {
86
87 CvCapture* capture = cvCaptureFromCAM(0);//从对摄像头的初始化捕获
88 if(!cvQueryFrame(capture)) cout<<"Video capture failed, please check the camera."<<endl;
89 else cout<<"Video camera capture status: OK"<<endl;
90 CvSize sz = cvGetSize(cvQueryFrame( capture));//得到摄像头图像大小
91
92 IplImage* src = cvCreateImage( sz, 8, 3 );//3通道,每个通道8位
93 IplImage* dst_crotsu = cvCreateImage(sz, 8, 1);
94 IplImage* dst_MaxSecond = cvCreateImage(sz, 8, 1);
95 IplImage* dst_hand = cvCreateImage(sz, 8, 3);
96 CvMemStorage* storage = cvCreateMemStorage(0);//分配大小为0的内存空间
97 CvMemStorage* storageHand = cvCreateMemStorage(0);
98 CvMemStorage* dftStorage = cvCreateMemStorage(0);
99 CvMemStorage* minStorage = cvCreateMemStorage(0);
100
101 CvSeq* contour = 0;//用于选取最大两区域
102 CvSeq* sq = 0;//用于选取手
103
104 cvNamedWindow("source", CV_WINDOW_AUTOSIZE);//创建用于显示的窗口
105 cvNamedWindow("cvSkinOtsu", CV_WINDOW_AUTOSIZE);
106 cvNamedWindow("cvHandFace", CV_WINDOW_AUTOSIZE);
107 cvNamedWindow("Hand", CV_WINDOW_AUTOSIZE);
108 cvNamedWindow("bg", CV_WINDOW_AUTOSIZE);
109
110 // 以下两行是为了计算图形的重心做准备
111 CvMoments moments;
112 CvMat* region;
113 //定义一些点和具体的参数
114 CvPoint pt1,pt2,ptmax;
115 double m00 = 0,m10,m01,p1x,p1y,p2x,p2y,max,sum,average;
116 int n = 0,Nc;
117
118 // src = cvQueryFrame(capture);
119 // cvShowImage("source", src);
120 // cvSaveImage("test.img", src);
121 // cout<<"hejhd"<<endl;
122 while(1)
123 {
124 IplImage* bg = cvCreateImage( sz, 8, 3);//
125 cvRectangle( bg, cvPoint(0,0), cvPoint(bg->width,bg->height), CV_RGB( 255, 255, 255), -1, 8, 0 );//画矩形,参数:Image,两个顶点坐标,线的颜色,线的厚度(CV_FILLED时绘制填充了色彩的矩形),线条类型,坐标点的小数点位数
126 bg->origin = 0;//表示坐标系统的原点,0表示左上,1表示左下
127 for(int b = 0; b< int(bg->width/10); b++)//画网格
128 {
129 cvLine( bg, cvPoint(b*20, 0), cvPoint(b*20, bg->height), CV_RGB( 200, 200, 200), 1, 8, 0 );//画竖线
130 //cvLine(图像,线段的第一个端点,第二个端点,颜色,粗细,类型,坐标点的小数点位数)
131 cvLine( bg, cvPoint(0, b*20), cvPoint(bg->width, b*20), CV_RGB( 200, 200, 200), 1, 8, 0 );//画横线
132 }
133 cvShowImage("bg", bg);
134 src = cvQueryFrame(capture);//得到一帧图像
135 cvSaveImage("src.jpg", src);
136 cvShowImage("source", src);
137 // cvWaitKey(100);
138 cvZero(dst_crotsu);
139 cvSkinOtsu(src, dst_crotsu);//得到二值化图像
140
141 // cvShowImage("cvSkinOtsu", dst_crotsu);
142 cvSaveImage("skinOtsu.jpg", dst_crotsu);
143
144 cvThreshold(dst_crotsu, dst_crotsu, 127, 255, CV_THRESH_BINARY);
145
146 int contour_num = cvFindContours(dst_crotsu, storage, &contour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);//得到最大两轮廓
147 double maxarea = 0;//
148 double secondarea = 0;
149 double minarea = 100;
150 cvZero(dst_MaxSecond);
151 CvSeq* _contour = contour;
152 int m = 0;
153 for(;contour != 0; contour = contour->h_next)
154 {
155 m++;
156 double tmparea = fabs(cvContourArea(contour));
157 if(tmparea < minarea) {cvSeqRemove(contour, 0); continue;}//删除噪声
158 if(tmparea > maxarea) {secondarea = maxarea; maxarea = tmparea;}//得到最大面积
159 else if(tmparea > secondarea) {secondarea = tmparea;}//得到第二大面积
160 }
161
162 contour = _contour;
163
164 for(; contour != 0; contour = contour->h_next)//画出最大两区域
165 {
166 double tmparea = fabs(cvContourArea(contour));
167 if (tmparea == maxarea)
168 {
169 CvScalar color = CV_RGB(255, 255, 255);
170 cvDrawContours(dst_MaxSecond, contour, color, color, 0, CV_FILLED);
171 }
172 else if (tmparea == secondarea)
173 {
174 CvScalar color = CV_RGB(255, 255, 255);
175 cvDrawContours(dst_MaxSecond, contour, color, color, 0, CV_FILLED);
176 }
177 }
178
179 cvShowImage("cvHandFace", dst_MaxSecond);
180 cvSaveImage("handface.jpg", dst_MaxSecond);
181
182 // cvWaitKey(100);
183
184 //选取手区域
185 cvZero(dst_hand);
186 cvThreshold(dst_MaxSecond, dst_MaxSecond,127,255,CV_THRESH_BINARY);
187 Nc = cvFindContours(dst_MaxSecond, storageHand, &sq, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
188 for (; sq != NULL; sq = sq->h_next)
189 {
190 max = 0;
191 sum = 0;
192 average = 0;
193 vector<CvPoint> pt_vec;
194 vector<CvPoint>::iterator piter;
195 n++;
196
197 CvSeq* csq = cvApproxPoly(sq, sizeof(CvContour), storageHand, CV_POLY_APPROX_DP, 25, 0);// 相似多边形逼近该轮廓,以更好地求重心坐标
198 region = (CvMat*)csq;
199 // 将保存多边形的矩阵region的重心计算出来并保存在moments中
200 cvMoments(region,&moments,0);
201 m00 = moments.m00; // 总重
202 m10 = moments.m10; // x轴重
203 m01 = moments.m01; // y轴重
204 double inv_m00 = 1. / m00; // 总重的倒数
205 pt1.x = cvRound(m10 * inv_m00); // 重心的横坐标
206 p2x = pt1.x * 1.0; // 用p2x表示重心的横坐标
207 pt1.y = cvRound(m01 * inv_m00); // 重心的纵坐标
208 p2y = pt1.y * 1.0; // 用p2y来表示重心的纵坐标,以方便计算距离
209
210 cout << "contour #" << n << ":" << endl; // 打印当前的轮廓
211 // 打印当前轮廓的重心
212 cout << "重心: " << "(" << p2x << "," << p2y << ")" << endl;
213 // 打印当前轮廓包含像素点的总数
214 cout << "sq->total = " << sq->total << endl;
215
216 //将当前轮廓中的每一个点保存在链表 pt_vec中
217 for (int i = 0; i < sq->total; ++i)
218 {
219 CvPoint* p = CV_GET_SEQ_ELEM(CvPoint,sq,i);
220 pt_vec.push_back(*p);
221 }
222 // 将链表的迭代器指向链表中的第一个点上
223 piter = pt_vec.begin();
224 //循环进行轮廓上每一个点的操作
225 for (; piter < pt_vec.end();++piter)
226 {
227 pt2 = *piter; // 将当前迭代器所指向的点保存在pt2中
228 p1x = pt2.x * 1.0; // 获取当前点的横坐标
229 p1y = pt2.y * 1.0; // 获取当前点的纵坐标
230 double d = distance(p1x,p1y,p2x,p2y); // 求当前点到重心的距离
231 sum += d; // 每次将距离都加在sum,以便求总距离
232
233 //求最大距离所对应的点,把最大距离保存在max中,所对应的点 保存在 ptmax中
234 if (d > max)
235 {
236 max = d;
237 ptmax = pt2;
238 }
239 }
240
241 //求轮廓所有的点到重心的平均距离
242 average = sum / (sq->total * 1.0);
243 double ab = max / average; // ab 就是那个倍数
244 cout << "distanceMax = " << max << endl; // 打印最大距离
245 cout << "distanceAverage = " << average << endl; // 打印平均距离
246 cout << "ab = " << ab << endl; // 打印那个倍数
247 if (ab < 1.5) // 如果该倍数小于1.6,则舍弃该轮廓 改成1.5倍
248 {
249 cout << "remove #" << n << endl; // 打印删除的是哪个轮廓
250 cvSeqRemove(sq,0); // 删除该轮廓
251 continue;
252 }
253
254 //将保留下的轮廓填充颜色显示
255 CvScalar color = CV_RGB(255,255,255);
256 cvDrawContours(dst_hand,sq,color,color,-1,-1,8);
257
258 //在bg图上确定手的区域
259 CvRect rect = cvBoundingRect( sq, 0 );//返回一个2d矩形的点集合
260 cvRectangle( bg, cvPoint(rect.x, rect.y + rect.height), cvPoint(rect.x + rect.width, rect.y), CV_RGB(200, 0, 200), 1, 8, 0 );
261
262 cvDrawContours(bg,sq,CV_RGB(127,0,0),CV_RGB(127,0,0),-1,-1,8);
263 cvShowImage("bg",bg);
264
267 }
268
269
270 cvShowImage("Hand", dst_hand);
271 cvSaveImage("hand.jpg", dst_hand);
272
273 cvWaitKey(100);
274
275 }
276
277 cvReleaseCapture( &capture);
278 cvDestroyAllWindows();
279 }