Taking an image and making it look like an oil painting is not only visually impressive, but also easy, from an algorithmic point of view. This page will show you how to write code to achieve the oil painting effect.

The parameters

There are two important parameters. The radius and the number of levels of intensity. Like 2d convolution, for each pixel, a number of pixels around that pixel are taken into account. The radius simply defines how many pixels in each direction to look for. A radius of 5, for example, should be good for rough oil painting pictures.

Levels of intensity

For this algorithm, each pixel will be put into an intensity ‘bin’. The true intensity of a pixel is defined as (r+b+g)/3, and can range anywhere from 0 to 256. However, oil paintings have a much more blocky effect, so each pixel will have its intensity binned. For a fairly blocky oil painting, 20 is a good reference number.

The algorithm

Step 1

For each pixel, all pixels within the radius will have to be examined. Pixels within the radius of the current pixel will be referred to as sub-pixels. For each sub-pixel, calculate the intensity, and determine which intensity bin that intensity number falls into. Maintain a counter for each intensity bin, which will count the number of sub-pixels which fall into each intensity bin. Also maintain the total red, green, and blue values for each bin, later; these may be used to determine the final value of the pixel.

for (each pixel)
{
	for (each pixel, within radius r of pixel)
	{
		int curIntensity = (int)((double)((r+g+b)/3)*intensityLevels)/255.0f;
		intensityCount[curIntensity]++;
		averageR[curIntensity] += r;
		averageG[curIntensity] += g;
		averageB[curIntensity] += b;
	}
}

Step 2

For each pixel, determine which intensity bin has the most number of pixels in it. Yes, this can be rolled into step 1, but for the purposes of this tutorial, this way is simpler to understand the code.

// Step 2, find the maximum level of intensity
// Yes, this can be rolled into step one and be slightly faster
// But for the purposes of this tutorial, it's simpler this way.
for (each level of intensity)
{
	if (intensityCount[i] > curMax)
	{
		curMax = intensityCount[i];
		maxIndex = i;
	}
}

Step 3

After we determine which intensity the pixel represents, as determined by the intensity bin with the most pixels, we can then determine the final color of the pixel by taking the total red, green, and blue values in that specific bin, and dividing that by the total number of pixels in that specific intensity bin.

// Step 3, calculate the final value
finalR = averageR[maxIndex] / curMax;
finalG = averageG[maxIndex] / curMax;
finalB = averageB[maxIndex] / curMax;

Results

Forest before

Forest Before (click to zoom in)

Forest After

Forest After. Radius 5, levels 20, (click to zoom in)