element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet & Tria Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • About Us
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      • Japan
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Vietnam
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Raspberry Pi Projects
  • Products
  • Raspberry Pi
  • Raspberry Pi Projects
  • More
  • Cancel
Raspberry Pi Projects
Blog Pi in the Face: Python Code
  • Blog
  • Documents
  • Events
  • Polls
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Raspberry Pi Projects to participate - click to join for free!
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: screamingtiger
  • Date Created: 30 Oct 2015 1:31 PM Date Created
  • Views 545 views
  • Likes 4 likes
  • Comments 1 comment
  • rpiintermediate
  • pieintheface
  • raspberrypi
  • pumpkinpi_2015
  • raspberry_pi_projects
  • pumpkinpi2015
Related
Recommended

Pi in the Face: Python Code

screamingtiger
screamingtiger
30 Oct 2015

Previous posts for this project here:

http://www.element14.com/community/community/raspberry-pi/raspberrypi_projects/blog/tags#/?tags=pieintheface

 

I have the following functionality:

  • Load up images in a specific structure
  • Load up sounds in a specific structure
  • Animate eyes, with a realistic yet random blinking method
  • Animate the mouth to make it appear to be talking
  • The eye can be poked (demonstrated on last post) which results in a specific action.

 

To Do:

Play sounds

Finish Physical Pumpkin.

Make "scare" technique using PIR sensor.

 

 

 

 

 

The eye Poke

 

 

image

 

The poking Sequence for the left eye, the right eye is the same with variables swapped:

def getPoking(self):
if(self.leftPoking):
if(self.pokeStart == 0):
self.leftEyeBlit = display.blit(self.leftEyeBlink[-1],(self.leftEyeX,self.leftEyeY))
self.lastPoke = time.time();
self.pokeStart = time.time();
else:
if(time.time() - self.pokeStart > 10):
self.leftPoking = 0
self.pokeStart = 0
self.leftEyeBlit = display.blit(self.leftEye,(self.leftEyeX,self.leftEyeY))

    

 

 

In this code, the eye poke is the same image as when the eye is nearly fully closed in a blink.  I will change this to blit the squint image that is given in the file structure.

 

There are a couple variables here that come into play.

pokeStart = the time the poke sequence started, if it has started, else it is 0

lastPoke = the time the last poke occurred

 

You can see on line 8 above:

if(time.time() - self.pokeStart > 10):

 

We can only start the poke sequence every 10 seconds.  I plan to put this into a configurable variable.

 

Detecting the Eye Poke

Whenever an images is blitted to the screen, it returns a rectangle object that gives the coordinates of the rectangle the image is on.

In my main game loop I have this code:

for event in pygame.event.get():

if event.type == QUIT:

pygame.quit()

sys.exit()

elif event.type == KEYDOWN:

if event.key == K_ESCAPE:

pygame.quit()

sys.exit()

elif event.type == pygame.MOUSEBUTTONDOWN:

pos = pygame.mouse.get_pos()

if(faces[0].eyes.leftEyeBlit.collidepoint(pos)):

faces[0].eyes.leftPoking = 1

elif(faces[0].eyes.rightEyeBlit.collidepoint(pos)):

faces[0].eyes.rightPoking = 1;

  

 

The event MOUTBUTTONDOWN gives us the x,y coordinates of the button press, or in this case a screen touch.  Given a rect object, there is a function called collidepoint, which given a x,y point will return true if that coordinate is within the rectangle.  If it is, we run the poke sequence for the eye that is poked.

 

 

 

Playing Sounds an the Talking Mouth

Playing sounds in pygame is fairly trivial.  I will be using the Music object to play sounds.  This is a multi-threaded approach as the sound played in the background of your code, and releases your code pointer after it is started.

 

The way this will work is I will have several sound bytes, some of which play together in a specific order the same way the images are grouped together.  Once a sound file is loaded,it can be played then continuously polled to see if it is done.

 

The sound will begin playing, and I will tell the mouth to start talking.  In the main game loop, the sound will be polled and if it is completed, the mouth will be told to stop talking.  The next sound in the sequence will then be played with a small pause between.  If the sequence is completed, a new sequence will be picked at random.

 

There are 2 special sequences.  One of which is the eye poke.  The eye can only be poked every 10 seconds, and the sound and animation sequence will play.  This will override the currently playing sequence.

 

The other special sequence is the scare sequence.  This is chosen at random in between randomly played sequences.

 

 

