Sunday, July 8, 2018

Non Photorealistic Rendering Software - The Painter

"The Painter" is software to painterly render a photograph. Details of the methodology can be found in Non Photorealistic Rendering - Stroke-Based Rendering (SBR).

After you have extracted thepainter-x64.rar (windows 64 bit), you should see a directory called "waterlilykiwi". It contains everything you need to check that the software is running correctly on your machine. The file called "thepainter.bat" is the one you double-click to run the software. It tells windows where the executable is. You need to edit the file so that the location of the executable is the correct one for your installation. I use notepad++ to edit text files but I am sure you can use notepad or wordpad with equal success. If you want to use "The Painter" on your own photograph, create a directory alongside "waterlilykiwi" (in another words, at the same level as "waterlilykiwi"), copy "thepainter.bat" and "thepainter_input.txt" from "waterlilykiwi" to that new directory and double-click "thepainter.bat".

The file that controls the input to the software is "thepainter_input.txt". It should look like this:

128 1.0 1.0 50.0 0.5 0
64 1.0 1.0 50.0 0.5 0
32 1.0 1.0 50.0 0.5 0
16 1.0 1.0 50.0 0.5 0
8 1.0 1.0 50.0 0.5 0
8 1.0 1.0 50.0 0.5 1

Line 1 contains the name of the reference photograph you want to render (here, content_image.png). There should be no spaces in the image name.

Line 2 contains the name of the initial canvas rgb image. I would not worry too much about that one and just use ../texture/canvas01/rgb.png.

Line 3 contains the name of the initial canvas bumpity image. I would not worry too much about that one and just use ../texture/canvas01/bumpity.png.

If your final rendered image shows relatively large portions of unpainted canvas, then you may want to change to another canvas.

Line 4 contains the number of brushes that are gonna be used to paint the canvas (here, 6).

For each brush (going from large to small), there should be a line in the following format:

brush_radius opacity_strength bumpity_strength error_threshold grid_size_factor brush_ind

The brush radius "brush_radius" is the radius of the brush to be used to paint the strokes on the canvas. You don't want to use a brush radius that is too small since the painting is gonna look exactly like the photograph, which is not what you want. Here, with a reference image that's 2048 pixels high (the largest dimension), I am using the brush radius sequence: 128, 64, 32, 16, 8 (and another 8 with a shorter brush) but, if the reference image were larger (say, twice as large), I would probably use the sequence 256, 128, 64, 32, 16 (and another 16 with a shorter brush). It all depends on the size of the photograph you are starting with. And the style you want (coarse or fine).

The opacity strength "opacity_strength" is a number ranging from 0.0 to 1.0. The larger the opacity strength, the more opaque the brush strokes are going to be (in the canvas rgb image). More transparent brush strokes lead to a smoother, more blended look.

The bumpity strength "bumpity_strength" is a number ranging from 0.0 to 1.0. The larger the bumpity strength, the more height the brush strokes are going to have (in the canvas bumpity image). In other words, the larger the bumpity strength, the more visible the brush strokes are going to be when the canvas image is bumpmapped by the bumpity map (using the bump map filter in Gimp).

The error threshold "error_threshold" controls how the brush stroke colors should match the reference photo. The larger the error threshold, the coarser the painting will look.

The grid size factor "grid_size_factor" controls the size of the grid. If set 1o 1., the number of grid cells that are gonna be used in the width (height) direction is about the width (height) divided by the brush radius times the grid size factor. Since, for each grid cell, one brush stroke can potentially be applied somewhere in that grid cell, the lower the grid size factor, the more grid cells and the more brush strokes.

The brush index (brush_ind) controls which brush texture is going to be used for that particular brush size. It is an index into the brush textures list going from 0 to (number of brush textures - 1).

Line 11 contains the number of brush textures to be used (here, 2):

For each brush texture, there should be 2 lines telling the program where the opacity image file and the bumpity image file are located.

Line 16 contains the name of the output canvas rgb image (here, canvas_rgb_image.jpg). There should be no spaces in the image name.

Line 17 contains the name of the output canvas bumpity image (here, canvas_bumpity_image.jpg). There should be no spaces in the image name.

The brush textures are stored in the "texture" directory. What does a typical brush texture look like? Let's have a look at the brush texture images for brush01.

Brush texture opacity image for brush01.

As you build up layers of paint on the canvas with brush strokes, if the opacity is high (grayscale value is close to white), the paint that's underneath the current brush stroke will not be seen by transparency. If the opacity is low (grayscale value is close to black), the paint that's underneath the current brush stroke will be seen by transparency. The lower the opacity, the more the paint that's underneath will show. If the opacity is pure black, the current brush stroke will not deposit any paint on the canvas.

The opacity value coming from the brush texture opacity image is multiplied by the opacity strength to give the actual opacity value that's gonna be used. So, for example, if the opacity strength is set at 0.5, the opacity that is actually used is half the opacity that's in the brush texture opacity image. This enables to change the opacity quite easily without having to change the brush texture opacity image.

I am calling the brush height map the brush bumpity map because it rhymes with brush opacity map. It's really a brush bump map which is applied to the canvas bump map (aka canvas height map or canvas bumpity map). The brush bumpity map describes the height of the paint that's deposited by the brush. If the grayscale value is black, there is no bump and the paint is laid flat. The whiter the value, the more height the paint will have when it is deposited by the brush stroke. Here, we are simulating impasto oil painting which is characterized by thick layers of paint.

