Non-Photorealistic Rendering

Implementing a clever algorithm developed by Aaron Hertzman by differing the brush sizes and stroke length and other factors this program can create images to imitate different artistic styles, such as impressionism and pointillism.

Painterly Rendering with Brush Strokes of Multiple Sizes

Program usage:

Code can be downloaded here:
Compile the project file in visual studio 2005
this project makes heavy use of the Cimg graphics library for image display and processing
make sure you have cimg.h included in your project.
the resulting .exe file can be found in the example folder
run the program from the command line:
command line options:
-blur # the kernal to use for the Gaussian blur
-i input.ppm just what it sounds like
if neither -blur or -i is chosen the program presumes a blur of 1 and for input original_parrot.ppm
all other options can be found in tester.cpp at the top these are the defaults:
#define numberBrushes 3
#define ErrorTolerance 0.01
#define GridSize 1
#define maxStrokeLength 4
#define minStrokeLength 1
#define curvyFactor 10
keep in mind that the brush radii are set manually according to hertzman’s paper so I have followed suit
to change the radii values go to line 439 of tester.cpp and change accordingly, noting that the brushRadii
array should have as many entries as you do brushes or else you’ll get errors

Program Modules:

The program processes images in this manner:
Read in Image —–> Painter Layer —–> Create gaussian and difference Image and blank canvas
———-> Call makeStroke function to do calculations as to what sort of stroke needs to be made
———-> paint the stroke onto the current canvas
———-> Repeat paint layer procedure for all brushes
———-> Display Resulting Image, as well as examples of a gaussian and difference image used in the program and the original image

The program uses several classes to keep track of and calculate the stroke points and color:

the Color class just holds int values for r,g,b and the function colorDifference
The Pair class is used to help keep track of coordinates in the image and in placing strokes consists of:

class Pair
float x, y;
void add(Pair);
void subtract(Pair);
void scale(int);
void divide(int);
void equals(Pair p1);
void normalize();
Pair NormalPlus(Pair, Pair, float width);
Pair NormalMinus(Pair, Pair, float width);

class Stroke
int radius;
Color StrokeColor;

The Stroke class merely keeps track of the stroke color and the radius and is used in conjunction with the pair class to paint strokes.


The algorithm I employed was the same as used in Aaron Hertzman’s paper Painterly Rendering with Curved Brush Strokes of Multiple Sizes. Also, Gooch and Gooch’s Non-Photorealistic Rendering was used as reference as per its pseduo-code.

To quote the book, “Hertzman computes a list of brushstrokes for each layer of the painting…Brushstrokes are limited to a constant color, and image gradients are used to guide stroke placement. Each layer if made up of brushstrokes of only a single size. Layers are painted on top of one another in a simple four-step algorithm:

1. Create a reference image for the layer by blurring the source image using a Gaussian blur with a kernal size based on the brush radius for that layer.
2. Compute a per-pixel difference image between the blurred reference image and the current painted image
3. Grid off the painted image, and for each cell compute the average error in that cell from the difference image.
4. If the error in the cell is higher than a user set tolerance, compute a stroke starting at the current cell.

Strokes are computed by first computing the image gradient at the current control point using the sobel-filtered luminance of the reference image. The next point is placed normal to the gradient at a distance equal to the radius of the current stroke.”

Troubleshooting / Conclusions:

Brushstrokes were rendering as soon as strokes were calculated, unlike Hertzman’s paper where the strokes are stored and then painted randomly. I ran out of time in trying to implement a stroke storage and randomization method. Also there were problems with the make stroke function. I checked thoroughly to ensure that the gradient was being calculated correctly, but still when trying to run the program after a certain point the memory would overflow and the program would crash. As a result the curved brushstroke portion of the paper was not able to be implemented limiting the choices for rendering. Instead I used a makeStroke function that Hertzman described in section 2.1 of his paper for use instead of for curved brush strokes. Given more time I’d like to fully implement all aspects of the paper and try to figure out where the memory error is.

Over all though, the project turned out fairly well. By varying the number of brushes , the blur size, and the brush radii you can get different effects on the image from a very broken up dotted painting to a highly detailed recreation.



All Pictures used a -blur of 10. The bird was painted using brush sizes of in order of 10| 10,8,6 | 10,8,6,4,2,1
All other photos were rendered using brush sizes of |10,8,6|, also a rendering of image 3 was done with the 5 brush set as well
Also included are some examples of the Gaussian image, the difference image, and images of the image gradients for x and y

. . . . .

. . .

. .

. Street .

. .

. .

. .

. .
. .
. .

Leave a Reply

Please log in using one of these methods to post your comment: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: