ECE597 Project Interactive Pong

From eLinux.org
Revision as of 09:28, 21 May 2010 by Simonea (talk | contribs) (Added file)
Jump to: navigation, search

This project is part of the ECE597 32-bit Embedded Linux class at Rose-Hulman Institute of Technology in Terre Haute, IN.

Project Goal

The goal of this project is to implement a simple game of "Pong" on the Beagle Board that allows a user to play the game by gesturing with their hands rather than using a mouse or keyboard.

Team Members

Elliot Simon

Mitch Garvin

Matt Luke

Jian Li

Code

OpenCV (C++)

void loadTemplateImage()

Get the color of the hand, so that we can track the movement of the hand automatically.

{
	
        IplImage *tempimage = cvLoadImage("/home/dica/workspace/cvex1/sshou.bmp",1);

	cvCvtColor( tempimage, hsv, CV_BGR2HSV );

	int _vmin = vmin, _vmax = vmax;

	cvInRangeS( hsv, cvScalar(0,smin,MIN(_vmin,_vmax),0),
		cvScalar(180,256,MAX(_vmin,_vmax),0), mask );

	cvSplit( hsv, hue, 0, 0, 0 );

	selection.x = 1;

	selection.y = 1;

	selection.width = 320-1;

	selection.height= 240-1;

	cvSetImageROI( hue, selection );

	cvSetImageROI( mask, selection );

	cvCalcHist( &hue, hist, 0, mask );

	float max_val = 0.f;

	cvGetMinMaxHistValue( hist, 0, &max_val, 0, 0 );

	cvConvertScale( hist->bins, hist->bins, max_val ? 255. / max_val : 0., 0 );

	cvResetImageROI( hue );

	cvResetImageROI( mask );

	track_window = selection;

	track_object = 1;


	cvZero( histimg );

	int bin_w = histimg->width / hdims;

	for(int i = 0; i < hdims; i++ )


	{

		int val = cvRound( cvGetReal1D(hist->bins,i)*histimg->height/255 );

		CvScalar color = hsv2rgb(i*180.f/hdims);

		cvRectangle( histimg, cvPoint(i*bin_w,histimg->height),

			cvPoint((i+1)*bin_w,histimg->height - val),

			color, -1, 8, 0 );

	}

	cvReleaseImage(&tempimage);

}



int main( int argc, char** argv )

create window, get the color of the hand and then track the movement of the hand

