Skip to content

Instantly share code, notes, and snippets.

@peccu
Created July 9, 2010 07:39
Show Gist options
  • Select an option

  • Save peccu/469197 to your computer and use it in GitHub Desktop.

Select an option

Save peccu/469197 to your computer and use it in GitHub Desktop.
#include "cv.h"
#include "highgui.h"
#include <ctype.h>
#include <stdio.h>
#define MAIN_WINDOW "Main Window"
#define MAIN_WINDOW_WIDTH 1024
#define MAIN_WINDOW_HEIGHT 768
#define GRAYCODE_NUM 32 //投影するグレイコード画像の枚数
#define GRAYCODE_FILE "graycode/GraycodeImage%d.png" //グレイコード画像ファイル
#define FIELD_SIZE 320
#define PROJECTION_WIDTH 320 //投影画像サイズ
#define PROJECTION_HEIGHT 240
#define ARRAY_SIZE 4 //クリックする格子点の数
#define Z_SCALE 1
#define WAIT_FRAME 10 //投影-撮影間の読み飛ばしフレーム数
#define WHITE_COLOR 255 //2値化時の白の値
#define BLACK_COLOR 0 //2値化時の黒の値
#define THRESHOLD 15 //2値化時にパターン投影外と判断する閾値
int window_width = 640; // ウインドウ幅
int window_height = 480; // ウインドウ高さ
//テーブル上の格子座標
double table_field[ARRAY_SIZE][2]=
{
{0,0},{FIELD_SIZE,0},{0,FIELD_SIZE},{FIELD_SIZE,FIELD_SIZE}
};
//カメラ上の格子点の座標
double camera_field[ARRAY_SIZE][2];
double projector_field[ARRAY_SIZE][2];
CvMat *Hct; //テーブル <-> カメラ間のホモグラフィ行列
CvMat *Hpt; //テーブル <^> プロジェクタ間のホモグラフィ行列
bool TOP_VIEW_MODE = false;
bool CAMERA_VIEW_MODE=true;
bool PROJECTOR_VIEW_MODE=false;
bool CAMERA_CALIBRATION_MODE=false;
bool PROJECTOR_CALIBRATION_MODE=false;
bool GAME_MODE=false;
bool Inverse = false;
int AI_PLAYER = 0;
bool AI_MODE = false;
CvCapture* capture = 0;
IplImage *frame; // OpenCVからのキャプチャ画像
IplImage *binarized_img; // 二値化画像
IplImage *gray_img; // グレースケール画像
IplImage *top_img; // フィールドを上からみた画像=机の画像
IplImage *projection_img; // プロジェクタに投影する画像
IplImage **graycode_img; //投影用コード画像
IplImage **captured_code_img; //コード投影の撮影結果
IplImage *coded_imgX; //コード値X
IplImage *coded_imgY; //コード値X
IplImage *sample_img; //テスト用画像
IplImage *field_img; //投影用フィールド
int click_count =0;
int graycode_count =0;
int wait_count = 0;
int capture_width, capture_height;
//game用パラメータ
#define BOARD_THRESHOLD 50 //盤面判定の時の2値化の閾値
#define STABLE_COUNT 30 //手が確定する判定時間
#define GRID_SIZE 105 //1マスの大きさ[pix]
#define PLAYER_1 1
#define PLAYER_2 -1
#define BLANK 0 //打たれていない場所
#define MOMENT_TH 800 //石が置かれたかの判定閾値
IplImage *initial_field; //fieldの初期画像用
int game_state = 0; //手番
double pre_cx=0;
double pre_cy=0;
double stable_count=0;
int board[3][3];
bool board_update = false;
bool AI_turn=false;
bool SEARCH_MODE=false;
int available_player=PLAYER_1;
int winner = BLANK;
////////////////////幾何変換変換用ルーチン/////////////////////////////////////////////
//グレイコード値を整数に変換
unsigned int Gray_to_num(unsigned int x)
{
unsigned int ret =x;
while(x >>= 1)
ret ^= x;
return ret;
}//first <-> second間のホモグラフィ計算
void
FindHomography(double first[ARRAY_SIZE][2], double second[ARRAY_SIZE][2], CvMat** Homography)
{
CvPoint2D32f src_pnt[ARRAY_SIZE], dst_pnt[ARRAY_SIZE];
for(int i=0;i<ARRAY_SIZE;i++){
src_pnt[i] = cvPoint2D32f ( (float)first[i][0], (float)first[i][1]);
dst_pnt[i] = cvPoint2D32f ( (float)second[i][0], (float)second[i][1]);
}
if(!Homography){
cvReleaseMat(Homography);
}
*Homography = cvCreateMat(3,3,CV_32FC1);
cvGetPerspectiveTransform(src_pnt,dst_pnt,*Homography);
CV_MAT_ELEM(**Homography,float,2,2)=Z_SCALE;
for(int y = 0; y < 3;++y){
for(int x = 0; x < 3;++x)
{
printf( "%f ",CV_MAT_ELEM(**Homography,float,y,x));
}
printf("\n");
}
//reprojectionの確認用
for(int i=0;i<ARRAY_SIZE;i++){
double X=(double)first[i][0];
double Y=(double)first[i][1];
double Z=1;
double x = X*CV_MAT_ELEM(**Homography,float,0,0)+Y*CV_MAT_ELEM(**Homography,float,0,1)+Z*CV_MAT_ELEM(**Homography,float,0,2);
double y = X*CV_MAT_ELEM(**Homography,float,1,0)+Y*CV_MAT_ELEM(**Homography,float,1,1)+Z*CV_MAT_ELEM(**Homography,float,1,2);
double z = X*CV_MAT_ELEM(**Homography,float,2,0)+Y*CV_MAT_ELEM(**Homography,float,2,1)+Z*CV_MAT_ELEM(**Homography,float,2,2);
printf("%lf, %lf %lf = %lf, %lf %lf \n", x/z, y/z, z, X, Y, Z);
}
}
void
ProjectorCalibration()
{
//まず、二値化画像の作成
IplImage **binarized_img;
IplImage *posi_img;
IplImage *nega_img;
binarized_img = (IplImage **)malloc(sizeof(IplImage *)*(GRAYCODE_NUM/2));
posi_img = cvCreateImage(cvSize(capture_width,capture_height),IPL_DEPTH_8U,1);
nega_img = cvCreateImage(cvSize(capture_width,capture_height),IPL_DEPTH_8U,1);
for(int i=0;i<GRAYCODE_NUM;i+=2){
//まずは画像領域の確保
binarized_img[i/2] = cvCreateImage(cvSize(capture_width,capture_height),IPL_DEPTH_8U,1);
//入力画像をグレースケールに変換
cvCvtColor(captured_code_img[i],posi_img,CV_RGB2GRAY);
cvCvtColor(captured_code_img[i+1],nega_img,CV_RGB2GRAY);
//ネガポジ画像による2値化
fprintf(stderr, "code No. %d \n",i);
for(int y=0;y<capture_height;y++){
for(int x=0;x<capture_width;x++){
if( abs(CV_IMAGE_ELEM(posi_img, uchar, y, x) - CV_IMAGE_ELEM(nega_img, uchar, y, x))<THRESHOLD){
CV_IMAGE_ELEM(binarized_img[i/2], uchar, y,x) = (unsigned char) BLACK_COLOR;
}
else if ( CV_IMAGE_ELEM(posi_img, uchar, y, x) >= CV_IMAGE_ELEM(nega_img, uchar, y, x)){
CV_IMAGE_ELEM(binarized_img[i/2], uchar, y,x) = (unsigned char) WHITE_COLOR;
}
else{
CV_IMAGE_ELEM(binarized_img[i/2], uchar, y,x) = (unsigned char) BLACK_COLOR;
}
}
}
}
//コード画像の作成
int bit_num = GRAYCODE_NUM/4;
coded_imgX = cvCreateImage(cvSize(capture_width,capture_height),IPL_DEPTH_8U,1);
coded_imgY = cvCreateImage(cvSize(capture_width,capture_height),IPL_DEPTH_8U,1);
for(int y=0;y<capture_height;y++){
for(int x=0;x<capture_width;x++){
unsigned int val = 0;
for(int i=0;i<bit_num;i++){
if( CV_IMAGE_ELEM(binarized_img[i],uchar,y,x)==WHITE_COLOR)
val+= (int)(pow(2.0 ,i));
}
//グレイコードを2進数に変換.
if(val>0){
if(Inverse){
CV_IMAGE_ELEM(coded_imgX, uchar, y,x) = (unsigned char )(WHITE_COLOR - Gray_to_num(val));
}
else{
CV_IMAGE_ELEM(coded_imgX, uchar, y,x) = (unsigned char )(Gray_to_num(val));
}
}
else
CV_IMAGE_ELEM(coded_imgX, uchar, y,x) = 0;
//同じ処理をY方向にもする
val = 0;
for(int i=0 ;i<bit_num;i++){
if( CV_IMAGE_ELEM(binarized_img[i+8],uchar,y,x)==WHITE_COLOR)
val+= (int)(pow(2.0 ,(i)));
}
//グレイコードを2進数に変換.
if(val>0){
if(Inverse){
CV_IMAGE_ELEM(coded_imgY, uchar, y,x) = (unsigned char )(WHITE_COLOR - Gray_to_num(val));
}
else{
CV_IMAGE_ELEM(coded_imgY, uchar, y,x) = (unsigned char )(Gray_to_num(val));
}
}
else
CV_IMAGE_ELEM(coded_imgY, uchar, y,x) = 0;
}
}
//ホモグラフィの計算
//プロジェクタ座標系の計算
for(int i=0;i<ARRAY_SIZE;i++){
int x = (int)(camera_field[i][0]);
int y = (int)(camera_field[i][1]);
projector_field[i][0]=(double)(PROJECTION_WIDTH * CV_IMAGE_ELEM(coded_imgY, uchar, y, x))/256.0;
projector_field[i][1]=(double)(PROJECTION_HEIGHT * CV_IMAGE_ELEM(coded_imgX, uchar, y, x))/256.0;
fprintf(stderr,"camera %d %d, code %lf %lf, projector %lf %lf \n",
x,
y,
(double)(CV_IMAGE_ELEM(coded_imgX, uchar, y, x)),
(double)(CV_IMAGE_ELEM(coded_imgY, uchar, y, x)),
projector_field[i][0],
projector_field[i][1]);
}
FindHomography(projector_field,table_field,&Hpt);
cvShowImage("debug_window",coded_imgY);
cvReleaseImage(&posi_img);
cvReleaseImage(&nega_img);
for(int i=0;i<GRAYCODE_NUM/2;i++){
cvReleaseImage(&binarized_img[i]);
}
return;
}
void
GetTopView()
{
if(Hct){
cvWarpPerspective(frame,top_img,Hct, CV_INTER_LINEAR | CV_WARP_FILL_OUTLIERS, cvScalarAll(100));
}
}
void
GetProjectorView()
{
if(Hpt){
if(GAME_MODE)
cvWarpPerspective(field_img,projection_img ,Hpt, CV_INTER_LINEAR | CV_WARP_INVERSE_MAP | CV_WARP_FILL_OUTLIERS, cvScalarAll(100));
else
cvWarpPerspective(sample_img,projection_img ,Hpt, CV_INTER_LINEAR | CV_WARP_INVERSE_MAP | CV_WARP_FILL_OUTLIERS, cvScalarAll(100));
}
}
////////////////////ゲーム用ルーチン/////////////////////////////////////////////
void
MakeFieldImage()
{
// field を初期化して、線を引く
cvSet(field_img,cvScalarAll(255));
cvRectangle(field_img,cvPoint(0,FIELD_SIZE*2/8),cvPoint(FIELD_SIZE/8, FIELD_SIZE*6/8),CV_RGB(255,0,0),CV_FILLED);
cvRectangle(field_img,cvPoint(FIELD_SIZE*7/8,FIELD_SIZE*2/8),cvPoint(FIELD_SIZE, FIELD_SIZE*6/8),CV_RGB(255,0,0),CV_FILLED);
for(int x =0;x<3;x++){
for(int y=0;y<3;y++){
if(board[x][y]== PLAYER_1 && PLAYER_1==AI_PLAYER){
//とりあえず黒丸を描く、要変更
cvCircle(field_img, cvPoint(x*FIELD_SIZE/3 + FIELD_SIZE/6, y*FIELD_SIZE/3 + FIELD_SIZE/6), FIELD_SIZE/7, cvScalarAll(0),4);
}
if(board[x][y]==PLAYER_2 && PLAYER_2==AI_PLAYER){
//とりあえずXを描く
cvLine(field_img,cvPoint(x*FIELD_SIZE/3,y*FIELD_SIZE/3),cvPoint( (x+1)*FIELD_SIZE/3,(y+1)*FIELD_SIZE/3),cvScalarAll(0),4);
cvLine(field_img,cvPoint( (x+1)*FIELD_SIZE/3,y*FIELD_SIZE/3),cvPoint( (x)*FIELD_SIZE/3,(y+1)*FIELD_SIZE/3),cvScalarAll(0),4);
}
}
}
}
void
CoutBoard(int input[3][3]){
fprintf(stderr,"------\n");
for(int y=0;y<3;y++){
fprintf(stderr,"%2d%2d%2d\n",input[0][y],input[1][y],input[2][y]);
}
}
int
CountBlank(int input[3][3]){
int blank=0;
for(int x=0;x<3;x++){
for(int y=0;y<3;y++){
if(input[x][y]==BLANK)
blank++;
}
}
return blank;
}
//盤面の勝敗判定
int
BoardCheck(int input_board[3][3])
{
for(int i=0;i<3;i++){
int val = input_board[i][0]+input_board[i][1]+input_board[i][2];
if( val==PLAYER_1*3){
return PLAYER_1;
}
if(val==PLAYER_2*3){
return PLAYER_2;
}
val = input_board[0][i]+input_board[1][i]+input_board[2][i];
if( val==PLAYER_1*3){
return PLAYER_1;
}
if(val==PLAYER_2*3){
return PLAYER_2;
}
}
int val = input_board[0][0]+input_board[1][1]+input_board[2][2];
if( val==PLAYER_1*3){
return PLAYER_1;
}
if(val==PLAYER_2*3){
return PLAYER_2;
}
val = input_board[2][0]+input_board[1][1]+input_board[0][2];
if( val==PLAYER_1*3){
return PLAYER_1;
}
if(val==PLAYER_2*3){
return PLAYER_2;
}
return BLANK;
}
void
ShowWinner(){
if(winner == AI_PLAYER){
cvPutText(field_img, "COMPUTER", cvPoint(0,FIELD_SIZE*1/3),&cvFont(5,4),cvScalar(0,255,0,0));
cvPutText(field_img, "WIN", cvPoint(0,FIELD_SIZE*2/3),&cvFont(5,4),cvScalar(255,0,0,0));
GetProjectorView();
cvShowImage(MAIN_WINDOW,projection_img);
}
if(AI_PLAYER!=0 && winner == AI_PLAYER*-1){
cvPutText(field_img, "YOU", cvPoint(0,FIELD_SIZE*1/3),&cvFont(5,4),cvScalar(0,255,0,0));
cvPutText(field_img, "WIN", cvPoint(0,FIELD_SIZE*2/3),&cvFont(5,4),cvScalar(255,0,0,0));
GetProjectorView();
cvShowImage(MAIN_WINDOW,projection_img);
}
if( AI_PLAYER == 0 && winner == PLAYER_1){
cvPutText(field_img, "PLAYER 1", cvPoint(0,FIELD_SIZE*1/3),&cvFont(4,4),cvScalar(0,255,0,0));
cvPutText(field_img, "WIN", cvPoint(0,FIELD_SIZE*2/3),&cvFont(4,4),cvScalar(255,0,0,0));
GetProjectorView();
cvShowImage(MAIN_WINDOW,projection_img);
}
if( AI_PLAYER == 0 && winner == PLAYER_2){
cvPutText(field_img, "PLAYER 2", cvPoint(0,FIELD_SIZE*1/3),&cvFont(4,4),cvScalar(0,255,0,0));
cvPutText(field_img, "WIN", cvPoint(0,FIELD_SIZE*2/3),&cvFont(4,4),cvScalar(255,0,0,0));
GetProjectorView();
cvShowImage(MAIN_WINDOW,projection_img);
}
if(winner == BLANK){
cvPutText(field_img, "DRAW", cvPoint(0,FIELD_SIZE*1/3),&cvFont(4,4),cvScalar(0,255,0,0));
cvPutText(field_img, "GAME", cvPoint(0,FIELD_SIZE*2/3),&cvFont(4,4),cvScalar(255,0,0,0));
GetProjectorView();
cvShowImage(MAIN_WINDOW,projection_img);
}
return;
}
//直接入力による盤面の更新
bool
UpdateBoard2(int bx, int by, int player)
{
if(bx<0 || bx>=3 || by <0 || by>=3)
return false;
if(board[bx][by]!=0)
return false;
board[bx][by]=player;
game_state++;
CoutBoard(board);
//交代や、盤面のチェックはここだとまずいかも。
//手番の交代
available_player = player*-1;
winner = BoardCheck(board);
if(winner !=0){
fprintf(stderr,"WINNER = %d \n",winner);
available_player = BLANK;
}
return true;
}
//座標入力による盤面の更新
bool
UpdateBoard(int x, int y, int player)
{
int bx,by;
bx = x/(FIELD_SIZE/3);
by = y/(FIELD_SIZE/3);
return UpdateBoard2(bx,by,player);
}
bool
FieldCheck()
{
//Field画像を取得
GetTopView();
if(!top_img){
return false;
}
IplImage *current_field = cvCloneImage(top_img);
// added from
IplImage* dst; // 入力
IplImage* color_dst; // 出力
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* lines = 0;
int i;
float *p;
CvSeq *circles = 0; // みつかった円
// here
//盤面の確認用差分画像
IplImage *debug_image1, *debug_image2,*debug_image3;
debug_image1 = cvCreateImage(cvGetSize(current_field), IPL_DEPTH_8U,1);
debug_image2 = cvCreateImage(cvGetSize(initial_field), IPL_DEPTH_8U,1);
debug_image3 = cvCreateImage(cvGetSize(initial_field), IPL_DEPTH_8U,1);
cvCvtColor(current_field ,debug_image1, CV_RGB2GRAY);
cvCvtColor(initial_field ,debug_image2, CV_RGB2GRAY);
cvAbsDiff(debug_image1,debug_image2,debug_image3);
cvThreshold(debug_image3,debug_image2,BOARD_THRESHOLD,255,CV_THRESH_BINARY);
cvShowImage("debug",debug_image2);
// added from
//画像領域の確保
dst = cvCreateImage( cvGetSize(debug_image2), 8, 1 );
color_dst = cvCreateImage( cvGetSize(debug_image2), 8, 3 );
// 線検出
//まずはエッジ検出
cvCanny( debug_image2, dst, 50, 200, 3 );
//エッジ画像をカラー化
cvCvtColor( dst, color_dst, CV_GRAY2BGR );
//ハフ変換で直線検出
lines = cvHoughLines2( dst, // 入力画像
storage, // 検出された線を保存する領域
CV_HOUGH_PROBABILISTIC, // ハフ変換の種類
1, // 距離解像度(1ピクセル当たりの単位)
CV_PI/100,//720,//180, // 角度解像度(ラジアン単位)
5, // しきい値
5, // 各種法で使うパラメータ1 増やすと線が減った
10 ); // パラメータ2
//検出した直線を描
for( i = 0; i < lines->total; i++ )
{
CvPoint* line = (CvPoint*)cvGetSeqElem(lines,i);
cvLine( color_dst, line[0], line[1], CV_RGB(255,0,0), 3, CV_AA, 0 );
}
// 円検出
// // (2)ハフ変換のための前処理(画像の平滑化を行なわないと誤検出が発生しやすい)
// cvSmooth (dst, debug_image2, CV_GAUSSIAN, 11, 11, 0, 0);
// storage = cvCreateMemStorage (0);
// // (3)ハフ変換による円の検出と検出した円の描画
// circles = cvHoughCircles (dst, storage, CV_HOUGH_GRADIENT,
// 1, 100, 20, 40, 10, MAX (dst->width, dst->height));
// //入力パラメータの説明
// // src_img_gray : 入力画像
// // storage : 検出結果の保存領域
// // CV_HOUGH_GRADIENT :ハフ変換の種類。2段階ハフ変換
// // 1: 円の中心を求めるときの解像度
// // 100:円検出における中心座標間の最小間隔(微妙にずれた円をたくさん検出するのを防ぐ)
// // 20: パラメータ、Cannyエッジ検出のパラメータになる。
// // 50: パラメータ、中心検出のしきい値
// // 10: 検出すべき円の最小半径
// // MAX (src_img_gray->width, src_img_gray->height) : 検出すべき円の最大半径(この場合は画像サイズの大きい方)
// for (i = 0; i < circles->total; i++)
// {
// p = (float *)cvGetSeqElem (circles, i);
// cvCircle (color_dst, cvPoint (cvRound (p[0]), cvRound (p[1])), 3, CV_RGB (0, 255, 0), -1, 8, 0);
// cvCircle (color_dst, cvPoint (cvRound (p[0]), cvRound (p[1])), cvRound (p[2]), CV_RGB (255, 0, 0), 3, 8, 0);
// }
// 重心検出
// char text[10][30];
// double spatial_moment, central_moment, norm_c_moment;
// CvFont font;
// CvSize text_size;
// CvMoments momentsr;
// // (2)入力画像の3次までの画像モーメントを計算する
// cvMoments (dst, &momentsr, 0);
// // (3)モーメントを得られたCvMoments構造体の値を使って計算する.
// // m10 を求める得る場合は(&moments, 1, 0) にかえる
// // Spatial moment と Central moment の場合、どのように値が違うか確認する。
// spatial_moment = cvGetSpatialMoment (&momentsr, 0, 0);
// central_moment = cvGetCentralMoment (&momentsr, 0, 0);
// //重心を計算してみる 重心(cx,cy) = (m10/m00 , m01/m00);
// double cx = cvGetSpatialMoment(&momentsr, 1,0)/spatial_moment;
// double cy = cvGetSpatialMoment(&momentsr, 0,1)/spatial_moment;
// fprintf(stderr,"centroid (cx, cy) = ( %.3lf, %.3lf ) \n", cx,cy);
// // (4)得られたモーメントを文字として画像に描画
// cvInitFont (&font, CV_FONT_HERSHEY_SIMPLEX, 1.0, 1.0, 0, 2, 8);
// snprintf (text[0], 30, "spatial=%.3f", spatial_moment);
// snprintf (text[1], 30, "central=%.3f", central_moment);
// cvGetTextSize (text[0], &font, &text_size, 0);
// for (i = 0; i < 2; i++){
// cvPutText (color_dst, text[i], cvPoint (10, (text_size.height + 3) * (i + 1)), &font, cvScalar(255,0,0));
// fprintf(stderr,"%s \n", text[i]);
// }
//ウインドウに表示
cvNamedWindow( "Hough", 1 );
cvShowImage( "Hough", color_dst );
// here
//9枚の画像領域を確保
IplImage **current_grid;
IplImage **initial_grid;
current_grid = (IplImage**)malloc(sizeof(IplImage*)*9);
initial_grid = (IplImage**)malloc(sizeof(IplImage*)*9);
//盤面の分割
for(int i = 0;i<9;i++){
int x = i%3;
int y = i/3;
cvSetImageROI(current_field, cvRect(x*current_field->width/3, y*current_field->height/3,current_field->width/3,current_field->height/3));
cvSetImageROI(initial_field, cvRect(x*initial_field->width/3, y*initial_field->height/3,initial_field->width/3,initial_field->height/3));
current_grid[i]=cvCreateImage(cvGetSize(current_field), IPL_DEPTH_8U,1);
initial_grid[i]=cvCreateImage(cvGetSize(initial_field), IPL_DEPTH_8U,1);
cvCvtColor(current_field ,current_grid[i], CV_RGB2GRAY);
cvCvtColor(initial_field ,initial_grid[i], CV_RGB2GRAY);
cvResetImageROI(current_field);
cvResetImageROI(initial_field);
}
//各画像を認識
//作業用領域
IplImage *sub_image;
sub_image = cvCreateImage(cvGetSize(current_grid[0]),IPL_DEPTH_8U,1);
IplImage *bin_image;
bin_image = cvCreateImage(cvGetSize(current_grid[0]),IPL_DEPTH_8U,1);
CvMoments moments[9];
for(int i=0;i<9;i++){
cvAbsDiff(initial_grid[i],current_grid[i],sub_image);
cvThreshold(sub_image,bin_image,BOARD_THRESHOLD,255,CV_THRESH_BINARY);
//マグネットなんかがおかれてそうな場所を決定
//モーメントを求める
cvMoments(bin_image, &moments[i],1);
}
//しばらく様子見
Sleep(100);
//マグネットがおかれてそうな場所が1つに絞れたら場所の決定+盤面を更新して返す。
bool avairable_board[3][3];
int x=-1,y=-1,count=0;
CvMoments tmp_moments;
for(int i=0;i<9;i++){
int tx= i%3;
int ty= i/3;
//もういっかい同じ計算をします。
if(board[tx][ty]==BLANK && cvGetSpatialMoment(&moments[i],0,0) > MOMENT_TH){
cvAbsDiff(initial_grid[i],current_grid[i],sub_image);
cvThreshold(sub_image,bin_image,BOARD_THRESHOLD,255,CV_THRESH_BINARY);
//マグネットなんかがおかれてそうな場所を決定
//モーメントを求める
// cvSetImageCOI(bin_image,1);
cvMoments(bin_image, &tmp_moments,1);
//面積が大きいままならば石がおかれたかもしれない。
if(cvGetSpatialMoment(&tmp_moments,0,0) > MOMENT_TH ){
x=tx;
y=ty;
count ++;
}
}
}
//とりあえず、画像は解放
cvReleaseImage(&debug_image1);
cvReleaseImage(&debug_image2);
cvReleaseImage(&debug_image3);
cvReleaseImage(&current_field);
cvReleaseImage(&sub_image);
cvReleaseImage(&bin_image);
for(int i = 0;i<9;i++){
cvReleaseImage(&initial_grid[i]);
cvReleaseImage(&current_grid[i]);
}
if(count == 1){
board_update = UpdateBoard2(x,y,available_player);
return true;
}
//マグネットが見つからなくても、いったん返す
return false;
}
bool
AIRandom(int player)
{
//現在の盤面のコピー
int local_board[3][3];
int avairable_area=0;
for(int y=0;y<3;y++){
for(int x=0;x<3;x++){
local_board[x][y]=board[x][y];
if(local_board[x][y] == BLANK){
avairable_area++;
}
}
}
if(avairable_area == 0){
return false;
}
//乱数を生成する。
srand(time(NULL));
int x,y;
for(;;){
x = rand()%3;
y = rand()%3;
if(local_board[x][y]==BLANK){
board_update = UpdateBoard2(x,y,player);
return true;
}
}
return false;
}
///////////////////イベント処理用とか初期化とか/////////////////////////////////////////////
void
initGameMode()
{
printf("Game mode \n");
PROJECTOR_VIEW_MODE=false;
CAMERA_VIEW_MODE=false;
TOP_VIEW_MODE=false;
GAME_MODE = true;
available_player = PLAYER_1;
game_state = 0;
winner = 0;
if (field_img) cvReleaseImage(&field_img);
field_img = cvCloneImage(sample_img);
if (!sample_img){
fprintf(stderr, "No sample image\n");
}
if (!field_img)
fprintf(stderr, "No ImageFile\n");
for(int x =0;x<3;x++){
for(int y=0;y<3;y++){
board[x][y]=0;
}
}
MakeFieldImage();
//プロジェクタで盤面を投影する。
if(field_img){
GetProjectorView();
// cvFlip(projection_img, projection_img);
cvShowImage(MAIN_WINDOW, projection_img);
}
//フィールドの初期画像を保存
GetTopView();
if(initial_field){
cvReleaseImage(&initial_field);
}
initial_field = cvCloneImage(top_img);
}
// キーボード入力コールバック
void keyboardFunc(unsigned char key)
{
fprintf(stderr,"key = %c\n", key);
switch (key) {
case '1':
if(GAME_MODE){
board_update = UpdateBoard2(0,0,available_player);
}
break;
case '2':
if(GAME_MODE){
board_update = UpdateBoard2(1,0,available_player);
}
break;
case '3':
if(GAME_MODE){
board_update = UpdateBoard2(2,0,available_player);
}
break;
case '4':
if(GAME_MODE){
board_update = UpdateBoard2(0,1,available_player);
}
break;
case '5':
if(GAME_MODE){
board_update = UpdateBoard2(1,1,available_player);
}
break;
case '6':
if(GAME_MODE){
board_update = UpdateBoard2(2,1,available_player);
}
break;
case '7':
if(GAME_MODE){
board_update = UpdateBoard2(0,2,available_player);
}
break;
case '8':
if(GAME_MODE){
board_update = UpdateBoard2(1,2,available_player);
}
break;
case '9':
if(GAME_MODE){
board_update = UpdateBoard2(2,2,available_player);
}
break;
case 'q':
case 'Q':
case '\033': // '\033' は ESC の ASCII コード
exit(0);
break;
case 'T':
case 't':
printf("Top view mode \n");
TOP_VIEW_MODE = true;
CAMERA_VIEW_MODE=false;
PROJECTOR_VIEW_MODE=false;
GAME_MODE = false;
break;
case 'C':
printf("Camera Calibration mode \n");
CAMERA_CALIBRATION_MODE = true;
click_count = 0;
case 'c':
printf("Camera View mode \n");
CAMERA_VIEW_MODE = true;
TOP_VIEW_MODE=false;
PROJECTOR_VIEW_MODE=false;
GAME_MODE = false;
break;
case 'P':
printf("Projector Calibration mode \n");
PROJECTOR_CALIBRATION_MODE=true;
graycode_count=0;
wait_count=1;
case 'p':
printf("Projector View mode \n");
PROJECTOR_VIEW_MODE=true;
CAMERA_VIEW_MODE=false;
TOP_VIEW_MODE=false;
GAME_MODE = false;
break;
case 'A':
AI_PLAYER= PLAYER_1;
initGameMode();
break;
case 'a':
AI_PLAYER= PLAYER_2;
initGameMode();
break;
case 'g':
AI_PLAYER = 0;
initGameMode();
break;
default:
break;
}
}
//------------------------------------------------------------------------------
// アイドル時のコールバック
void idleFunc()
{
if (capture) { //カメラが存在するとき
//キャプチャ
frame = cvQueryFrame(capture);
//}
//
} else { //カメラが存在しないとき
//特にやることなし
}
//テクスチャに描画したい画像を投げる
if(CAMERA_VIEW_MODE){
cvShowImage(MAIN_WINDOW, frame);
}
else if(TOP_VIEW_MODE){
//
GetTopView();
if(top_img){
cvShowImage(MAIN_WINDOW, top_img);
}
else{
cvShowImage(MAIN_WINDOW,frame);
fprintf(stderr, "Found No top image\n");
}
}else if(PROJECTOR_VIEW_MODE){
if(PROJECTOR_CALIBRATION_MODE){
//wait_countが0WAIT_FRAMEの整数倍の時にキャプチャ
if(wait_count%WAIT_FRAME == 0){
frame = cvQueryFrame(capture);
captured_code_img[graycode_count]= cvCloneImage(frame);
graycode_count++;
}
//整数倍でないときは、グレイコード画像を投影
if(wait_count%WAIT_FRAME!=0 && graycode_count<GRAYCODE_NUM){
printf("No %d \n",graycode_count);
cvShowImage(MAIN_WINDOW,graycode_img[graycode_count]);
}
//投影・撮影が終わったらカウンターを戻してキャリブレーションの計算
if(graycode_count>=GRAYCODE_NUM){
graycode_count=0;
ProjectorCalibration();
//最後にフラグを戻してキャリブレーション終了
PROJECTOR_CALIBRATION_MODE=false;
}
wait_count++;
}
//PROJECTOR_VIEW_MODEの場合は、ホモグラフィを元に変換して投影。
else if(projection_img){
GetProjectorView();
cvShowImage(MAIN_WINDOW,projection_img);
}
else{
cvShowImage(MAIN_WINDOW,frame);
}
}
else if(GAME_MODE){
//(マウスで)手の入力があった場合
//FieldRecognition();
//AI対戦でAIの手番の場合次の手を決定
if(available_player !=0){
if(available_player == AI_PLAYER){
AIRandom(AI_PLAYER);
}
else{
FieldCheck();
}
}
if(board_update){
//フィールド画像の作成
MakeFieldImage();
//プロジェクタ画像の更新
board_update=false;
}
if(winner !=0 || CountBlank(board)==0){
// fprintf(stderr,"WINNER = %d \n",winner);
ShowWinner();
// available_player = BLANK;
}
//プロジェクタで盤面を投影する。
if(field_img){
GetProjectorView();
// cvFlip(projection_img, projection_img);
cvShowImage(MAIN_WINDOW,projection_img);
}
else{
fprintf(stderr, "No field image\n");
}
}
else{
exit(1);
}
}
/* コールバック関数 */
void
on_mouse (int event, int x, int y, int flags, void *param = NULL)
{
double ox=0, oy=0;
// (4)マウスイベントを取得
switch (event) {
case CV_EVENT_LBUTTONDOWN:
//マウス左ボタンが押された座標を取得
ox = x*capture_width/window_width;
oy = y*capture_height/window_height;
if(CAMERA_CALIBRATION_MODE){
camera_field[click_count][0]= ox;
camera_field[click_count][1]= oy;
printf("%lf, %lf \n",ox,oy);
click_count++;
if(click_count>=ARRAY_SIZE){
click_count=0;
FindHomography( camera_field , table_field , &Hct);
CAMERA_CALIBRATION_MODE = false;
}
}
else if(GAME_MODE){
//board_update = UpdateBoard(ox,oy,1);
}
else{
printf("%lf, %lf \n",ox,oy);
}
break;
}
}
int main( int argc, char** argv )
{
capture = cvCaptureFromCAM(0);
if (capture) { // カメラが見つかったとき
//記念に一枚
frame = cvQueryFrame(capture);
// カメラのキャプチャ領域サイズを取得
capture_width = (int)( cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH) );
capture_height = (int)( cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT) );
} else {
fprintf(stderr,"Could not initialize capturing...\n");
return -1;
}
//プロジェクタ画像用 tic-tac-toe のフィールド
projection_img = cvCreateImage(cvSize(PROJECTION_WIDTH, PROJECTION_HEIGHT), IPL_DEPTH_8U, 3);
cvRectangle(projection_img, cvPoint(0,0),cvPoint(PROJECTION_WIDTH -1, PROJECTION_HEIGHT - 1 ), cvScalarAll(0));
//テスト用画像
sample_img = cvLoadImage("laughingman2.png");
if(!sample_img){ //画像ファイルも見つからないときは終了
fprintf(stderr, "Found No ImageFile for projection view\n");
exit(-1);
}
//top view 用の画像
top_img = cvLoadImage("topview2.png");
if(!top_img){ //画像ファイルも見つからないときは終了
fprintf(stderr, "Found No ImageFile for top view\n");
exit(-1);
}
graycode_img = (IplImage **)malloc(sizeof(IplImage *)*GRAYCODE_NUM);
captured_code_img = (IplImage **)malloc(sizeof(IplImage *)*GRAYCODE_NUM);
char filename[256];
for(int i=0;i<GRAYCODE_NUM;i++){
sprintf(filename, GRAYCODE_FILE , i);
graycode_img[i]=cvLoadImage(filename);
if(!graycode_img[i]){
fprintf(stderr, "can not find %s \n", filename);
}
}
//作業用画像
gray_img = cvCreateImage (cvGetSize (frame), IPL_DEPTH_8U, 1);
//メインウインドウの作成
cvNamedWindow( MAIN_WINDOW,0);
cvResizeWindow(MAIN_WINDOW, MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT);
cvSetMouseCallback (MAIN_WINDOW, on_mouse);
cvNamedWindow( "debug_window",0);
for(;;)
{
idleFunc();
char c=cvWaitKey(10);
if( c >= 0 ){
fprintf(stderr,"key = %d, %c, \n", c, c);
if(c=='q'){
printf("q\n");
}
keyboardFunc(c);
}
}
cvReleaseCapture( &capture );
cvDestroyWindow("Camera");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment