Opencv 基于肤色的手势分割


 首先通过摄像头采集图像,用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 }

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM