Pyrex demo for baypiggies

Drew's BayPIGgies Pyrex presentation

At the 2004/11/11 BayPiggies meeting, I did some Pyrex demos.

My first demo was how to build a minimal Pyrex module that has a Python statement and a C call.

Then I talked about using Pyrex to optimize a simple Python program.

Initial Python program

#!/usr/bin/python

from glob import glob
import itertools

class Bigrams:
    """original python version"""
    def __init__(self):
        self.pairs = {}

    def add(self,word):
        for i in range(len(word)-1):
            pair = word[i]+word[i+1]
            self.pairs[pair] = self.pairs.get(pair,0) + 1

    def freqs(self):
        """get list of (pair,freq)"""
        return self.pairs.items()

files = "/usr/include/*.h"

b = Bigrams()

for line in itertools.chain(*[file(x) for x in glob(files)]):
    for word in line.split():
        b.add(word)

freq_pair = [(v,k) for k,v in b.freqs()]
freq_pair.sort()
for fp in freq_pair[-8:]:
    print "%8d %s" % fp 
On my box, I get this list of the most popular bigrams:
% cat /proc/cpuinfo | egrep "vendor|MHz"
vendor_id       : AuthenticAMD
cpu MHz         : 1400.081
vendor_id       : AuthenticAMD
cpu MHz         : 1400.081

% time ./bigrams
   12210 te
   12486 fi
   13053 nt
   13267 ef
   13780 er
   13940 ne
   17057 de
   30403 in
./bigrams  4.19s user 0.07s system 92% cpu 4.604 total

Move to Pyrex

I can't wait 4 seconds for this stuff! So, I moved the Bigrams class into Pyrex and built an extension. That version runs in about 4.7sec. Pyrex translates (most) Python to C code that uses the Python API. Run times are usually unaffected. The big speedups come when you let Pyrex write some of your code in faster C by avoiding PyObjects; calling other C libs; etc.

Try a Pyrex language extension

Then, I changed "for i in range(len(word)-1):" to "for i from 0 <= i < len(word)-1:" to demo one of Pyrex's bonus features. That loop syntax isn't related to translating normal Python to C; nor is it related to calling C functions. The for/from Pyrex loop uses a plain C integer as the loop index, avoiding a few Python API calls per iteration. This gets me a bit of a speedup. With the Pyrex int loop, the program takes 3.7sec.

Rewrite the algorithm

Now I am ready to tinker with the algorithm. I rewrite the Bigrams class in Pyrex, using a fixed-size C buffer to store the frequency counts. This removes the Python dict access, and I am also now looping over characters in a C string.
cdef class Bigrams4:
    """pack results into a fixed array like a C programmer would"""
    cdef int pairs[65536]
    def __init__(self):
        for x in range(65536):
            self.pairs[x]=0

    def add(self,word):
        cdef char *cword
        cdef unsigned short pairidx
        cword = word

        for i from 0 <= i < len(word)-1:
            pairidx = (cword[i]<<8) + cword[i+1]
            self.pairs[pairidx] = self.pairs[pairidx] + 1

    def freqs(self):
        """get list of (pair,freq)"""
        letter_pairs = []
        for x in range(65536):
            if self.pairs[x]>0:
                letter_pairs.append(("%s%s" % (chr(x >> 8), chr(x & 0xff)),
                                     self.pairs[x]))
        return letter_pairs
Note 'cdef class', which means this is a new C type. Pyrex writes this C struct for the new type:
struct __pyx_obj_6bigram_Bigrams4 {
  PyObject_HEAD
  int (pairs[65536]);
};
My 'self.pairs' code, formerly an attribute lookup, is now a C struct member access:

Pyrex source:
self.pairs[pairidx] = self.pairs[pairidx] + 1
C emitted from the Pyrex compiler:
/* "/home/drewp/projects/digrams/bigram.pyx":51 */
    (((struct __pyx_obj_6bigram_Bigrams4 *)__pyx_v_self)->pairs[__pyx_v_pairidx]) = ((((struct __pyx_obj_6bigram_Bigrams4 *)__pyx_v_self)->pairs[__pyx_v_pairidx]) + 1);

This runs in 1.2 sec, which I felt was good enough for my demo. Note that my rewritten algorithm could also be ported to plain Python. That version takes 3.7 sec-- the C datatypes made the big difference here.

Swig version

I also did a swig version which runs in .68 sec. It's probably faster because it doesn't use a class at all, because the final packing is faster, or because it doesn't contain the error checks that Pyrex includes. I haven't investigated the exact reason.

Source

Get bigrams.tgz and run "python setup.py build_ext --inplace" to build the Pyrex extension (you'll need Pyrex installed). Run "make" to build the swig extension (you'll need swig).

Then, try "time bigrams" after setting line 29 to the class version you want to run. Edit line 25 to adjust which files that are scanned. The file "bigrams5" contains the fixed-array version written in Python, which I didn't want to have cluttering the original bigrams program.

These source files also contain a demo of calling uuid_generate from libuuid. Remove all the uuid code if you're on a platform that doesn't have that lib, I guess.

Sleeping Dog short film

Sleeping Dog short film

Sleeping Dog

For the Robotmedia film festival in Berkeley (archive.org link), I produced a short video with my girlfriend Kelsi and dog Micky. This is not Micky's first performance (but this one might have been).

Download the movie:

small 5.1M mpeg2 slow local copy
large 18M divx4 slow local copy

Production

Kelsi and I wrote the story over lunch on 2004/1/29. We shot that night from 19:29-21:58 and got 36min of footage. It's mostly shots of the dog resting. We didn't break any dishes while shooting, but I shattered the vase while I was packing up.

A theme of this festival was 'silent'. The entrants make silent movies and a live band accompanies them at the show. The point of our movie was to film a story that relied completely upon sound effects, and then to present all the sounds in the picture.

Editing

I cut the show in several hours with cuisine, an open-source editor. I delivered the picture early the next week, so the band could see it and prepare their music. I produced the title effects over two days on the week of the show.

Screenshot of cuisine's timeline interface editing this show (before I did the audio mix using the show audio)

Titles

The titles are generated by a Python program that I wrote for this show. It's about 160 lines of code. The program generates all the moves from equations in the code- any 'keyframing' came from trial and error adjustment of expressions like "5*sin(id*4)+(25*f)*sin(30)". I could see the graphics in real time, and see the composited show pretty quickly, too, so I went through hundreds of iterations.

titles.py has a mode where it can render larger than the output frame and then scale down, which is how I antialiased some of the effects.

titles.py - program to write DV files for all 11 title effects
pygamedv.py - module for starting up pygame and optionally resizing and sending the display to encodedv (part of the libdv project)

The fonts are all from Larabie Fonts. I didn't seem to have a program around that would preview a bunch of ttf files that weren't setup as X11 fonts, so I wrote one. The result is less than twice as long as this paragraph, thanks to pygame and SDL.

Music

As mentioned above, I worked on the movie in silence. After I set the timing, the band Legends & Deeds prepared the music and performed it live at the show. That was the first time I ever heard the soundtrack, and I think it's super. In the movie files above, you hear a recording right from the band's mixer during the live performance. At the end, I crossfade to a room mic to hear the audience.

Software used, beyond what's commonly distributed with Linux

  • dvgrab - move DV from camcorder to disk
  • libdv - DV decoding and encoding
  • cuisine - browse and edit footage, composite titles
  • ffmpeg - write VBR mpeg movies from DV
  • transcode - write divx movies from DV
I used mozilla to display my captured footage (screenshot), and the dv1394 kernel driver to output DV back to tape.

summermovies

summermovies

summermovies intro for Robotmedia

Fish I built a simple camera arm to get the slow camera move in the opening shot. It pivots by resting on the tripod's screw that would stick into a camera. The result was smooth enough that I could track the fish into the tank in about 10 keys over 500 frames. Finally, there's a bug in the conversion from the simulation rotations to the renderer's system, so the resulting fish rotation has problems (maybe gimbal lock, or maybe just a math error). The simulator looks great; the output rotation has lots of flips. Brawl I tried to make a shot where the crowd of people got faster and faster. blue at sierra; too bad the ground isn't blue crowd is carson high bulge in pov crack in pure povray bg blur and light shafts by ryan in AE Arch vid is old RM couldnt render roto by showtime py prog to make screen

Powered by Zope

image straightener

image straightener

The object is to auto-straighten an image. I have written some test code that tries to figure out the dominant angles in an image. A dominant angle near horizontal or vertical tells us that we should rotate the image by the right amount to make that angle *be* horiz/vert.

