CS798 Assignment 1: Painterly rendering

Due date: Friday, January 25th, 2008


Painterly rendering algorithms create an artistic rendering from a source image (usually a photograph). You are to implement one such algorithm, namely the one described by Aaron Hertzmann in "Painterly Rendering with Curved Brush Strokes of Multiple Sizes", from the 1998 SIGGRAPH conference. A sample rendering from the paper is shown above. The algorithm covers a canvas in layers of strokes of decreasing radius, applying strokes where the canvas differs sufficiently from the reference image. Long strokes can be generated that follow curves of nearly constant colour in the image.


You should start by reading the paper, available from this page. You'll notice that the paper contains pretty good pseudocode for the core algorithms -- the pseudocode translates readily into a real program.

The next step is to construct an implementation of the algorithm (see below for some notes regarding the implementation). You are required to implement the following aspects of the paper.

Next, you must implement at least one nontrivial extension to your algorithm. This part of the assignment is open-ended, and there are many possible extensions. Ideally, a "non-trivial" extension is one that produces a noticeable, qualitative change to the drawings produced by your system. You should be able to show side-by-side images with and without your extension and convince a stranger that your extension is doing something.

What follows is a short list of suggestions for extensions. You are free (indeed, encouraged) to dream up other ideas. If you're unsure about your idea, or need more guidance, come talk to me.

This part of the assignment is not intended to be overwhelming; it's just a way to get you thinking about what ideas might follow on from the paper. Don't feel you have to wear your fingers down to nubs trying to implement your extension. A proof-of-concept will suffice.

The final step is to produce painterly renderings using your program. You can use any photographs you like; I recommend Philip Greenspun's collection and Freefoto. What's important is that your renderings should clearly demonstrate the features you implemented. At least one image should be rendered a few times with different parameters (see Section 3.1 of the paper) to produce different artistic effects.

What to submit

You need to produce a short write-up describing your implementation and showcasing the paintings you created. Your write-up can either be a PDF document or a web page. Your submission should not contain more than about three or four pages of text, though you're welcome to make it longer by including lots of pictures.

I would prefer for you to make your submission available on the web and mail me a URL by the deadline. If you would prefer not to do that, mail me the PDF or an archive of the web page as an attachment.

You are free to structure your submission as you desire. But it should at least include the following:

You're welcome to include other comments and observations. In particular, I welcome your comments on the strengths and weaknesses of this paper and painterly rendering in general. Is it useful, or a clever hack? I'm also interested in ideas for future work.

By default, I'm not going to look at your source code. But I reserve the right to request it as part of marking if it sounds from your description like there's something worth taking a look at. I also reserve the right to request a live demonstration; this could be important if you create an especially nice interface or a realtime version of the algorithm.

Implementation notes

You're free to construct your implementation in whatever way you like, as long as it can produce the desired final renderings. But for the purposes of this sort of work, not all languages and libraries are created equal. There are a few things you'll need to be able to do with images.

You're encouraged to find implementations of these algorithms out in the world and incorporate them into your program. You are only responsible for the core algorithms described in the paper. If you're not sure about whether it's okay to use some piece of code, ask me.

You don't have to follow the details of the paper exactly. What's important is obtain and render a similar set of strokes. So, for example, you don't need to use the z-buffer to randomize stroke order; it suffices to store up a sequence of strokes in memory and execute them in random order. Indeed, you don't need to use OpenGL at all.

C++/Python thoughts

In 2004 my implementation was written in a style similar to the assignments in CS488 at the time: a Python interface (for flexibility) with a C++ back end (for speed). The Python Imaging Library makes loading and saving images a snap. PIL_usm is a Python module that provides Gaussian blurring to the PIL. In my C++ code, I rendered strokes to an image (stored as a character array) using the excellent libart library. If you wanted to turn this sort of implementation into an interactive OpenGL interface, you might also find the PyOpenGL library useful.

Certain important operations in PIL, PIL_usm, and libart are non-obvious and require hard-won knowledge to get working. I've tried to summarize the technical aspects that could help you in two files: painthelp.cpp and paint.py. You can use these files as a skeleton for your own implementation, or steal ideas from them as necessary.

C++/OpenGL thoughts

Aaron Hertzmann has also been kind enough to donate some code to the assignment. Mainly, he's providing his Stroke class, which can render coloured strokes using OpenGL triangle strips. Feel free to borrow from the following files: color.h, Point.h Stroke.h, Stroke.cpp. You might also try using the Cairo library, which these days is very stable, widely available, and fully featured.

Java thoughts

For the 2008 offering of the course, I experimented with rewriting my earlier implementation in Java. This turned out to be sufficiently easy that I don't think it's necessary to provide skeleton code, just pointers to some useful classes:

It would be interesting to store up all the strokes ever generated by the system, and at the very end to emit them in a vector format such as SVG. Then you could scale the resulting painting to any size.

Craig S. Kaplan Last updated: