|Perl: the Markov chain saw|
"Build a better mousetrap and the world will beat a path to your door." -- attributed to Ralph Waldo Emerson
The problem -- we had mice in our basement.
We'd seen one mouse, and didn't know if there were more. When the mouse came upstairs briefly, and scared my 4-year-old, I knew it was time to take action.
I bought some old-fashioned traps (with the metal spring), and according to the hardware store salesman's advice, put it inside a box (so they wouldn't approach it from behind, and possibly avoid getting caught).
Within a few days, four traps had disappeared! (one of which was a "glue trap"). I didn't know if it was mice taking them, or some larger animal. Had they sprung the traps, or gotten stuck and dragged them away? My curiosity started me thinking, "is there a way I can monitor the traps with Perl?"
The result is the program described here, called "MouseCam".
My requirements for a Perl solution were straightforward.
First, I needed a program (hopefully freeware) to save images to a laptop PC. (There may be some way to do the same thing with Perl, but I don't know what it is).
I would then write a Perl program that in server mode would deliver those images over the wireless LAN connection.
The client would fetch images once every second, save them all to disk, and display them as often as required.
Mice are nocturnal, so at night when they would most likely be out, nobody would be monitoring, and saving the images would be the only real important action (displaying would not be necessary). However, the client would have to have a "purge" capability, so as to automatically delete images after a maximum number had accrued, so as to avoid ever filling up the disk).
Finally, the program would need to provide a playback capability.
The freeware image-capture program is called "Fwink" (available here)
It conveniently lets the user save images at a given interval (which in my case is always set to maximum frequency -- 1 second).
It might seem a small thing, but the fact that it saves each image to the same filename means that there's never a danger of using up disk space.
With some experimentation, I've found the best "Text Effect" setting is to have both date and time at the top of the image, with a text size of 28 pixels, and "None" for the "Background Style".
Fwink has worked equally well with both cameras I've used, a Creative, and a Logitech Quickcam Pro 4000.
As the program evolved, several "nice-to-have" features became evident.
Being able to monitor one remote camera was quite impressive, so I considered it would be even nicer to monitor up to four at once.
After playing with the opening "graphic" title screen, and creating some algorithms that nicely compressed it into a much smaller space, I got the idea of extending the idea (primarily to handle more than just 2 colors), and creating icons for each of the playback controls.
One common Perl/Tk control that I haven't had as much need for as much as the others is the "Scale" object. In this project, I had the perfect chance to implement two of them; one for controlling the exact image, and the other for controlling the playback speed.
During the daytime, though, it would be nice to be able to monitor progress, at longer intervals (eg. once per minute uploads to a webservers).
Another thing I may sometime try to incorporate is an "image-detection" feature. A couple of times, I've found places during playback where an insect or ladybug walked into camera range, and it would be fun to see if it's possible to reliably track such occurrences.
Once the camera was in place, I got luckier with the mousetraps.
After programming till late in the night, and much of the next day, I got to see the first mouse caught; observing how it approached the trap, lured by the smell of the peanut butter.
Altogether 6 mice were caught in the traps, 4 of which showed up in the "MouseCam" images. The 2 which weren't recorded were because of human error (mine). One of them was after a couple of days had passed, and I mistakenly concluded there were no more mice. The other was because I was working on the program, and didn't expect any mice so early in the evening.
The more mice that were caught, the warier the mice became. The 6th mouse approached the trap 5 times and appeared to either carefully lick the bait, or at least sniff at it, before finally getting caught on the 6th visit.
Writing "MouseCam" taught me some interesting things.
Unlike many Perl programs, this one has to function efficiently in realtime.
One of the most annoying problems with my program was that it couldn't go for very long without image distortion appearing. Sometimes the image would only be off by a few pixels, sometimes most of the image was "skewed". Often the image would have a partial discoloration, for example, it might be red starting part way through the picture. And on occasion, half of the image or more would be missing.
I was mistaken in thinking the image distortion was caused by failure in transferring over the wireless LAN. I finally realized it was due to my trying to read the image while another image was overwriting it (via "Fwink"). Once I debugged the problem and found that I could reliably save a copy of the image as soon as the modification time had changed, I never had another distortion problem again.
One major feature that I added late in the game was threads, which I was *sure* would speed up the image transfer. I thought if the GUI could concentrate on rendering the image locally, and let a worker thread handle the socket transfers, everything would be more responsive.
Surprisingly, this wasn't so -- if anything the latency was worse, or at least the same -- and I soon went back to the original model for the sake of simplicity.
A while back, I had written some Perl code to read XPM images, so this program was a chance to further refine the algorithm for doing image compression for the initial "splash screen". It was so much fun working on those algorithms that I extended them to handle multiple colors (up to 7), and created graphic "playback" icons.
In conclusion, I never did figure out how or why the mouse took the mousetraps.
Someday, I'm sure I'll find some of them (our basement is still filled with many boxes from our last move).
For now the mice seem to have all been caught. The next time there are any signs of them in our basement, though, I'll be ready.
There would seem to be lots of potential uses for the MouseCam program. It could be an inexpensive solution to a number of things; you only need a minimum of two computers (one of them a laptop), with Perl on both, and a wireless LAN, and voila, you've got yourself a home-surveillance system!
The program is available here. I would post it here (in fact, I did try), but it apparently exceeds the maximum posting length before I realized it.
Update: I've fixed some bugs, and added some improvements to the code, which I will continue to make available at the same location as above. It wasn't until I ran it on a new machine that I realized there was an error in the definition of the "starting graphic" (since it's only saved as an XPM once, and thereafter read from disk). It should now work in "client mode" on both Windows and Linux. (In "server mode", it currently only runs on Windows).