Just like with opacity, the actual bumpity used is the bumpity in the brush texture bumpity image times the bumpity strength.

The brush texture bumpity image I use is the same as the brush texture opacity image. I think it works fine to do that but you could certainly be fancy and have a brush texture bumpity image different than the brush texture opacity image.

Now that we have a better idea of what's in "thepainter_input.txt", let's see what "The Painter" does on that reference photo (the one from the "waterlilykiwi" directory).

This is the reference photograph aka the content image.

This is the canvas rgb image produced by "The Painter".

This is the canvas bumpity image produced by "The Painter".

To apply the canvas bumpity map to the canvas rgb image, I use gimp:

- Open as layers the canvas rgb image and the canvas bumpity image
- Activate the layer containing the canvas rgb image and click on Filters->Map->Bump Map
- For the Bump Map, choose the canvas bumpity image
- For the Map type, switch from Linear to Spherical
- You may want to adjust the Depth Parameter to adjust the height. Here, I used 6.
- Click OK and then export the bumpmapped image

This is the canvas rgb image bumpmapped.

You don't have to use the canvas bumpity image (the bump map) to create great "fake" oil paintings with thepainter. Here is another example of what thepainter can do, this time completely ignoring the produced bump map.
This is the picture I want to make an oil painting of. Taken from pixabay.

After a few rounds of blurring (Gaussian or Median blurring), this is what the input to thepainter looks like.

This is the input file for thepainter:

64 1.0 1.0 30.0 0.78 0
32 1.0 1.0 30.0 0.65 0
16 1.0 1.0 30.0 0.57 0
8 1.0 1.0 30.0 0.84 0
4 0.5 1.0 30.0 0.67 0

This is the output from thepainter (canvas_rgb_image.png).

Here is a video tutorial for thepainter:

Here is another video tutorial for thepainter:

Heres is another video tutorial for thepainter:

A few tips:
- It is a really good idea to apply several passes of blurring to the reference image aka content_image.png before running thepainter in order to get rid of unnecessary details. Click on Filters->Blur and then select Gaussian Blur, Median Blur, or Selective Gaussian Blur. In my opinion, this is a must. I use the default settings and apply the blurring several times. That seems to do the trick. Personally, I like Median Blur best.
- It is probably a good idea to limit the grayscale values of the bumpity map aka canvas_bumpity_image.png to be between 128 and 255. To do that, click on Colors->Levels and then put the value 128 in the left "Output Levels" value box. That way, when you apply the bump map, pixels in the canvas image aka canvas_rgb_image.png will never sink below the neutral plane at middle gray. Note that you will probably have to increase the height when applying the bump map to the canvas image (use 12 instead of 6, for example).
- It is probably a good idea , once you have created the bumpmapped image, to sharpen it a bit to enhance the visible brush stroke feel. Click on Filters->Enhance->Sharpen (Unsharp Mask). I usually apply the sharpening filter up to 3 times.
- If there are areas in your "painting" that don't look quite right, you can certainly run thepainter using various input files and combine the results in Gimp using layers and the Eraser tool.
- If you want to use your own brushes, google something like "oil brush png" and you will find tons of brushes to choose from.

"The Painter" is free to download at Painting Software.

Source code: thepainter on github.


  1. Hello! I'm a HUGE fan of this program, I've been playing around with it for a week now and it's still just as fun as ever. I was wondering if there's going to be any future developments on this software. I'm particularly interested in the ability to render these effects on an alpha channel or maybe have a separate file that renders to serve as a luma matte. Any and all help with this is very much appreciated :)

    1. Note that I have updated it yesterday (May 3 2020). Has nothing to do with your request though. Right now, it's working with images with 3 rgb channels. i am pretty sure you can use images that are grayscale (single channel) as the program will transform them internally as 3 channel rgb images with same grayscale values on all channels. So you could probably extract the alpha channel from an image and make a brand new grayscale image out of it and feed that to the program.

  2. Hi I right now am trying to study the painterly algorithm. And do you have any tips or sources on where I can study on how to recreate the painterly algorithm using a Brush Texture.

    1. My approach is based on 2 academic papers and it's explained in this post: I do use a brush texture and the use can use whatever texture he wants for the brushes. For each brush, there is one texture for the opacity and one for the bumpmapping (depth map).

    2. Hi I've tried experimenting on the Painter. And I've tried this specific image, the results are also on the link,, In the results there is a blank area where strokes where not added.
      I've tried running it with your parameters and the parameters of what Aaron Hertzmann says that it will reproduce an impressionistic painting. In your opinion what do you think are the perfect parameters or maybe changes in the program to make it more impressionistic.

    3. If large areas are not painted, it means you need to change the color of the initial canvas. Use the gray one instead of the white one, or make your own. Otherwise, it's a matter of experimenting. Send me an email (see taskbar on the right) and I will send you the best i can do with your photo. Cheers.

    4. Also, make sure you apply a smoothing filter to your initial photo to get rid of lot of unnecessary details. I suggest using the Median Filter in your case 2 to 3 times.

    5. Also, as you reduce the brush radius, you may want to decrease the opacity. Instead of 1.0, use 0.5, 0.4, 0.3, etc. You really don't want to use small brushes at high opacity otherwise they are gonna stick like a sore thumb in your painting.

    6. In your application, is there a minimum and maximum brush stroke length when you put the brush strokes in the canvas just like in the Painterly Algorithm ? I think it will be a good addition to the txt file "thepainter_input.txt"