首先通過攝像頭采集圖像,用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 }