{
    CvCapture* capture = 0;

    if( argc == 1 || (argc == 2 && strlen(argv[1]) == 1 && isdigit(argv[1][0])))
        capture = cvCaptureFromCAM( argc == 2 ? argv[1][0] - '0' : 0 );
    else if( argc == 2 )
        capture = cvCaptureFromAVI( argv[1] );

    if( !capture )
    {
        fprintf(stderr,"Could not initialize capturing...\n");
        return -1;
    }
    
    cvNamedWindow( "CamShiftDemo", 1 );
    cvNamedWindow( "Histogram", 1 );
    
    for(;;)
    {
        IplImage* frame = 0;
        int i, bin_w, c;

        frame = cvQueryFrame( capture );
        if( !frame )
            break;

        if( !image )
        {
            /* allocate all the buffers */
            image = cvCreateImage( cvGetSize(frame), 8, 3 );
            image->origin = frame->origin;
            hsv = cvCreateImage( cvGetSize(frame), 8, 3 );
            hue = cvCreateImage( cvGetSize(frame), 8, 1 );
            mask = cvCreateImage( cvGetSize(frame), 8, 1 );
            backproject = cvCreateImage( cvGetSize(frame), 8, 1 );
            hist = cvCreateHist( 1, &hdims, CV_HIST_ARRAY, &hranges, 1 );
            histimg = cvCreateImage( cvSize(320,200), 8, 3 );
            cvZero( histimg );
            loadTemplateImage();
        }

        cvCopy( frame, image, 0 );
        cvCvtColor( image, hsv, CV_BGR2HSV );

        if( track_object )
        {
            int _vmin = vmin, _vmax = vmax;

            cvInRangeS( hsv, cvScalar(0,smin,MIN(_vmin,_vmax),0),
                        cvScalar(180,256,MAX(_vmin,_vmax),0), mask );
            cvSplit( hsv, hue, 0, 0, 0 );

            if( track_object < 0 )
            {
                float max_val = 0.f;
                cvSetImageROI( hue, selection );
                cvSetImageROI( mask, selection );
                cvCalcHist( &hue, hist, 0, mask );
                cvGetMinMaxHistValue( hist, 0, &max_val, 0, 0 );
                cvConvertScale( hist->bins, hist->bins, max_val ? 255. / max_val : 0., 0 );
                cvResetImageROI( hue );
                cvResetImageROI( mask );
                track_window = selection;
                track_object = 1;

                cvZero( histimg );
                bin_w = histimg->width / hdims;
                for( i = 0; i < hdims; i++ )
                {
                    int val = cvRound( cvGetReal1D(hist->bins,i)*histimg->height/255 );
                    CvScalar color = hsv2rgb(i*180.f/hdims);
                    cvRectangle( histimg, cvPoint(i*bin_w,histimg->height),
                                 cvPoint((i+1)*bin_w,histimg->height - val),
                                 color, -1, 8, 0 );
                }
            }

            cvCalcBackProject( &hue, backproject, hist );
            cvAnd( backproject, mask, backproject, 0 );
            cvCamShift( backproject, track_window,
                        cvTermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 ),
                        &track_comp, &track_box );
            track_window = track_comp.rect;

            if( backproject_mode )
                cvCvtColor( backproject, image, CV_GRAY2BGR );
            if( !image->origin )
                track_box.angle = -track_box.angle;
            cvEllipseBox( image, track_box, CV_RGB(255,0,0), 3, CV_AA, 0 );
        }

        if( select_object && selection.width > 0 && selection.height > 0 )
        {
            cvSetImageROI( image, selection );
            cvXorS( image, cvScalarAll(255), image, 0 );
            cvResetImageROI( image );
        }

        cvShowImage( "CamShiftDemo", image );
        cvShowImage( "Histogram", histimg );

        }
    }

    return 0;
}

PyPong.py

#! /usr/bin/env python



# PyPong - an arcade Pong game programmed in Python

# Created by pymike, released to the Public Domain

import sys, os
import random

import pygame
from pygame.locals import *

HUMAN = 1
COM   = 2

class Player(pygame.sprite.Sprite):

    def __init__(self):
        pygame.sprite.Sprite.__init__(self, self.containers)
        self.image = pygame.Surface((16, 64))
        self.image.fill((255, 255, 255), (2, 0, 12, 64))
        self.image.fill((255, 255, 255), (0, 2, 16, 60))
        self.rect = self.image.get_rect(midleft = (16, 240))
        self._rect = Rect(self.rect)

    def update(self):
        self._rect = Rect(self.rect)

        key = pygame.key.get_pressed()
        if key[K_UP]:
            self.rect.move_ip(0, -5)
        if key[K_DOWN]:
            self.rect.move_ip(0, 5)

        if self.rect.bottom > 480:
            self.rect.bottom = 480
        if self.rect.top < 0:
            self.rect.top = 0

class Computer(pygame.sprite.Sprite):

    def __init__(self, ball):
        pygame.sprite.Sprite.__init__(self, self.containers)
        self.image = pygame.Surface((16, 64))
        self.image.fill((255, 255, 255), (2, 0, 12, 64))
        self.image.fill((255, 255, 255), (0, 2, 16, 60))
        self.rect = self.image.get_rect(midleft = (640-32, 240))
        self._rect = Rect(self.rect)
        self.ball = ball
        self.speed = 4
        self.max_speed = self.speed

    def update(self):
        self._rect = Rect(self.rect)

        if abs(self.ball.vy) < self.max_speed:
            self.speed = abs(self.ball.vy)
        else:
            self.speed = self.max_speed

        if self.ball.rect.centery > self.rect.centery:
            self.rect.move_ip(0, self.speed)
        if self.ball.rect.centery < self.rect.centery:
            self.rect.move_ip(0, -self.speed)

        if self.rect.bottom > 480:
            self.rect.bottom = 480
        if self.rect.top < 0:
            self.rect.top = 0

    def set_ball(self, ball):
        self.ball = ball

