Syllabus 20091117

From XPUB & Lens-Based wiki


On this day Megan was in the HotSeat, we visited Python's IF-THEN statement, and talked a little about how to smooth out those noisy live data sources.

IF-THEN Minimalist VJ

Code that responds to z, x, c and the i key!

In the code you see the following inside the "Event handling" code:

red = event.type == pygame.KEYDOWN


This assigns to the variable ("red" in this case), a True or False value reflecting "is the type of the current event a "KEYDOWN" -- so that means when it's key is pressed (KEYDOWN) red with get set to True, and when it's released (KEYUP), it will get set to False.

The full code:

import pygame, sys

pygame.init()
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()

(red, green, blue) = (False, False, False)
square = False

while True:
    # 1. PROCESS EVENTS
    for event in pygame.event.get():
        if event.type==pygame.QUIT or \
        (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
            sys.exit()
        elif event.type == pygame.KEYDOWN and event.key == pygame.K_f:
            # http://www.pygame.org/docs/ref/display.html#pygame.display.toggle_fullscreen
            pygame.display.toggle_fullscreen()
        elif event.type == pygame.KEYDOWN or event.type == pygame.KEYUP:
            if event.key == pygame.K_z:
                red = event.type == pygame.KEYDOWN
            if event.key == pygame.K_x:
                green = event.type == pygame.KEYDOWN
            if event.key == pygame.K_c:
                blue = event.type == pygame.KEYDOWN
            if event.key == pygame.K_i:
                square = event.type == pygame.KEYDOWN

    # 2. DRAW THE SCREEN!
    if red:
        a = 255
    else:
        a = 0

    if green:
        b = 255
    else:
        b = 0

    if blue:
        c = 255
    else:
        c = 0

    screen.fill((a, b, c))

    if square:
        pygame.draw.rect(screen, (0, 0, 0), (100, 100, 640-200, 480-200))

    pygame.display.update()
    clock.tick(30)


Smoothing Live Data

To start consider this very simple version, the rectangle (which gets drawn at position rx & ry), just jumps straight to the mouse (it looks at the difference in position between it and the mouse, and adds the full amount). This is just a slightly indirect way of directly setting the position, as in:

rx = mx


By saying:

rx = rx + (mx - rx)


or more compactly using the plus-equals operator:

rx += (mx-rx)


import pygame, sys

pygame.init()
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()

(rx, ry) = (0, 0)

while True:
    # PROCESS EVENTS
    for event in pygame.event.get():
        if event.type==pygame.QUIT or \
        (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
            sys.exit()
        elif event.type == pygame.KEYDOWN and event.key == pygame.K_f:
            # http://www.pygame.org/docs/ref/display.html#pygame.display.toggle_fullscreen
            pygame.display.toggle_fullscreen()

    mx, my = pygame.mouse.get_pos()

    # direct mapping
    rx += (mx - rx)
    ry += (my - ry)

    screen.fill((0, 0, 0))
    pygame.draw.rect(screen, (0, 0, 255), (rx, ry, 60, 60))
    pygame.display.update()
    clock.tick(30)


Now, to introduce a "chase" behavior, it's simple a question of only going part of the way. So instead of adding the full difference, we add a fraction of it -- in this case a quarter (0.25):

import pygame, sys

pygame.init()
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()

(rx, ry) = (0, 0)

while True:
    # PROCESS EVENTS
    for event in pygame.event.get():
        if event.type==pygame.QUIT or \
        (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
            sys.exit()
        elif event.type == pygame.KEYDOWN and event.key == pygame.K_f:
            # http://www.pygame.org/docs/ref/display.html#pygame.display.toggle_fullscreen
            pygame.display.toggle_fullscreen()

    mx, my = pygame.mouse.get_pos()

    # dampened feedback
    rx += 0.25 * (mx - rx)
    ry += 0.25 * (my - ry)

    screen.fill((0, 0, 0))
    pygame.draw.rect(screen, (0, 0, 255), (rx, ry, 60, 60))
    pygame.display.update()
    clock.tick(30)


Finally, we can add a bit of "momentum" by mixing in the previous dx/dy value: A subtle point in this code, by defining dx (and then adding it in the next step), this allows the right-hand side of the assignment to make use of dx's current (that is, the value it had in the last frame, and before we change it).

dx = (0.9 * dx) + (0.25 * (mx - rx))
rx += dx


The following does the same thing but is maybe easier to read:

old_dx = dx
dx = (mx - rx)
rx += (0.9 * old_dx) + (0.25 * dx)


By playing with the number ("slider") applied to old_dx (0.9 in this case), you can change how "heavy" the object is (by making it use more or less of it's old movement value).

import pygame, sys

pygame.init()
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()

(rx, ry) = (0, 0)
(dx, dy) = (0, 0)

while True:
    # PROCESS EVENTS
    for event in pygame.event.get():
        if event.type==pygame.QUIT or \
        (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
            sys.exit()
        elif event.type == pygame.KEYDOWN and event.key == pygame.K_f:
            # http://www.pygame.org/docs/ref/display.html#pygame.display.toggle_fullscreen
            pygame.display.toggle_fullscreen()

    mx, my = pygame.mouse.get_pos()

    # WITH "HISTORY"
    dx = (0.9 * dx) + (0.25 * (mx - rx))
    rx += dx

    dy = (0.9 * dy) + (0.25 * (my - ry))
    ry += dy

    screen.fill((0, 0, 0))
    pygame.draw.rect(screen, (0, 0, 255), (rx, ry, 60, 60))
    pygame.display.update()
    clock.tick(30)


daemon off
quiet on

# You may very well need to change this (check with 'dmesg'
# after plugging in your webcam).
videodevice /dev/video1

# Image size in pixels (valid range is camera dependent).
width 352
height 288
# width 640
# height 480

framerate 5
quality 85
auto_brightness off

# General threshold level and noise threshold
# level (for distinguishing between noise and motion).
threshold 4500
noise_level 64

# Initial brightness, contrast, hue (NTSC), and saturation.
# 0 = disabled (valid range 0-255).
brightness 0
contrast 0
saturation 0
hue 0

# Encode movies in real-time (install ffmpeg before enabling).
ffmpeg_cap_new off

# Codec to be used by ffmpeg for the video compression.
# Supported formats: mpeg4, msmpeg4.
ffmpeg_video_codec msmpeg4

# Target base directory for pictures and films (you may need
# to change this (or change its permissions) depending on
# which system user runs motion).
# target_dir /var/lib/motion/snapshots
target_dir images
output_motion	off
output_normal	on
#locate on
# on_motion_detected echo motion %K %L %i %J %D
# on_picture_save echo image %K %L %i %J %D
on_picture_save echo image %K %L %i %J %D %f

# FROM THE MOTION DOCS:
#  %i Width of the rectangle containing the motion pixels (the rectangle that is shown on the image when locate is on).
#  %J Height of the rectangle containing the motion pixels (the rectangle that is shown on the image when locate is on).
#  %K X coordinate in pixels of the center point of motion. Origin is upper left corner.
#  %L Y coordinate in pixels of the center point of motion. Origin is upper left corner and number is positive moving downwards (I may change this soon).
#  %D Number of pixels detected as Motion. If labelling is enabled the number is the number of pixels in the largest labelled motion area.