Edges of Change
This piece shows how virtual ants show us windows of the world where there is change.
Size 10.5 kB - File type text/python-sourceFile contents
"""
Edges of Change
Embodied Vision 2007
a course by Joost Rekveld
Coded by
Joris Slob and Sonja van Kerkhoff
This piece shows how virtual ants show us
windows of the world where there is change.
NOTE: The webcam library assumes that no other proram is using the webcam.
If another process is using the webcam, you will get a "Webcam failure" in
you output screen. Close the program that is using the webcam and start over.
If you start this program from IDLE and later stop the program, the webcam will
still appear to be locked. Close all pythonw.exe processes and start over.
NOTE: VideoCapture is a Windows only library.
"""
# --- IMPORTS ---
#
# Tkinter - For window management
# random - For the starting position and movement of ants
# PIL - For the manipulation of images
# VideoCapture - To get images from a webcam device
from Tkinter import *
from random import randrange
from PIL import Image, ImageTk
from VideoCapture import Device
TIME_DELAY = 1 # Time delay to allow window events to happen (keep minimal)
# Don't put this to 0, it will not redraw the window
POPULATION = 5000 # The amount of ants in the world
BOX_SIZE = 1 # The BOXSIZE times 2 plus 1 is the number of pixels of one edge
# of the square.
#
# 3 3
# ---|---
# | | 3
# - o -
# | | 3
# ---|---
#
# Here BOX_SIZE was set to 3, this means that from the mid-point
# of the box there are 3 pixels in every direction, making a total
# of 3+1+3 * 3+1+3 = 49 pixels
DECAY = 1 # This detemines the fade rate of the output pixels
def subtract_or_zero(num, amount):
""" Utility function for lowering a value with the ammount value, but also
makes sure that it doesn't go below 0.
"""
return max(num - amount, 0)
def image_subtract(num):
""" Utility function for lowering the values of all the pixels of an image
by DECAY.
"""
return subtract_or_zero(num, DECAY)
class Ant():
""" This is the ant class. It describes the functionality of a single ant.
Once the ants are initialised they only move one step each time the
move() function is called.
Ants interact only with the input image. They react if there's a big
enough difference (determined by luminosity) in its perceived colour
values. They react by making a window (or BOX) of the webcam image
visible in the output.
"""
def __init__(self, image):
""" Initialisation of a single ant. This function is called when a new
ant object is made. Every ant should be given the input image, so
they can determine the limits (image size) of where they can start.
"""
self.size = image.size # Determine the size of the image that the ant
# will live in. The size property is determined
# by the image extracted from the webcam.
self.image = image # Ants store their own copy of the input image to
# extract information out of it.
self.position = [randrange(0, self.size[0]), # Randomly pick a starting
randrange(0, self.size[1])] # position for the ant.
self.memory = [0,0,0] # Previously perceived pixel colour goes in here
def move(self, output):
""" This function makes the ant do one move, and has all the logic in
it to react to the input image and to draw on the output image. The
ant will need the output image to write on.
"""
change = 0 # Perceived change of colour, will be calculated later
lumen = 0 # Luminosity of the colour of the memory,
# will be calculated later
# Get the current perceived pixel colour for this ant
vision = self.image.getpixel((self.position[0],self.position[1]))
for band in range(len(vision)): # Go through the RGB values (bands)
change += abs(self.memory[band]-vision[band]) # Calculate difference
# between memory and
# vision
lumen += self.memory[band] # Calculate the luminosity of the
# memory colour
if change > lumen: # This is a way to avoid painting dark pixels
# Here the box around the position is calculated, for explanation
# see the BOX_SIZE comment above. Box is a list of the upper left
# and lower right corners
box = (self.position[0]-BOX_SIZE, self.position[1]-BOX_SIZE,
self.position[0]+BOX_SIZE, self.position[1]+BOX_SIZE)
window = self.image.copy().crop(box) # Gets a 'cropped window' from
# the input image ...
output.paste(window, box) # ... and show it on the output
deltax = randrange(-1,2) # Determines a random direction to move to.
deltay = randrange(-1,2) # This can be in the nine squares in the ants
# vicinity
# Now we have to make sure the ants don't walk off the screen, so we
# use the % operator (modulo) to make sure that they stay within the
# (image) size limits.
newx = (self.position[0] + deltax) % self.size[0]
newy = (self.position[1] + deltay) % self.size[1]
newpos = [newx, newy] # These are the new x, y positions the ant will
# move to
self.position = newpos # Here the ant gets the new position
for a in range(len(vision)): # Go through the colour bands
self.memory[a] = vision[a] # Update the ant's memory with the new
# vision values
return output
def set_image(self, image):
""" The ant needs updates of the webcam image.
"""
self.image = image
class MyWebcamApp:
""" This class has all the functionality in it for the application to work.
It has the information about the webcam, the ants and the output window
"""
def __init__(self):
""" Initialises the program. First a new window is created. Next the
webcam functionality is set up and tested. Ants will be created and
given random locations and a memory set to black. Next the window
will be made ready for output.
"""
self.root = Tk() # Tk does all the work for setting up a window
succeed = False # Variable to check if we have the webcam set up yet
while(not succeed): # As long as we have no webcam set up
try:
self.webcam = Device() # VideoCapture.Device() tries to
# establish a connection with the webcam
self.img = self.webcam.getImage() # Get a frame from the webcam
# We have to convert the frame to a format that Tk can show
# using the ImageTk library
self.imagedata = ImageTk.PhotoImage(self.img)
succeed = True # If all this works, we have succeeded
except:
print "Webcam failure" # Otherwise, show something is wrong
# Ant initialisation
self.myAnts = [] # We begin with a placeholder for the ants
for i in range(POPULATION): # We then fill the placeholder
self.myAnts.append(Ant(self.img)) # With ants which are given the
# current webcam image
""" This is a good place to change code if you want to.
* The first line (which is now commented out) will start the output
image with the initial input image
* The second line will start the output as black.
"""
# self.output = self.img.copy()
self.output = Image.new('RGB', self.img.size)
# We convert the output image to a format that Tk can show
self.imagedata = ImageTk.PhotoImage(self.output)
# Here we put the image into a label object, which is just a
# placeholder in your window
self.label = Label(self.root, image=self.imagedata)
# The pack() function tells the window that we have every element ready,
# so it can actually draw the window and fill it.
self.label.pack()
# We need to make sure the program will do allow the ants to do a move
# with each cycle. This call starts the whole cycle.
self.update()
self.root.mainloop() # Allow the window to react to events (such as the
# user pressing the 'close' button)
def redraw(self):
""" Redraw the picture currently displayed
This is done by reconfiguring the label to show the new imagedata
"""
self.label.config(image=self.imagedata)
def update(self):
""" This function handles the real activity of each cycle. At the
end it calls itself to make sure this keeps running.
This function will - get a new webcam image,
- give this new image to the ants
- allow the ants to do their move
- do a redraw of the output of all the ants
"""
# Make sure that the whole scent/output image decays slowly
self.output = Image.eval(self.output, image_subtract)
self.img = self.webcam.getImage() # Do a webcam update
for ant in self.myAnts: # This runs through all the ants
ant.set_image(self.img) # Give the image to the ant
self.output = ant.move(self.output) # After the ant move update the
# output
# Convert to the Tk format
self.imagedata = ImageTk.PhotoImage(self.output)
self.redraw()
self.root.after(TIME_DELAY, self.update) # This makes sure the
# program keeps updating
MyApp = MyWebcamApp() # Let there be a working program,
# And there was a working program,
# And the computer saw that it was working
Click here to get the file