class Ball(pygame.sprite.Sprite):

    def __init__(self):
        pygame.sprite.Sprite.__init__(self, self.containers)
        self.image = pygame.Surface((12, 12))
        self.image.fill((255, 255, 255), (2, 0, 8, 12))
        self.image.fill((255, 255, 255), (0, 2, 12, 8))
        self.rect = self.image.get_rect(midleft = (320, 240))
        self._rect = Rect(self.rect)
        self.vx = random.choice([5, -5])
        self.vy = random.choice([-2, -1, 1, 2])
        self.scored = 0

    def update(self):
        self._rect = Rect(self.rect)
        self.rect.move_ip(self.vx, self.vy)

        if self.rect.bottom > 480:
            self.rect.bottom = 480
            self.vy = -self.vy
        if self.rect.top < 0:
            self.rect.top = 0
            self.vy = -self.vy
        if self.rect.left < 0:
            self.kill()
            self.scored = COM
        if self.rect.right > 640:
            self.kill()
            self.scored = HUMAN

    def collide(self, bat):
        if self.rect.colliderect(bat.rect) and not self._rect.colliderect(bat._rect):
            if self._rect.right <= bat._rect.left and self.rect.right > bat.rect.left:
                self.rect.right = bat.rect.left
                self.vx = -self.vx
            if self._rect.left >= bat._rect.right and self.rect.left < bat.rect.right:
                self.rect.left = bat.rect.right
                self.vx = -self.vx
                self.vx += 1
                if self.rect.centery < bat.rect.centery:
                    self.vy = ((self.rect.centery-bat.rect.centery)/(bat.rect.height/2))*4.5
                    if self.vy >= -1:
                        self.vy = -1
                if self.rect.centery > bat.rect.centery:
                    self.vy = ((self.rect.centery-bat.rect.centery)/(bat.rect.height/2))*4.5
                    if self.vy <= 1:
                        self.vy = 1
            if self._rect.bottom <= bat._rect.top and self.rect.bottom > bat.rect.top:
                self.rect.bottom = bat.rect.top
                self.vy = -self.vy
            if self._rect.top >= bat._rect.bottom and self.rect.top < bat.rect.bottom:
                self.rect.top = bat.rect.bottom
                self.vy = -self.vy

class Menu:

    def __init__(self, screen):
        self.screen = screen
        self.font = pygame.font.SysFont("Comic Sans MS", 100)
        self.font2 = pygame.font.SysFont("Comic Sans MS", 50)
        self.font3 = pygame.font.SysFont("Comic Sans MS", 25)

        self.all = pygame.sprite.RenderUpdates()
        Computer.containers = self.all
        Ball.containers = self.all

        self.ball = Ball()
        self.com1 = Computer(self.ball)
        self.com2 = Computer(self.ball)
        self.com1.rect.left = 16
        self.clock = pygame.time.Clock()
        self.timer = 0

    def loop(self):
        while 1:

            self.clock.tick(60)
            self.all.update()
            self.timer += 1
            if self.timer >= 50:
                self.timer = 0

            for e in pygame.event.get():
                if e.type == QUIT:
                    pygame.quit()
                    sys.exit()
                if e.type == KEYDOWN:
                    if e.key == K_ESCAPE:
                        pygame.quit()
                        sys.exit()
                    if e.key == K_SPACE:
                        game = Game(self.screen)
                        game.loop()

            self.screen.fill((0, 0, 0))
            self.ball.collide(self.com1)
            self.ball.collide(self.com2)
            self.all.draw(self.screen)
            ren = self.font.render("PyPong", 1, (255, 255, 255))
            self.screen.blit(ren, (320-ren.get_width()/2, 20))
            ren = self.font3.render("March 2008", 1, (255, 255, 255))
            self.screen.blit(ren, (320-ren.get_width()/2, 220))
            ren = self.font3.render("by PyMike", 1, (255, 255, 255))
            self.screen.blit(ren, (320-ren.get_width()/2, 250))
            ren = self.font2.render("Press Space", 1, (255, 255, 255))
            if self.timer <= 25:
                self.screen.blit(ren, (320-ren.get_width()/2, 350))
            pygame.display.flip()