The Talking Mouth

 

def talk(self):
if(self.talking == 0): #determine when to talk again
self.mouthBlit = display.blit(self.mouth,(self.mouthX,self.mouthY))
if (time.time() - self.lastTalk >= self.talkDelay):
print("talk!" + "(" + str(self.talkDelay) + ")")
self.talking = 1
self.talkIndex = 0
self.talkDirection = 1
lastTalkSwap = time.time()
self.lastTalk = time.time()
else: #animate talking
if(time.time() - self.lastTalkSwap >= 0):
self.talkIndex = self.talkIndex + self.talkDirection
if(self.talkIndex >= len(self.mouthTalk)):
self.talkIndex = len(self.mouthTalk)-1
self.talkDirection = -1
if(self.talkIndex == 0 and self.talkDirection == -1):
## print("mouth reset")
self.talking = 0
self.talkDirection = 1
self.lasttalk = time.time()
## print("blink index:" + str(self.blinkIndex))
self.talkBlit = display.blit(self.mouthTalk[self.talkIndex],(self.mouthX,self.mouthY))
self.lastTalkSwap = time.time()
   

 

 

This is run the same way as the eyes.  An array of images is sequenced in order with a small delay between the blitting.  The array is played forwards, then backwards as one complete animation sequence.  Unlike the eye however, it does not stop there.  It continues to play the array back and forth until told to stop otherwise.

 

The variables in play here are:

talkDelay = The delay between talk blits, this is left small but can be configured.  I Emerpically determined a value that looked good for my images.

talkIndex = The current index of the image being blitted.  This cycles forwards and backwards through the mouth talking image array.

talkDirection = This is direction the talkIndex is moving.  it is 1 when moving forward, and -1 when moving backwards.  You can see on line 16 how this is set.

talkSwap - This last time the images were swapped, the time between swaps must be greater than or equal to talkDelay

 

 

 

The complete code thus far:

 

import os

import pygame,sys, time, random

from pygame.locals import *

 

#a generic function for loading a set of images from a given directory

def LoadImages(imagesPath):

imageList = list()

for dirname, dirnames, filenames in os.walk(imagesPath):

for filename in sorted(filenames):

try:

imageList.append( pygame.image.load(os.path.join(dirname, filename)))

except:

pass

return imageList



#a generic function for loading a set of sounds from a given directory

def LoadSounds(imagesPath):

soundList = list()

for dirname, dirnames, filenames in os.walk(imagesPath):

for filename in sorted(filenames):

try:

soundList.append( pygame.mixer.Sound(os.path.join(dirname, filename)))

except:

pass

return soundList

 

#define the face and sub classes, which is just used to keep all the images together that go together

 

 

 

 

class Mouth:

def __init__(self,path):

self.mouthTalk = LoadImages(os.path.join(path, 'mouth/talk/'))

print("Mouth images:" + str(len(self.mouthTalk)))

self.mouth = pygame.image.load(os.path.join(path, 'mouth/mouth.png'))

self.mouthX = 0

self.marginX = 20

self.marginY = 0;

self.mouthW = 480/2

self.mouthH = 350

self.mouthY = (480-350) / 2

self.lastTalk = time.time()

self.lastTalkSwap = time.time()

self.talkMin = 1

self.talkMax = 6

self.talkDelay =.03

self.talking = 0

self.talkIndex = 0

self.talkDirection = 1

self.leftPoking = 0

self.rightPoking = 0

self.lastPoke = 0

self.pokeStart = 0

self.mouthBlit = pygame.Rect(0,0,1,1)



def talk(self):

if(self.talking == 0): #determine when to talk again

self.mouthBlit = display.blit(self.mouth,(self.mouthX,self.mouthY))

if (time.time() - self.lastTalk >= self.talkDelay):

print("talk!" + "(" + str(self.talkDelay) + ")")

self.talking = 1

self.talkIndex = 0

self.talkDirection = 1

lastTalkSwap = time.time()

self.lastTalk = time.time()

else: #animate talking

if(time.time() - self.lastTalkSwap >= 0):

self.talkIndex = self.talkIndex + self.talkDirection

if(self.talkIndex >= len(self.mouthTalk)):

self.talkIndex = len(self.mouthTalk)-1

self.talkDirection = -1

if(self.talkIndex == 0 and self.talkDirection == -1):

## print("mouth reset")

self.talking = 0

self.talkDirection = 1

self.lasttalk = time.time()