There's no production code yet. I think what's needed right now is a more precise way of estimating the dominant angle.

Get the code with anonymous CVS:

cvs -z3 -d :pserver:anonymous@bigasterisk.com:/srcmirror co straighten

ViewCVS is available

Some results. The graph is some popularity measure vs angle. The peak in the middle is near 90, but isn't exactly 90. I'd like to know, to perhaps .1 degree, what that peak angle is.
more files

A search based on the Hough transform may also be useful. I have not looked into how other auto-straighteners work at all. I think they just use the derivative method that I'm using.

Business card

Business card

You're probably holding my business card right now. The writing in the star is a perl program that outputs the entire card in postscript, including the star of code. Click the image above for a bigger, almost legible version, or get the postscript version

The condensed code is here. This is the version that you see on the card. It's about 1945 characters long. I did not attempt to make the code as short as possible. Whenever it was getting too long for the shape, I'd adjust the shape parameters and compress the code a little more.

The verbose code is here. This version first strips itself to become the condensed version, and then outputs the postscript. The condensed and verbose versions output the same postscript. The full version is about 4241 characters (152 lines) long.

You might be interested in these pages about quines:

Now go look at my home page!

Using the parallel port with Linux

Using the parallel port with Linux

Using the parallel port for output with Linux

by Drew Perttula
Here's a tiny program for Linux that turns the parallel port's data lines on and off. parcon.c

You say:

> parcon 1h 2h 3l

old = 00000000
new = 00000011

to set the first and second lines high and the third low.

Here's an executable for Intel Linux systems. The program needs to be run as root. Either make yourself root before running it, or (as root)

chown root parcon
chmod u+s parcon
This turns on the setuid bit for the program, so it gets root privileges even when other users run it.

Here's a simple interface to parcon, written in Tcl/tk. You can click on the numbers or type them on the keyboard to toggle the states of the output lines. tkparcon

Want to use the parallel port from python? You might like to check out some SWIG modules I used on a project. Check out the whole project with:

cvs -z3 -d :pserver:anonymous@bigasterisk.com:/srcmirror co light9
The file light9/light8/parport.i is a swig wrapper for parport.c which I used on a recent project. It's messy and specific to the particular hardware I was controlling, but it contains at least what you'd need. There's a makefile too. (As well as entire functioning theater lighting control system :)


Home page

The button in my car

The button in my car


The button, outside of the car.

The button looked nice in the car, but then someone broke my window and snapped the button off. I haven't glued it back yet.

Powered by Zope

Compositing reel

Compositing Reel

Drew Perttula

October 2001 Demo Reel Notes

DivX demo reel (26MB) available, though you'll have to ask me to post a link to the current location, since it has moved around.

My tools are After Effects and Photoshop by Adobe; Rayz by Silicon Grail; Blender by NaN; POV-Ray, Gimp, and various scripting languages.


00:09s
God with flamethrower

00:15s
The interactive effects are an animated color-correction and a noise distortion field near the center of the image-- both Rayz effects.

00:17s
Original particle system written in Perl. 50 particles emit from a moving source position and direction. Particles accelerate upward and are recycled after they reach a certain distance. Rendered as spheres with POV-Ray.

00:18s
Final POV-Ray render using a halo effect with turbulence inside each sphere. Additional glow added later with Rayz.


00:28s
Church explosion

00:31s
Pew model by Harlan Hile using Blender. I exported the Blender object to POV-Ray for rendering.

00:36s
Pew animation written in POV-Ray's own scripting language. Pews and floor rendered in POV-Ray.

Z-buffer rendered as a separate pass with a gradient texture on all objects.


00:40s
Explosion is a POV-Ray sphere with a turbulent halo interior. I animated the threshold between opaque orange and transparent to increase the volume of the explosion over time.

I used Rayz to mask the explosion to an animated depth using the pew element Z-buffer.


00:43s
Traveling light pass from POV-Ray. Glow enhanced with Rayz.

00:49s
Dust element pulled from video shot at a ranch in Martinez; stabilized with Rayz. I replicated the usable dust area and masked it with another animated depth matte.

00:55s
To add authentic camera shake, I actually shook a real camera and tracked the result with Rayz. Then I applied the shake to the church shot (in the same slow motion as the rest of the shot).


01:06s
Amphitheater

01:10s
I have two friends who I can convince to stand outside the Sierra Spring Water building in Emeryville in various outfits. I'm the one in black at the end.

01:25s
Rayz contains Ultimatte, which I used on the 12 "cels" to create about 3000 frames of people.

01:27s
A custom crowd placer Perl script reads a bitmap of person locations (shown here). The script drives the Gimp to scale and place the individual-person frames at the right locations. Scaling is simply a function of y-position. Each person instance has its own counter and framerate.

01:29s
The placer script placed people on a large panorama, but for efficiency, it didn't include people very far outside of the current field of view. The actual panning and zooming was done with Rayz.


01:38s

Tower climb

Simple composite done with After Effects. The foreground element had to be tracked to undo a camera move.


01:50s

Neptune - ship flyby

A multi-layer effect done with POV-Ray (for the 3D work) and After Effects (for the compositing). The dog was composited over the cockpit, whose front window was replaced with a moving starfield. That result was mapped onto a surface inside the ship's window.


01:50s

Neptune - lever pull

After Effects combines a background miniature; a 3D lever created in POV-Ray; and a roto'd dog element. I adjusted the timing of the lever pull to match the dog's movement.


01:55s

Neptune - ship hit

Some simple After Effects lightning and camera shake effects, plus a roto'd dog animated over the scene.


2:05s

Real-time foreground over time-lapse scene

The background uses a custom video capture program I wrote that accumulates frames and saves a motion-blurred, time-lapse animation directly. The foreground (me) was difference-matted from its background using Rayz and placed over the time-lapse animation.

Powered by Zope

Toilet paper

Toilet Paper
Harlan Hile and Drew Perttula

University of California, Berkeley

10/16/2000

Introduction
This whole project began simply as a remodeling project, to repaint and re-floor a water closet. As time went on, it was decided that there was no reason to stop there, and we sought to bring the room into the high-tech realm. Although documentation on using Python in such an environment was scarce, we believe it was the right choice for the project.

Since then, the water closet has finally advanced to the

and the computer running it have developed into a very unusual use of Python.

The main objective of this project is to have fun, and create a smart, interactive, mysterious, and amusing bathroom going experience. However, it can also be viewed at as an experiment in home automation, artificial intelligence, and user interfaces.
 

Technology Overview

An inexpensive IBM 486 computer called toilet has been "installed" in the water closet alongside the real toilet. Toilet runs Slackware Linux and contains a sound card with joystick port, an ethernet card, standard parallel and serial ports, and a CD-ROM drive. The case is on the floor with a special cover (this is an especially adverse environment), and a mini keyboard is mounted on the wall.

Most data is acquired through the joystick port of the sound card, which gives 4 analog values (the axes) and 4 digital on/off values (the buttons). A few input bits of the parallel port are also used to read digital on/off values. See the schematic for a complete list of inputs and outputs.


 
How Python Fits in
 
We chose Python for its incredible prototyping speed and ease of extensibility. The interfaces to the parallel port, joystick port, and CD-ROM are custom C modules, for example. Older versions of the toilet software called separate programs from a script and parsed their output. Under Python's module system, our scripts are shorter, clearer, and faster.

Toilet also makes use of various modules written by others. In order to get news headlines from the web, it has httplib fetch selected web pages and the re module parse the headlines out of them. Once it has these headlines, it scrolls them across the LCD screen using termiosmodule. The sound output system uses the linuxaudiodev and wave modules to play .wav sounds. Toilet also uses a DB-API module to store history into a database and socket to listen for requests from other processes.

 
Python's threading is also used to allow sequences of events to execute, while input data is still polled, and locks are used to synchronize threads.
What it Does

When a motion sensor detects a visitor in the hallway outside, toilet picks from a list of sounds to play to encourage a visitor to enter. When Toilet has a visitor, it picks an appropriate track to play from the CD based on things like the seat positions, the laser beam across the seat being broken, the light switch being off, etc. It scrolls news headlines across the LCD panel. When the toilet is flushed, the lights flash and the music fades. If the seat is left up when the door is opened, a reminder sound plays. Toilet also serves web pages to the outside that allow us to view history from the database, request specific CD tracks, control the volume, and send messages to the LCD screen.

Conclusion

Toilet has been in development for nearly two years. We have used Python for about 3 months, during which time the development has gone significantly faster.

With the available sensors, and easy control and manipulation through Python, Toilet can be expected to do many more intelligent and entertaining things.