class Game:

    def __init__(self, screen):
        self.screen = screen

        self.all = pygame.sprite.RenderUpdates()
        Player.containers = self.all
        Computer.containers = self.all
        Ball.containers = self.all

        self.ball = Ball()
        self.player = Player()
        self.com = Computer(self.ball)
        self.clock = pygame.time.Clock()

        self.p1score = 0
        self.p2score = 0
        self.win_score = 21
        self.font = pygame.font.SysFont("Comic Sans MS", 50)
        self.font2 = pygame.font.SysFont("Comic Sans MS", 30)
        self.won = False
        self.served = False
        self.done = False

    def handle_input(self):
        for e in pygame.event.get():
            if e.type == QUIT:
                self.pygame.quit()
                sys.exit()
            if e.type == KEYDOWN:
                if e.key == K_ESCAPE:
                    self.done = True
                if e.key == K_SPACE:
                    self.served = True
                    self.ball = Ball()
                    self.com.set_ball(self.ball)

    def render(self):
        self.screen.fill((0, 0, 0))
        self.all.draw(self.screen)
        ren = self.font.render("%s:%s" % (self.p1score, self.p2score), 1, (255, 255, 255))
        self.screen.blit(ren, (320-ren.get_width()/2, 10))
        if self.won == HUMAN:
            ren = self.font.render("Player 1 won!", 1, (255, 255, 255))
            self.screen.blit(ren, (320-ren.get_width()/2, 240-ren.get_height()/2))            
        if self.won == COM:
            ren = self.font.render("Com won!", 1, (255, 255, 255))
            self.screen.blit(ren, (320-ren.get_width()/2, 240-ren.get_height()/2))   
        if not self.served and not self.won:
            ren = self.font2.render("Press Space to Serve", 1, (255, 255, 255))
            self.screen.blit(ren, (320-ren.get_width()/2, 240-ren.get_height()/2))   
        pygame.display.flip()

    def update(self):
        self.clock.tick(60)
        if not self.won and self.served:
            self.all.update()
        self.ball.collide(self.player)
        self.ball.collide(self.com)

        if self.p1score >= self.win_score:
            self.won = HUMAN
        if self.p2score >= self.win_score:
            self.won = COM

        if not self.ball.alive() and not self.won:
            if self.ball.scored == HUMAN:
                self.p1score += 1
            if self.ball.scored == COM:
                self.p2score += 1
            self.ball = Ball()
            self.com.set_ball(self.ball)
            self.served = False

        if self.won or not self.served:
            self.ball.kill()

    def loop(self):
        self.done = False
        while not self.done:
            self.update()
            self.handle_input()
            self.render()

def main():
    pygame.init()
    os.environ["SDL_VIDEO_CENTERED"] = "1"
    pygame.display.set_caption("PyPong")
    screen = pygame.display.set_mode((640, 480))
    menu = Menu(screen)
    menu.loop()


if __name__ == "__main__":

    main()

System Setup

Currently the hand-tracking and Pong games run in different processes. Hand-tracking requires a picture called "sshou.bmp" in the /home/user-name directory that is used as a reference for the histogram that tracks hands. The hand-tracking writes to a temporary pipe in /usr/yval, which the Pong game then reads the latest value from. To run, start the hand-tracking program in a root shell, then the Pong game in a separate root shell. Press [Spacebar] to begin a game, then [Spacebar] to release a ball. Moving a hand up and down in front of the camera causes the user's paddle to move and reflect that. The user's paddle will not move if there is not a ball "in play".

Task List

Below is an estimated list of tasks to complete Task list will be updated as necessary

Task Status Description
Pong on the Beagle Board Complete Port a simple game of classic pong onto the Beagle Board with keyboard/mouse input
Webcam interface with the Beagle Board and OpenCV Complete Get the Beagle Board to recognize the webcam and perform basic functionalities from OpenCV (connect to camera, snapshot, etc.)
Refactor Pong Complete Refactor Pong to work with a different input (camera)
Track hands with camera and OpenCV Complete Track user's hands using OpenCV library functions on the Beagle Board
Interface tracking camera with pong Complete Join the tracking and game into one system such that users can play pong with their hands

Files

File:PyPong.tgz File:Cvex1.zip

Links

Logitech Webcam Pro 9000