## print("blink index:" + str(self.blinkIndex))

self.talkBlit = display.blit(self.mouthTalk[self.talkIndex],(self.mouthX,self.mouthY))

self.lastTalkSwap = time.time()

 

 

 

 

 

 

class Eyes:

def __init__(self,path):

self.leftEyeSquint = LoadImages(os.path.join(path, 'eye/left/squint/'))

self.leftEye = pygame.image.load(os.path.join(path, 'eye/left/eye.png'))

self.leftEyeBlink = LoadImages(os.path.join(path , 'eye/left/blink/'))

## self.rightEyeSquint = LoadImages(os.path.join(path , 'eye/right/squint/'))

## self.rightEyeBlink = LoadImages(os.path.join(path , 'eye/right/blink/'))

self.rightEyeSquint = list()

self.rightEyeBlink = list()

self.leftEyeX = 800/2-480/4;

self.leftEyeY = 0;

self.marginX = 20

self.marginY = 0;

self.leftEyeW = 480/2

self.leftEyeH = 480 / 4

self.rightEyeX = 800/2-480/4

self.rightEyeY = 480/2

self.rightEyeW = 20

self.rightEyeH = 20

self.lastBlink = time.time()

self.lastBlinkSwap = time.time()

self.blinkMin = 1

self.blinkMax = 6

self.blinkDelay = random.randint(self.blinkMin,self.blinkMax)

self.blinking = 0

self.blinkIndex = 0

self.blinkDirection = 1

self.leftPoking = 0

self.rightPoking = 0

self.lastPoke = time.time()

self.pokeStart = 0

self.leftEyeBlit = pygame.Rect(0,0,1,1)

self.rightEyeBlit = pygame.Rect(0,0,1,1)



def getBlink(self):

if(self.blinking == 0): #determine when to blink again

if(not self.leftPoking):

self.leftEyeBlit = display.blit(self.leftEye,(self.leftEyeX,self.leftEyeY))

if(not self.rightPoking):

self.rightEyeBlit = display.blit(self.rightEye,(self.rightEyeX,self.rightEyeY+self.marginX))

if (time.time() - self.lastBlink >= self.blinkDelay):

print("Blink!" + "(" + str(self.blinkDelay) + ")")

self.blinking = 1

self.blinkIndex = 0

self.blinkDirection = 1

lastBlinkSwap = time.time()

if(self.blinkDelay <= 2):

self.blinkMin = 4

else:

self.blinkMin = 1

if(self.blinkDelay >= 4):

self.blinkMax = 3

else:

self.blinkMax = 6

self.blinkDelay = random.randint(self.blinkMin,self.blinkMax)

self.lastBlink = time.time()

else: #animate blinking

if(time.time() - self.lastBlinkSwap >= .05):

self.blinkIndex = self.blinkIndex + self.blinkDirection

if(self.blinkIndex >= len(self.leftEyeBlink)):

self.blinkIndex = len(self.leftEyeBlink)-1

self.blinkDirection = -1

if(self.blinkIndex == 0 and self.blinkDirection == -1):

## print("reset")

self.blinking = 0

self.blinkDirection = 1

self.lastBlink = time.time()

## print("blink index:" + str(self.blinkIndex))

if(not self.leftPoking):

self.leftEyeBlit = display.blit(self.leftEyeBlink[self.blinkIndex],(self.leftEyeX,self.leftEyeY))

if(not self.rightPoking):

self.rightEyeBlit= display.blit(self.rightEyeBlink[self.blinkIndex],(self.rightEyeX,self.rightEyeY+self.marginX))

self.lastBlinkSwap = time.time()







def getPoking(self):

if(self.leftPoking):

if(self.pokeStart == 0):

self.leftEyeBlit = display.blit(self.leftEyeBlink[-1],(self.leftEyeX,self.leftEyeY))

self.lastPoke = time.time();

self.pokeStart = time.time();

else:

if(time.time() - self.pokeStart > 10):

self.leftPoking = 0

self.pokeStart = 0

self.leftEyeBlit = display.blit(self.leftEye,(self.leftEyeX,self.leftEyeY))



# elif(self.rightPoking):

# if(not pokeStart)

# self.rightEyeBlit = display.blit(self.rightEyeBlink[-1],(self.rightEyeX,self.rightEyeY))

# self.lastPoke = time.time();

# self.pokeStart = time.time();



class Face:

def __init__(self,path):

#load each component of the eyes using the generic image loading function



self.talkSounds = LoadSounds(os.path.join(path , 'sounds/talk'))

self.singSounds = LoadSounds(os.path.join(path , 'sounds/sing'))

self.scareSounds = LoadSounds(os.path.join(path , 'sounds/scare'))



#create the eyes class

self.eyes = Eyes(path)

self.mouth = Mouth(path)

#define vars for face



#emperically determined coordinates



self.mouthX = 20

self.mouthY = 150

self.mouthW = 200

self.mouthH = 200



def PrintInfo(self):

print str(len(self.eyes.leftEyeSquint)) + ' left squint images loaded'

print str(len(self.eyes.leftEyeBlink )) + ' left blink images loaded'

print str(len(self.eyes.rightEyeSquint )) + ' right squint images loaded'

print str(len(self.eyes.rightEyeBlink )) + ' right blink images loaded'

print str(len(self.mouthTalk )) + ' talk images loaded'

print str(len(self.talkSounds )) + ' talk sounds loaded'

print str(len(self.singSounds )) + ' sing sounds loaded'

print str(len(self.scareSounds )) + ' scare sounds loaded'

 

 





#main code here

pygame.init()

display = pygame.display.set_mode((800, 480),pygame.FULLSCREEN | pygame.HWSURFACE | pygame.DOUBLEBUF )

pygame.display.set_caption('Funny Pumpkin')

 

 

#Create a list of faces classes, this example only has 1 face, but multiple faces can be used

faces = list()

#load the default face

faces.append(Face('./faces/default/'))

 

#test the class

##faces[0].PrintInfo()

for i in range(len(faces[0].eyes.leftEyeBlink)):

faces[0].eyes.leftEyeBlink[i] = pygame.transform.scale(faces[0].eyes.leftEyeBlink[i], (faces[0].eyes.leftEyeW-faces[0].eyes.marginX,faces[0].eyes.leftEyeH-faces[0].eyes.marginY))

faces[0].eyes.leftEyeBlink[i] = pygame.transform.rotate(faces[0].eyes.leftEyeBlink[i],270)

faces[0].eyes.rightEyeBlink.append(pygame.transform.flip(faces[0].eyes.leftEyeBlink[i],False,True))

faces[0].eyes.leftEye = pygame.transform.scale(faces[0].eyes.leftEye, (faces[0].eyes.leftEyeW-faces[0].eyes.marginX,faces[0].eyes.leftEyeH-faces[0].eyes.marginY))

faces[0].eyes.leftEye = pygame.transform.rotate(faces[0].eyes.leftEye,270)

faces[0].eyes.rightEye = pygame.transform.flip(faces[0].eyes.leftEye,False,True)

 

for i in range(len(faces[0].mouth.mouthTalk)):

faces[0].mouth.mouthTalk[i] = pygame.transform.rotate(faces[0].mouth.mouthTalk[i],270)

faces[0].mouth.mouthTalk[i] = pygame.transform.scale(faces[0].mouth.mouthTalk[i], (faces[0].mouth.mouthW-faces[0].mouth.marginX,faces[0].mouth.mouthH-faces[0].mouth.marginY))

faces[0].mouth.mouth = pygame.transform.rotate(faces[0].mouth.mouth,270)

faces[0].mouth.mouth = pygame.transform.scale(faces[0].mouth.mouth, (faces[0].mouth.mouthW-faces[0].mouth.marginX,faces[0].mouth.mouthH-faces[0].mouth.marginY))

 

 

 

 

 

 

 

 

#global vars

FPS = 30

 

 

r = list()

 

 

#main game loop

i = 0

faces[0].eyes.getBlink()

while(1):

faces[0].eyes.getBlink()

faces[0].eyes.getPoking()

faces[0].mouth.talk()

for event in pygame.event.get():

if event.type == QUIT:

pygame.quit()

sys.exit()

elif event.type == KEYDOWN:

if event.key == K_ESCAPE:

pygame.quit()

sys.exit()

elif event.type == pygame.MOUSEBUTTONDOWN:

pos = pygame.mouse.get_pos()

if(faces[0].eyes.leftEyeBlit.collidepoint(pos)):

faces[0].eyes.leftPoking = 1

elif(faces[0].eyes.rightEyeBlit.collidepoint(pos)):

faces[0].eyes.rightPoking = 1;







pygame.display.update()

    

  • Sign in to reply
  • DAB
    DAB over 10 years ago

    Nice detailed post.

     

    DAB

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube