Personal tools
You are here: Home Mediatechnology Courses Embodied Vision Edges of Change
Document Actions

Edges of Change

by Joris Slob last modified 2007-05-26 21:12

This piece shows how virtual ants show us windows of the world where there is change.

Click here to get the file

Size 10.5 kB - File type text/python-source

File 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
« September 2010 »
Su Mo Tu We Th Fr Sa
1234
567891011
12131415161718
19202122232425
2627282930
 

Powered by Plone CMS, the Open Source Content Management System

This site conforms to the following standards: