#include "rockpaperscissors.h" #include <windows.h> #include "submit.h" void Ellipse(IplImage * sabunn_img); char windowNameCapture[] = "Capture"; // キャプチャした画像を表示するウィンドウの名前 char windowNameSkin[] = "Skin"; // 肌色抽出画像を表示するウィンドウの名前 char windowNameConvexHull[] = "ConvexHull"; // ConnvexHull画像を表示するウィンドウの名前 char windowNameTrack[] = "Track"; // トラックバーの表示 char windowNameFit[] = "Fit"; int hullarea; // ConvexHull内の面積 int handarea; // 手領域の面積 IplImage *frameImage = 0; //最低限、ウィンドウに表示するイメージ // モルフォロジー IplImage *dst_img_subunn = 0; // ellipseの処理用 IplImage *dst_img = 0; // 矩形の枠の個数を格納するもの int kukei_count = 0; // 指の個数をカウント int yubi_count = 0; // 画像を生成する IplImage *hsvImage; // HSV画像用IplImage IplImage *convexHullImage; // ConvexHull画像用IplImage IplImage *skinImage; // 肌色抽出画像用IplImage IplImage *temp; // 一時保存用IplImage IplImage *label; // ラベル結果保存用IplImage int count = 1; int hmax, hmin, smax, smin, vmax, vmin; //h:色相 s:色彩 v:明度 static int rockmax = 100, rockmin = 80, scissormax = 80, scissormin = 70, papermax = 70, papermin = 50; //グー最大、グー最小,チョキ最大、チョキ最小,パー最大、パー最小の並び char filename[1000]; char *path = "C:\\Documents and Settings\\Administrator\\My Documents\\My Pictures\\frame"; double ireko[10][2]; // // ウィンドウを作成する // void createWindows() { cvNamedWindow(windowNameCapture); cvNamedWindow(windowNameSkin); cvNamedWindow(windowNameConvexHull); cvNamedWindow(windowNameTrack); cvNamedWindow(windowNameFit); } // // 肌色を抽出する // // 引数: // frameImage : キャプチャした画像用IplImage // hsvImage : HSV画像用IplImage // skinImage : 肌色抽出画像用IplImage // void extractSkinColor(IplImage * frameImage, IplImage * hsvImage, IplImage * skinImage) { CvScalar color; // HSV表色系で表した色 unsigned char h; // H成分 unsigned char s; // S成分 unsigned char v; // V成分 // BGRからHSVに変換する cvCvtColor(frameImage, hsvImage, CV_BGR2HSV); //肌色抽出 for (int x = 0; x < skinImage->width; x++) { for (int y = 0; y < skinImage->height; y++) { color = cvGet2D(hsvImage, y, x); h = color.val[0]; s = color.val[1]; v = color.val[2]; if (h <= hmax && h >= hmin && s <= smax && s >= smin && v <= vmax && v >= vmin) { // 肌色の場合 cvSetReal2D(skinImage, y, x, 255); } else { cvSetReal2D(skinImage, y, x, 0); } } } } // // 欠損領域を補間する // // 引数: // skinImage : 肌色抽出画像用IplImage // temp : 一時保存用IplImage // void interpolate(IplImage * skinImage, IplImage * temp) { //膨張をITERATIONS回行う cvDilate(skinImage, temp, NULL, ITERATIONS); //収縮をITERATIONS回行う cvErode(temp, skinImage, NULL, ITERATIONS); } // // 最大領域(手領域)の抽出を行う // // 引数: // skinImage : 肌色抽出画像用IplImage // label : ラベリングした結果 // convexHullImage : ConvexHull画像用IplImage // // 戻り値: // 手領域の面積 // int pickupMaxArea(IplImage * skinImage, IplImage * label, IplImage * convexHullImage) { int handarea = 0; // 手領域の面積 for (int x = 0; x < skinImage->width; x++) { for (int y = skinImage->height - 1; y >= 0; y--) { if (cvGetReal2D(label, y, x) == 1) { // 最大領域だった場合 handarea++; cvSet2D(convexHullImage, y, x, CV_RGB(255, 255, 255)); } else { cvSetReal2D(skinImage, y, x, 0); cvSet2D(convexHullImage, y, x, CV_RGB(0, 0, 0)); } } } return handarea; } // // ConvexHullを生成する // // 引数: // skinImage : 肌色抽出画像用IplImage // handarea : 手領域の面積(点の数) // handpoint : 手領域内の点の座標配列へのポインタ // hull : ConvexHullの頂点のhandpointにおけるindex番号へのポインタ // pointMatrix : 手領域用行列へのポインタ // hullMatrix : ConvexHull用行列へのポインタ // void createConvexHull(IplImage * skinImage, int handarea, CvPoint ** handpoint, int **hull, CvMat * pointMatrix, CvMat * hullMatrix) { int i = 0; // ConvexHullを計算するために必要な行列を生成する *handpoint = (CvPoint *) malloc(sizeof(CvPoint) * handarea); *hull = (int *) malloc(sizeof(int) * handarea); *pointMatrix = cvMat(1, handarea, CV_32SC2, *handpoint); *hullMatrix = cvMat(1, handarea, CV_32SC1, *hull); for (int x = 0; x < skinImage->width; x++) { for (int y = 0; y < skinImage->height; y++) { if (cvGetReal2D(skinImage, y, x) == 255) { (*handpoint)[i].x = x; (*handpoint)[i].y = y; i++; } } } // ConvexHullを生成する cvConvexHull2(pointMatrix, hullMatrix, CV_CLOCKWISE, 0); } // // ConvexHullを描画する // // 引数: // convexHullImage : ConvexHull画像用IplImage // handpoint : 手領域内の点の座標配列 // hull : ConvexHullの頂点のhandpointにおけるindex番号 // hullcount : ConvexHullの頂点の数 // void drawConvexHull(IplImage * convexHullImage, CvPoint * handpoint, int *hull, int hullcount) { CvPoint pt0 = handpoint[hull[hullcount - 1]]; for (int i = 0; i < hullcount; i++) { CvPoint pt = handpoint[hull[i]]; cvLine(convexHullImage, pt0, pt, CV_RGB(0, 255, 0)); pt0 = pt; } } // // ConvexHull内の面積を求める // // 引数: // convexHullImage : ConvexHull画像用IplImage // handpoint : 手領域内の点の座標配列 // hull : ConvexHullの頂点のhandpointにおけるindex番号 // hullcount : ConvexHullの頂点の数 // // 戻り値: // ConvexHull内の面積 // int calcConvexHullArea(IplImage * convexHullImage, CvPoint * handpoint, int *hull, int hullcount) { // ConvexHullの頂点からなる行列を生成 CvPoint *hullpoint = (CvPoint *) malloc(sizeof(CvPoint) * hullcount); CvMat hMatrix = cvMat(1, hullcount, CV_32SC2, hullpoint); for (int i = 0; i < hullcount; i++) { hullpoint[i] = handpoint[hull[i]]; } // ConvexHull内の点の数を数える int hullarea = 0; for (int x = 0; x < convexHullImage->width; x++) { for (int y = 0; y < convexHullImage->height; y++) { if (cvPointPolygonTest(&hMatrix, cvPoint2D32f(x, y), 0) > 0) { hullarea++; if (count == 1) cvCircle(frameImage, cvPoint(x, y), 1, cvScalar(0, 0, 0), -1); } } } count = 0; free(hullpoint); return hullarea; } // // ジャンケンの判定を行う // // 引数: // handarea : 手領域の面積 // hullarea : ConvexHull内の面積 // void decide(int handarea, int hullarea) { double ratio; // ConvexHull内の面積に対する手領域の面積の割合 // 送信用変数 char goo='g'; char tyoki='t'; char par='p'; char null='n'; // 割合判定用変数 float rockmax1, rockmin1, scissormax1, scissormin1, papermax1, papermin1; // 指の距離用変数 int mater = 0; rockmax1 = rockmax / 100.0; rockmin1 = rockmin / 100.0; scissormax1 = scissormax / 100.0; scissormin1 = scissormin / 100.0; papermax1 = papermax / 100.0; papermin1 = papermin / 100.0; //printf("rockmax1 = %lf\n", rockmax1); //printf("rockmin1 = %lf\n", rockmin1); //printf("scissormax1 = %lf\n", scissormax1); //printf("scissormin1 = %lf\n", scissormin1); //printf("papermax1 = %lf\n", papermax1); //printf("papermin1 = %lf\n", papermin1); // 割合計算 ratio = handarea / (double) hullarea; // 指距離計算 if(yubi_count==2){ mater=(int)((ireko[yubi_count-2][0] - ireko[yubi_count-1][0]) + (ireko[yubi_count-2][1] - ireko[yubi_count-1][1])); if(mater<0) mater = mater * (-1); } //printf("Ratio = %lf", ratio); //printf("hullarea:%d→ ",hullarea); //printf("handarea:%d→ ",handarea); //printf("Ratio : %lf→", ratio); //printf("yubi_count:%d",yubi_count); printf("--------------------------------------------------------------------------------\n"); printf("面積の判定\n"); printf("if((%d <= 190000 && %d >= 19000) && (%d >= 18000 && %d <= 110000))\n",hullarea,hullarea,handarea,handarea); if((hullarea <= 190000 && hullarea >= 19000) && (handarea >= 18000 && handarea <= 110000)){ // グーの判定 if ((ratio >= rockmin1 && ratio <= rockmax1) && yubi_count == 0 ){ printf("グー\n"); } // チョキの判定 }else if ( yubi_count == 2 ) { printf("チョキの面積\n"); printf("if((%d >= 21000 && %d <= 110000) && (%d >= 15000 && %d <= 75000))\n",hullarea,hullarea,handarea,handarea); if((hullarea >= 21000 && hullarea <= 110000) && (handarea >= 15000 && handarea <= 75000)){ printf("チョキの白黒の割合\n"); printf("if(%f >= %f && %f <= %f)\n",ratio,scissormin1,ratio,scissormax1); if(ratio >= scissormin1 && ratio <= scissormax1){ printf("指の距離判定\n"); printf("if(%d<100.0)\n",mater); if(mater<130){ printf("チョキ\n"); } }else printf("数値がおかしい\n"); }else printf("数値がおかしい\n"); // パーの判定 }else if ( yubi_count == 5 ) { printf("パーの面積\n"); printf("if((%d >= 43000 && %d <= 180000) && (%d >= 28000 && %d <= 100000))\n",hullarea,hullarea,handarea,handarea); if((hullarea >= 43000 && hullarea <= 180000) && (handarea >= 28000 && handarea <= 100000)){ printf("パーの白黒の割合\n"); printf("if(%f >= %f && %f <= %f)\n",ratio,papermin1,ratio,papermax1); if(ratio >= papermin1 && ratio <= papermax1){ printf("パー\n"); }else printf("数値がおかしい\n"); }else printf("数値がおかしい\n"); } else { printf("数値がおかしい\n"); } } if(hullarea >= 190000){ printf("もう少し離してください\n"); }else if(hullarea <= 19000){ printf("もう少し近づけてください\n"); } } void sabun(IplImage * src_img) { // モロフォロジー演算用 IplImage *dst_img_dilate; IplImage *dst_img_erode, *dst_img_erode1; IplImage *dst_img_opening; IplImage *tmp_img; IplConvKernel *element; dst_img_dilate = cvCloneImage(src_img); //拡大 dst_img_erode = cvCloneImage(src_img); //縮小 //dst_img_erode1 = cvCloneImage(src_img); dst_img_opening = cvCloneImage(src_img); //差分 dst_img_subunn = cvCloneImage(src_img); tmp_img = cvCloneImage(src_img); //構造要素を生成する element = cvCreateStructuringElementEx(9, 9, 4, 4, CV_SHAPE_RECT, NULL); //各種のモルフォロジー演算を実行する cvMorphologyEx(src_img, dst_img_opening, tmp_img, element, CV_MOP_OPEN, 1); //cvMorphologyEx(src_img, dst_img_subunn , tmp_img, element, CV_MOP_OPEN, 1); cvErode(dst_img_opening, dst_img_erode, element, 5); //縮小を5回繰り返す cvDilate(dst_img_erode, dst_img_dilate, element, 8); //拡大を8回繰り返す cvSub(dst_img_opening, dst_img_dilate, dst_img_subunn); //差分を求める // メモリを解放する cvReleaseImage(&dst_img_dilate); cvReleaseImage(&dst_img_erode); cvReleaseImage(&dst_img_opening); Ellipse(dst_img_subunn); cvReleaseImage(&dst_img_subunn); cvReleaseImage(&tmp_img); } void Ellipse(IplImage * sabunn_img) { // ellipseの処理用 IplImage *dst_img; IplImage *src_img_gray = 0; IplImage *tmp_img; CvMemStorage *storage = cvCreateMemStorage(0); CvSeq *contours = 0; CvBox2D ellipse; CvTreeNodeIterator it; CvPoint2D32f pt[4]; src_img_gray = cvCloneImage(sabunn_img); tmp_img = cvCreateImage(cvGetSize(sabunn_img), IPL_DEPTH_8U, 1); dst_img = cvCloneImage(sabunn_img); // (2)二値化と輪郭の検出 cvThreshold(src_img_gray, tmp_img, 95, 255, CV_THRESH_BINARY); cvReleaseImage(&src_img_gray); cvFindContours(tmp_img, storage, &contours, sizeof(CvContour), CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0)); if (contours == NULL) printf("\n"); else{ // (3)ツリーノードイテレータの初期化 cvInitTreeNodeIterator(&it, contours, 3); while ((contours = (CvSeq *) cvNextTreeNode(&it)) != NULL) { if (contours->total > 6) { // (4)楕円のフィッティング ellipse = cvFitEllipse2(contours); ellipse.angle = 90.0 - ellipse.angle; // (5)輪郭,楕円,包含矩形の描画 if (ellipse.size.width > ellipse.size.height && ellipse.size.height * 2 <= ellipse.size.width || ellipse.size.width < ellipse.size.height && ellipse.size.height >= ellipse.size.width * 2) { if (ellipse.size.width * ellipse.size.height > 2000) { ireko[yubi_count][0] = ellipse.center.x; ireko[yubi_count][1] = ellipse.center.y; yubi_count++; printf("中心:%f %f\n",ellipse.center.x, ellipse.center.y); cvDrawContours(dst_img, contours, CV_RGB(255, 0, 0), CV_RGB(255, 0, 0), 0, 1, CV_AA, cvPoint(0, 0)); cvEllipseBox(dst_img, ellipse, CV_RGB(0, 0, 255), 2); cvBoxPoints(ellipse, pt); cvLine(dst_img, cvPointFrom32f(pt[0]), cvPointFrom32f(pt[1]), CV_RGB(0, 255, 0)); cvLine(dst_img, cvPointFrom32f(pt[1]), cvPointFrom32f(pt[2]), CV_RGB(0, 255, 0)); cvLine(dst_img, cvPointFrom32f(pt[2]), cvPointFrom32f(pt[3]), CV_RGB(0, 255, 0)); cvLine(dst_img, cvPointFrom32f(pt[3]), cvPointFrom32f(pt[0]), CV_RGB(0, 255, 0)); } } } } } if (dst_img->origin == 0) { // 左上が原点の場合 cvFlip(dst_img, dst_img, 0); } // 画像を表示させる cvShowImage(windowNameFit, dst_img); // メモリを解放する cvReleaseImage(&dst_img); cvReleaseMemStorage(&storage); cvReleaseImage(&tmp_img); } void Track(void) { // 肌色を抽出する extractSkinColor(frameImage, hsvImage, skinImage); // 欠損領域を補間する interpolate(skinImage, temp); // ラベリングを行う Label *labeling = createLabeling(); exec(labeling, skinImage, label, true, IGNORE_SIZE); if (getNumOfResultRegions(labeling) > 0) { // IGNORE_SIZEよりも大きな領域があった場合 int hullcount; // ConvexHullの頂点の数 CvPoint *handpoint; // 手領域内の点の座標配列 int *hull; // ConvexHullの頂点のhandpointにおけるindex番号 CvMat pointMatrix; // 手領域用行列 CvMat hullMatrix; // ConvexHull用行列 // 最大領域(手領域)の抽出を行う handarea = pickupMaxArea(skinImage, label, convexHullImage); // ConvexHullを生成する createConvexHull(skinImage, handarea, &handpoint, &hull, &pointMatrix, &hullMatrix); hullcount = hullMatrix.cols; // ConvexHullを描画する drawConvexHull(convexHullImage, handpoint, hull, hullcount); // ConvexHull内の面積を求める hullarea = calcConvexHullArea(convexHullImage, handpoint, hull, hullcount); // 差分を求める sabun(skinImage); // ジャンケンの判定を行う decide(handarea, hullarea); // メモリを解放する free(handpoint); free(hull); } else { // 画像を初期化する cvSetZero(convexHullImage); } releaseLabeling(labeling); if (skinImage->origin == 0) { // 左上が原点の場合 cvFlip(skinImage, skinImage, 0); } if (convexHullImage->origin == 0) { // 左上が原点の場合 cvFlip(convexHullImage, convexHullImage, 0); } } void on_change(int pos) { Track(); } int main(int argc, char **argv) { int key; // キー入力用の変数 CvCapture *capture = NULL; // カメラキャプチャ用の構造体 // カメラを初期化する if ((capture = cvCreateCameraCapture(-1)) == NULL) { // カメラが見つからなかった場合 printf("カメラが見つかりません\n"); return -1; } frameImage = cvQueryFrame(capture); // キャプチャ画像用IplImage hsvImage = cvCreateImage(cvGetSize(frameImage), IPL_DEPTH_8U, 3); convexHullImage = cvCreateImage(cvGetSize(frameImage), IPL_DEPTH_8U, 3); skinImage = cvCreateImage(cvGetSize(frameImage), IPL_DEPTH_8U, 1); temp = cvCreateImage(cvGetSize(frameImage), IPL_DEPTH_8U, 1); label = cvCreateImage(cvGetSize(frameImage), IPL_DEPTH_16S, 1); // ウィンドウを生成する createWindows(); // トラックバーを生成 cvCreateTrackbar("色相最大値", "Track", &hmax, 255, on_change); cvSetTrackbarPos("色相最大値", "Track", 26); cvCreateTrackbar("色相最小値", "Track", &hmin, 255, on_change); cvSetTrackbarPos("色相最小値", "Track", 1); cvCreateTrackbar("色彩最大値", "Track", &smax, 255, on_change); cvSetTrackbarPos("色彩最大値", "Track", 255); cvCreateTrackbar("色彩最小値", "Track", &smin, 255, on_change); cvSetTrackbarPos("色彩最小値", "Track", 51); cvCreateTrackbar("明度最大値", "Track", &vmax, 255, on_change); cvSetTrackbarPos("明度最大値", "Track", 255); cvCreateTrackbar("明度最小値", "Track", &vmin, 255, on_change); cvSetTrackbarPos("明度最小値", "Track", 0); // ジャンケン判定感度を生成 cvCreateTrackbar("グー最大値", "Track", &rockmax, 100, on_change); cvSetTrackbarPos("グー最大値", "Track", 100); cvCreateTrackbar("グー最小値", "Track", &rockmin, 100, on_change); cvSetTrackbarPos("グー最小値", "Track", 85); cvCreateTrackbar("チョキ最大値", "Track", &scissormax, 100, on_change); cvSetTrackbarPos("チョキ最大値", "Track", 85); cvCreateTrackbar("チョキ最小値", "Track", &scissormin, 100, on_change); cvSetTrackbarPos("チョキ最小値", "Track", 60); cvCreateTrackbar("パー最大値", "Track", &papermax, 100, on_change); cvSetTrackbarPos("パー最大値", "Track", 75); cvCreateTrackbar("パー最小値", "Track", &papermin, 100, on_change); cvSetTrackbarPos("パー最小値", "Track", 50); while (1) { frameImage = cvQueryFrame(capture); on_change(hmax); //on_change1(rockmax); // 画像を表示する cvShowImage(windowNameCapture, frameImage); cvShowImage(windowNameSkin, skinImage); cvShowImage(windowNameConvexHull, convexHullImage); yubi_count = 0; // キー入力判定 key = cvWaitKey(1); if (key == 'q') { // 'q'キーが押されたらループを抜ける break; } else if (key == 'c') { printf("撮影します\n"); sprintf(filename, "%s%d.bmp", path, count); cvSaveImage(filename, skinImage); printf("撮影しました\n"); count++; } } // キャプチャを解放する cvReleaseCapture(&capture); // メモリを解放する cvReleaseImage(&hsvImage); cvReleaseImage(&skinImage); cvReleaseImage(&temp); cvReleaseImage(&label); cvReleaseImage(&convexHullImage); cvReleaseImage(&dst_img_subunn); cvReleaseImage(&dst_img); // ウィンドウを破棄する cvDestroyWindow(windowNameCapture); cvDestroyWindow(windowNameSkin); cvDestroyWindow(windowNameConvexHull); cvDestroyWindow(windowNameFit); return 0; }