#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;
}
最終更新:2010年02月08日 08:21