One of the (too many) things I do is manage the website for the Westerville Community Bands
(www.westervillebands.org). I needed a "summer" themed background picture for the site. I had a nice photo of the band playing at the Alumn Creek Amphitheater in Westerville, but it was a bit too busy for a background. I wanted something more abstract.
So I wrote a quick-and-dirty program to "paint" a picture by drawing a 45 degree stroke up and to the left. It worked by choosing the pixel under the current location and moving up one and over one until the pixel wasn't "close enough" in color to the original color or we were at the edge of the picture. Then it would draw a line from the start location to the end location using the original color. Repeat ad nauseam. Of course the original algorithm had a math error that caused strokes to get too long the further right you got in the picture...but I did figure that out eventually.
For the last year or so I've been playing around with the algorithm in between other activities. Below are some images of the results.
NOTE: you'll need to view these full-size on a fairly big screen. They look pretty much like regular photographs when they're shrunk down.
Except for this one...I had the "abstraction" level cranked up pretty high.
 |
| Olivia Wilde |
The current version is based on seed fill regions - the "paint bucket" tool in most graphics applications.
The basic outline of the code is:
- Do a 3x3 "blur" operation on the image to smooth out any noise and coincidentally smooth out the edges of regions.
- Check points every 10 pixels (or so) and see if the colors around that point are pretty close to each other and that it's not part of a seed fill region. If so then start a seed fill region.
- Go back through every point on the image and if it's not part of a seed fill region start a seed fill region there.
- Merge any regions that are smaller than some size (usually between 5 - 25 pixels) with the neighboring region that has the closest average color until all regions are above the minimum size.
- Go back through the image merging adjacent regions who's average color is "close enough" to each other. Continue to do this until no more regions are merged.
- Figure out the slope of each region. For wide regions use the top edge or bottom edge; whichever one is not horizontal. For tall regions use the left edge unless it's vertical.
- Create a stroke for every line segment that intersects the region by going down and across. Pick the median color of the points that intersect the region from the original image, not the blurred image. The median color is used to minimize getting a streak of the wrong color across the image.
- Create the output canvas.
- Fill the regions with the average region color, possibly passed through a color transforming function. The filling is used because I'm using anti-aliased line drawing and with a white background you get a lot of streaks. I have also just painted everything twice, but filling works a bit better.
- Draw the strokes on the canvas, possibly passed through a color transforming function.
Of course there was a lot of playing around to figure out what worked. There was a lot of code that got thrown away because it didn't work or the time it took to run didn't make an appreciable difference in the output.
 |
| Mountains in the Fall |
And once again I learned that you really need to profile the code before optimizing. A couple of "optimizations" that I made, really slowed down the processing.
I also added the ability to manipulate the output colors, though none of these pictures are using it.
 |
| Asters in the Garden |
Now all I have to do is build the robot to actually paint the picture....of course they do make them; they're called ink-jet printers...
 |
| Mountain Scene |
 |
| Neuschwanstein Castle |