• Motion sensing game (Ping Pong Game)



    Project Demonstration



    Here is the source code of the project based on OpenCV anc C++.

    Before you run this code on Linux, you should install the OpenCV library  first.

    #include<opencv/highgui.h>
    #include<opencv2/opencv.hpp>
    #include<string>
    #include<sstream>
    #include<stdlib.h>
    #include<time.h>
    #include<stdio.h>
    using namespace std;
    using namespace cv;
    // The length and width of window frame
    const int FRAME_LENGTH =  640, FRAME_WIDTH = 480;
    
    // The stating point of BALL
    const int BALL_START_X = 320, BALL_START_Y = 26;
    // the constant value of PI
    const double PI = acos(-1);
    // The speed of the ball
    double SPEED = 35;
    // The redius of the ball
    int RADIUS = 20;
    
    // the diagonal points of the board
    int recX1 = 225, recY1 = 460;
    int recX2 = 415, recY2 = 480;
    const int boardLength = recX2 - recX1;
    const int boardWidth = recY2 - recY1;
    
    // The initial min and max of HSV filter values
    int H_MIN = 0;
    int H_MAX = 256;
    int S_MIN = 0;
    int S_MAX = 256;
    int V_MIN = 0;
    int V_MAX = 256;
    
    // The maximum number of object to be detected
    const int MAX_NUM_OBJECT = 50;
    // Minimum and maximum area of object
    const int MIN_OBJECT_AREA = 20 * 20;
    const int MAX_OBJECT_AREA = FRAME_LENGTH * FRAME_WIDTH / 1.5;
    
    // Name of different windows
    const string originalImage = "Original Image";
    const string hsvImage = "HSV Image";
    const string thresholdImage = "Threshold image";
    const string trackbarName = "Trackbars";
    // The flag to control whether tracking object
    bool trackObject = true;
    
    // Scoreboard
    int scoreBoard = 0;
    
    // GameOver
    bool gameOver = false;
    
    // Select the game mode
    int choice = 0;
    
    // acceleration
    int acc = 0;
    
    // This function will be called when a trackbar position is changed
    void onTrackbar(int, void *){	
    }
    // Convert the integer to string
    string intToString(int);
    // Create trackbars
    void createTrackbars();
    // Draw crosshair to locate the position of the target
    void drawObject(int ,int , Mat&);
    // Reduce noise
    void morphOps(Mat&);
    // Locate the position of the object
    void trackFilteredObject(int& ,int&, Mat, Mat&);
    // Depict the board
    void depictBoard(Mat, Point, Point);
    // Depict Generator to generate the ball
    void depictGenerator(Mat);
    // Limit the board in case of the board out of border
    void limitBoard(int&, int&, int);
    // Class of BALL
    class NewBall{
    	private:
    	// The driction of the ball at the beginning
    	double angle;
    	// the moving vector of the ball for each frame
    	int Vx, Vy;
    	// the center point of the ball
    	Point point;
    	public:
    		NewBall();
    		void reset();
    		// Movement the ball like baseball
    		void baseballMoving(Point, Point);
    		// Movement the ball like ping pong ball
    		void pingpongMoving(Point, Point);
    		// Depict the ball
    		void depictBall(Mat);
    		void accelerate();
    };
    NewBall::NewBall(){
    		// the center point of the ball
    		point = Point(BALL_START_X, BALL_START_Y);
    		// Initialize the random seed
    		srand(time(NULL));
    		// The driction of the ball at the beginning
    		// Filter the angle of the driection let it between PI/4 and PI/4*3
    		angle = (rand() % 51 + 25) / 100.0 * PI;
    		// the moving vector of the ball for each frame
    		Vx = (int)(cos(angle) * SPEED);
    		Vy = (int)(sin(angle) * SPEED);
    }
    void NewBall::accelerate(){
    		Vx = (int)(cos(angle) * SPEED);
    		Vy = (int)(sin(angle) * SPEED);
    }
    void NewBall::reset(){
    		// the center point of the ball
    		point = Point(BALL_START_X, BALL_START_Y);
    		// Initialize the random seed
    		// The driction of the ball at the beginning
    		angle = (rand() % 51 + 25) / 100.0 * PI;
    		// the moving vector of the ball for each frame
    		Vx = (int)(cos(angle) * SPEED);
    		Vy = (int)(sin(angle) * SPEED);
    }
    // Depict the ball on the frame
    void NewBall::depictBall(Mat img){
    	int lineType = 8;
    	circle(img, point, RADIUS, Scalar(0,0,255), CV_FILLED, lineType);
    }
    // Movement of the ball
    void NewBall::baseballMoving(Point a, Point b){
    	if(point.x - RADIUS < 0 || point.x + RADIUS > FRAME_LENGTH){
    		Vx = -Vx;	
    	}	
    	if(point.y + RADIUS > FRAME_WIDTH + 5){
    		// the ball will dispear and then appear in the original position
    		gameOver = true;
    	}
    	if((point.y + RADIUS > FRAME_WIDTH - boardWidth - 10) && (point.x >= a.x - 12 && point.x <= b.x + 12)){
    		cout << a.x - 12<< " " << b.x + 12<< endl;
    		scoreBoard++;
    		if(scoreBoard == 5 && acc == 0){
    			SPEED += 5;
    		}else if (scoreBoard == 10 && acc == 1){
    		    SPEED += 5;
    		}else if (scoreBoard == 15 && acc == 2){
    		    SPEED += 5;
    		}else if (scoreBoard == 20 && acc == 3){
    		    SPEED += 5;
    		}
    		Vy = -Vy;
    	}
    	if(point.y - RADIUS < 0){
    		this->reset();	
    	}
    	point.x = point.x + Vx;
    	point.y = point.y + Vy;
    }
    
    // Movement of the ball
    void NewBall::pingpongMoving(Point a, Point b){
    	if(point.x - RADIUS < 0 || point.x + RADIUS > FRAME_LENGTH){
    		Vx = -Vx;	
    	}	
    	if(point.y + RADIUS > FRAME_WIDTH + 5){
    		// the ball will dispear and then appear in the original position
    		gameOver = true;
    	}
    	if((point.y + RADIUS > FRAME_WIDTH - boardWidth - 10) && (point.x >= a.x - 12 && point.x <= b.x + 12)){
    		cout << a.x - 12<< " " << b.x + 12<< endl;
    		cout << Vx << " " << Vy << endl;
    		scoreBoard++;
    		if(scoreBoard == 5 && acc == 0){
    		    SPEED += 5;
    			this->accelerate();
    		}else if (scoreBoard == 10 && acc == 1){
    			SPEED += 5;
    			this->accelerate();
    		}else if (scoreBoard == 15 && acc == 2){
    			SPEED += 5;
    			this->accelerate();
    			acc++;
    		}else if (scoreBoard == 20 & acc == 3){
    			SPEED += 5;
    			this->accelerate();
    			acc++;
    		}
    		Vy = -Vy;
    	}
    	if(point.y - RADIUS < 0){
    		Vy = -Vy;	
    	}
    	point.x = point.x + Vx;
    	point.y = point.y + Vy;
    }
    
    
    int main(int argc, char*argv[]){
    		// two boolean variables to control the two functions, one is to track object and the other 
    		//reduce noise
    		bool useMorphOps = true;
    		//Matrix to store each frame from the webcam feed
    		Mat cameraFeed;
    		//Martrix storage for HSV image
    		Mat HSV;
    		//Matrix sotrage for binary threshold image
    		Mat threshold;
    		//x and y values for the current location of the object
    		int x = 0, y = 0;
    		//The position of the object on the last frame
    		int lastX = 0, lastY = 0;
    		//Create slider bars for HSV filtering
    		createTrackbars();
    		//video capture object to acquire webcam feed
    		VideoCapture cap(0);
    		char name[] = "camera";
    		if(!cap.isOpened()){
    			cout << "Camera can not opened." << endl;
    			return -1;
    		}
    		//Window of original image
    		namedWindow(originalImage, WINDOW_AUTOSIZE);
    		//Window of HSV image
    		namedWindow(hsvImage, WINDOW_AUTOSIZE);
    		//Window of threshold image
    		namedWindow(thresholdImage, WINDOW_AUTOSIZE);
    		// move windows to its proper location
    		moveWindow(originalImage, 20, 20);
    		moveWindow(hsvImage, 20, 600);
    		moveWindow(thresholdImage, 700, 20);
    		// flag to control the movement of the ball
    		bool ballMoveFlag = 0;
    		NewBall ball;
    
    		while(1){
    			bool bSuccess = cap.read(cameraFeed);
    			if(!bSuccess){
    				cout << "Can not read a frame from video stream" << endl;
    				break;
    			}
    			//Convert the original image to HSV
    			cvtColor(cameraFeed, HSV, COLOR_BGR2HSV);
    			//Select a range of H,S,V properties and then it can filter the
    			//specific color
    			inRange(HSV, Scalar(H_MIN, S_MIN, V_MIN), Scalar(H_MAX,S_MAX,V_MAX), threshold);
    			//Reduce noise
    			if(useMorphOps){
    				morphOps(threshold);	
    			}
    			// press the button to go on the game
    			if(waitKey(30) == 27){  // press the ESC button
    				cout << "You exit the game" << endl;
    				break;
    			}
    			if(waitKey(30) == 32){  // press the space button
    				ballMoveFlag = 1;
    				ball.reset();
    				gameOver = false;
    				acc = 0;
    				SPEED = 35;
    			}
    			if(waitKey(30) == 49){
    				choice = 1;	
    			}
    			if(waitKey(30) == 50){
    				choice = 2;
    			}
    
    			//On the last frame keep the location of the frame
    			lastX = x;
    			lastY = y;
    			//Parameters x and y record the current position of the objcet
    			if(trackObject){
    				trackFilteredObject(x, y, threshold, cameraFeed);
    				int diffX = x - lastX, diffY = y - lastY;
    				//Control the movement of the rectangle and do not let it out of the boarder
    				limitBoard(recX1, recX2, diffX);
    			}
    			// Depict the image of the board and the ball
    			depictGenerator(cameraFeed);
    			depictBoard(cameraFeed, Point(recX1, recY1), Point(recX2, recY2));
    			ball.depictBall(cameraFeed);
    			putText(cameraFeed,"Your Score " + intToString(scoreBoard), Point(350,50),1,2,Scalar(0,255,255),2);		
    			if(ballMoveFlag == false){
    				putText(cameraFeed, "Press space bar to start", Point(30,400),2,1,Scalar(0,0,255),4);		
    			}
    			if(choice != 1 && choice != 2){
    				putText(cameraFeed, "Press 1 to select baseball mode", Point(30,200),2,1,Scalar(0,0,255),4);	
    				putText(cameraFeed, "Press 2 to select pingpong mode", Point(30,300),2,1,Scalar(0,0,255),4);	
    			}
    			if(ballMoveFlag == 1){
    				// Generate the ball
    				//Move the center fo the circle to the next position
    				if(choice == 1){
    					ball.baseballMoving(Point(recX1, recY1), Point(recX2, recY2));
    				}else if(choice == 2){
    					ball.pingpongMoving(Point(recX1, recY1), Point(recX2, recY2));	
    				}
    				if(gameOver == true){
    					choice = 0;
    					ballMoveFlag = 0;
    					scoreBoard = 0;
    				}
    			}
    			//If the ball is out of boarder, GameOver
    			if(gameOver == true && choice == 0 && ballMoveFlag == 0){
    				putText(cameraFeed, "GAME OVER", Point(30,100),2,1,Scalar(0,0,255),4);	
    			}
    			imshow(originalImage, cameraFeed);
    			imshow(hsvImage, HSV);
    			imshow(thresholdImage, threshold);
    		}
    		return 0;
    }
    // Limit the board in case of the board out of border
    void limitBoard(int &recX1, int &recX2, int diffX){
    	if(recX1 + diffX < 0){
    		recX1 = 0;
    		recX2 = boardLength;
    	}else if(recX2 + diffX > FRAME_LENGTH){
    		recX2 = FRAME_LENGTH;
    		recX1 = FRAME_LENGTH - boardLength;
    	}else{
    		recX1 += diffX;
    		recX2 += diffX;
    	}
    }
    
    // Depict the generator 
    void depictGenerator(Mat img){
    	int thickness = CV_FILLED;
    	int lineType = 8;
    	Point a(300,0);
    	Point b(340,5);
    	rectangle(img, a, b, Scalar(0,255,0), thickness, lineType);
    }
    
    void depictBoard(Mat img, Point a, Point b){
    	int thickness = CV_FILLED;
    	int lineType = 8;
    	rectangle(img, a, b, Scalar(255,0,0), thickness, lineType);
    }
    void createTrackbars(){
    	namedWindow(trackbarName, 0);
    	moveWindow(trackbarName, 800, 500);
    	createTrackbar("H_MIN", trackbarName, &H_MIN, H_MAX, onTrackbar);
    	createTrackbar("H_MAX", trackbarName, &H_MAX, H_MAX, onTrackbar);
    	createTrackbar("S_MIN", trackbarName, &S_MIN, S_MAX, onTrackbar);
    	createTrackbar("S_MAX", trackbarName, &S_MAX, S_MAX, onTrackbar);
    	createTrackbar("V_MIN", trackbarName, &V_MIN, V_MAX, onTrackbar);
    	createTrackbar("V_MAX", trackbarName, &V_MAX, V_MAX, onTrackbar);
    }
    // Draw crosshair to locate the position of the target
    void drawObject(int x,int y, Mat &frame){
    	circle(frame,Point(x, y), 20, Scalar(255,0,0), 2);
    	if(y - 25 > 0){
    		line(frame, Point(x, y), Point(x, y-25), Scalar(255,0,0), 2);	
    	}else{
    		line(frame, Point(x, y), Point(x, 0), Scalar(255,0,0), 2);	
    	}
    	if(y + 25 < FRAME_WIDTH){
    		line(frame, Point(x,y), Point(x,y+25),Scalar(255,0,0), 2);	
    	}else{
    		line(frame, Point(x,y), Point(x,FRAME_WIDTH),Scalar(255,0,0), 2);	
    	}
    	if(x - 25 > 0){
    		line(frame, Point(x,y), Point(x-25, y), Scalar(255,0,0), 2);	
    	}else{
    		line(frame, Point(x,y), Point(0, y), Scalar(255,0,0), 2);	
    	}
    	if(x + 25 < FRAME_LENGTH){
    		line(frame, Point(x,y), Point(x+25,y), Scalar(255,0,0),2);	
    	}else{
    		line(frame,Point(x,y), Point(FRAME_LENGTH,y), Scalar(255,0,0),2);	
    	}
    	putText(frame, intToString(x)+","+intToString(y), Point(x,y+30),1,1,Scalar(255,0,0),2);
    }
    
    // Locate the position of the object
    void trackFilteredObject(int& x,int& y, Mat threshold, Mat& cameraFeed){
    	Mat temp;
    	threshold.copyTo(temp);
    	// these two vectors needed for output of findContours
    	vector< vector<Point> > contours;
    	vector< Vec4i > hierarchy;
    	// find contours of filtered image using openCV findContours function
        findContours(temp, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
    	// use moments method to find our filtered object
    	double refArea = 0;
    	bool objectFound = false;
    	if(hierarchy.size() > 0){
    		int numObjects = hierarchy.size();
    		// if number of objects greater than MAX_NUM_OBJECTS we have noisy filter
    		if(numObjects < MAX_NUM_OBJECT){
    			for(int index = 0; index >= 0; index = hierarchy[index][0]){
    				Moments moment = moments((cv::Mat)contours[index]);
    				double area = moment.m00;
    				if(area > MIN_OBJECT_AREA && area < MAX_OBJECT_AREA && area > refArea){
    				 	x = moment.m10 / area;
    					y = moment.m01 / area;
    					objectFound = true;
    					refArea = area;
    				}else{
    					objectFound = false;	
    				}
    				if(objectFound == true){
    					putText(cameraFeed, "Tracking Object", Point(0,50),2,1,Scalar(0,255,0),2);
    					drawObject(x,y,cameraFeed);
    				}
    
    			}
    		}
    		else{
    			putText(cameraFeed,"TOO MUCH NOISE! ADJUST FILTER", Point(0,50),1,2,Scalar(0,0,255),2);		
    		}	
    	}
    }
    
    // Convert the integer to string
    string intToString(int number){
    	std::stringstream ss;
    	ss << number;
    	return ss.str();
    }
    void morphOps(Mat &thresh){
    	Mat erodeElement = getStructuringElement( MORPH_RECT, Size(3,3));
    	Mat dilateElement = getStructuringElement( MORPH_RECT, Size(8,8));
    	erode(thresh, thresh, erodeElement);
    	erode(thresh, thresh, erodeElement);
    	dilate(thresh, thresh, dilateElement);
    	dilate(thresh, thresh, dilateElement);
    }
    
    





  • 相关阅读:
    javascript
    vue-cli3 按需加载loading,服务的方式调用
    chrome devTool
    vuex
    git
    DNS寻址以及IP解析
    es6小记
    webpack 安装vue(两种代码模式compiler 和runtime)
    游戏屏幕朝向
    C#读写Json
  • 原文地址:https://www.cnblogs.com/wgwyanfs/p/6865898.html
Copyright © 2020-2023  润新知